aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--.cirrus.yml8
-rw-r--r--.editorconfig26
-rw-r--r--.fuzzbuzz.yml2
-rw-r--r--.gitignore3
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Makefile.am2
-rw-r--r--README.md13
-rw-r--r--build-aux/m4/ax_boost_process.m4121
-rw-r--r--build-aux/m4/ax_boost_thread.m4187
-rw-r--r--build-aux/m4/ax_gcc_func_attribute.m4223
-rw-r--r--build-aux/m4/bitcoin_qt.m4138
-rw-r--r--build_msvc/bitcoin_config.h15
-rw-r--r--build_msvc/common.init.vcxproj2
-rw-r--r--build_msvc/vcpkg.json1
-rwxr-xr-xci/lint/04_install.sh1
-rwxr-xr-xci/test/00_setup_env.sh2
-rw-r--r--ci/test/00_setup_env_i686_centos.sh2
-rw-r--r--ci/test/00_setup_env_mac.sh4
-rw-r--r--ci/test/00_setup_env_native_asan.sh2
-rw-r--r--ci/test/00_setup_env_native_fuzz.sh2
-rw-r--r--ci/test/00_setup_env_native_fuzz_with_valgrind.sh2
-rw-r--r--ci/test/00_setup_env_native_multiprocess.sh1
-rw-r--r--ci/test/00_setup_env_native_qt5.sh4
-rw-r--r--ci/test/00_setup_env_native_valgrind.sh2
-rw-r--r--ci/test/00_setup_env_win64.sh6
-rwxr-xr-xci/test/05_before_script.sh2
-rwxr-xr-xci/test/wrap-wine.sh2
-rw-r--r--configure.ac238
-rw-r--r--contrib/debian/copyright15
-rw-r--r--contrib/devtools/README.md3
-rwxr-xr-xcontrib/devtools/symbol-check.py10
-rwxr-xr-xcontrib/devtools/test-security-check.py11
-rwxr-xr-xcontrib/devtools/test-symbol-check.py26
-rwxr-xr-xcontrib/gitian-build.py8
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml13
-rw-r--r--contrib/gitian-descriptors/gitian-osx-signer.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml8
-rw-r--r--contrib/gitian-descriptors/gitian-win-signer.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml4
-rw-r--r--contrib/guix/README.md36
-rwxr-xr-xcontrib/guix/guix-build.sh41
-rw-r--r--contrib/guix/libexec/build.sh59
-rw-r--r--contrib/guix/manifest.scm21
-rw-r--r--contrib/init/bitcoind.openrc7
-rw-r--r--contrib/testgen/README.md4
-rwxr-xr-xcontrib/testgen/gen_key_io_test_vectors.py84
-rwxr-xr-xcontrib/verifybinaries/verify.py4
-rw-r--r--depends/Makefile35
-rw-r--r--depends/README.md4
-rw-r--r--depends/config.site.in4
-rw-r--r--depends/packages/boost.mk2
-rw-r--r--depends/packages/libnatpmp.mk11
-rw-r--r--depends/packages/libxcb.mk15
-rw-r--r--depends/packages/libxkbcommon.mk32
-rw-r--r--depends/packages/packages.mk4
-rw-r--r--depends/packages/qt.mk50
-rw-r--r--depends/packages/zlib.mk31
-rw-r--r--depends/patches/qt/drop_lrelease_dependency.patch2
-rw-r--r--depends/patches/qt/fix_android_jni_static.patch2
-rw-r--r--depends/patches/qt/fix_android_qmake_conf.patch24
-rw-r--r--depends/patches/qt/fix_configure_mac.patch50
-rw-r--r--depends/patches/qt/fix_lib_paths.patch193
-rw-r--r--depends/patches/qt/fix_mingw_cross_compile.patch25
-rw-r--r--depends/patches/qt/fix_no_printer.patch4
-rw-r--r--depends/patches/qt/fix_powerpc_libpng.patch23
-rw-r--r--depends/patches/qt/fix_qpainter_non_determinism.patch4
-rw-r--r--depends/patches/qt/fix_qt_pkgconfig.patch12
-rw-r--r--depends/patches/qt/fix_rcc_determinism.patch15
-rw-r--r--depends/patches/qt/fix_riscv64_arch.patch14
-rw-r--r--depends/patches/qt/freetype_back_compat.patch28
-rw-r--r--depends/patches/qt/no-xlib.patch17
-rw-r--r--depends/patches/qt/no_sdk_version_check.patch20
-rw-r--r--depends/patches/qt/xkb-default.patch26
-rw-r--r--doc/Doxyfile.in2
-rw-r--r--doc/REST-interface.md7
-rw-r--r--doc/bips.md7
-rw-r--r--doc/bitcoin-conf.md2
-rw-r--r--doc/build-osx.md330
-rw-r--r--doc/build-unix.md85
-rw-r--r--doc/build-windows.md2
-rw-r--r--doc/dependencies.md7
-rw-r--r--doc/descriptors.md2
-rw-r--r--doc/developer-notes.md15
-rw-r--r--doc/external-signer.md171
-rw-r--r--doc/files.md1
-rw-r--r--doc/fuzzing.md93
-rw-r--r--doc/init.md19
-rw-r--r--doc/release-notes-18466.md10
-rw-r--r--doc/release-notes-20861.md13
-rw-r--r--doc/release-notes.md4
-rw-r--r--doc/tor.md116
-rw-r--r--share/examples/bitcoin.conf14
-rw-r--r--share/setup.nsi.in1
-rw-r--r--src/.clang-format3
-rw-r--r--src/Makefile.am32
-rw-r--r--src/Makefile.qt.include7
-rw-r--r--src/Makefile.test.include35
-rw-r--r--src/Makefile.test_fuzz.include2
-rw-r--r--src/Makefile.test_util.include3
-rw-r--r--src/addrdb.cpp12
-rw-r--r--src/addrman.h94
-rw-r--r--src/banman.cpp2
-rw-r--r--src/base58.cpp2
-rw-r--r--src/bech32.cpp52
-rw-r--r--src/bech32.h39
-rw-r--r--src/bench/bech32.cpp2
-rw-r--r--src/bench/block_assemble.cpp17
-rw-r--r--src/bench/coin_selection.cpp9
-rw-r--r--src/bench/data.cpp2
-rw-r--r--src/bench/duplicate_inputs.cpp8
-rw-r--r--src/bench/mempool_eviction.cpp8
-rw-r--r--src/bench/mempool_stress.cpp2
-rw-r--r--src/bench/rpc_blockchain.cpp50
-rw-r--r--src/bench/wallet_balance.cpp21
-rw-r--r--src/bitcoin-cli.cpp205
-rw-r--r--src/bitcoin-wallet.cpp69
-rw-r--r--src/bitcoind.cpp124
-rw-r--r--src/chain.h13
-rw-r--r--src/chainparams.cpp33
-rw-r--r--src/chainparams.h30
-rw-r--r--src/chainparamsbase.cpp9
-rw-r--r--src/coins.cpp8
-rw-r--r--src/coins.h12
-rw-r--r--src/compat.h19
-rw-r--r--src/compat/assumptions.h14
-rw-r--r--src/compat/glibc_compat.cpp7
-rw-r--r--src/compat/glibc_sanity.cpp45
-rw-r--r--src/compat/sanity.h1
-rw-r--r--src/core_io.h2
-rw-r--r--src/core_write.cpp17
-rw-r--r--src/crypto/muhash.cpp42
-rw-r--r--src/crypto/muhash.h7
-rw-r--r--src/dummywallet.cpp1
-rw-r--r--src/flatfile.cpp2
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/i2p.cpp407
-rw-r--r--src/i2p.h268
-rw-r--r--src/index/base.cpp43
-rw-r--r--src/index/blockfilterindex.cpp4
-rw-r--r--src/index/txindex.cpp2
-rw-r--r--src/init.cpp62
-rw-r--r--src/init.h5
-rw-r--r--src/interfaces/chain.h11
-rw-r--r--src/interfaces/handler.cpp5
-rw-r--r--src/interfaces/node.h5
-rw-r--r--src/key_io.cpp30
-rw-r--r--src/logging.cpp14
-rw-r--r--src/logging.h11
-rw-r--r--src/miner.cpp15
-rw-r--r--src/miner.h10
-rw-r--r--src/net.cpp371
-rw-r--r--src/net.h119
-rw-r--r--src/net_processing.cpp1329
-rw-r--r--src/net_processing.h12
-rw-r--r--src/netaddress.cpp64
-rw-r--r--src/netaddress.h32
-rw-r--r--src/netbase.cpp437
-rw-r--r--src/netbase.h211
-rw-r--r--src/node/coin.cpp3
-rw-r--r--src/node/coinstats.cpp86
-rw-r--r--src/node/coinstats.h4
-rw-r--r--src/node/interfaces.cpp84
-rw-r--r--src/node/psbt.h20
-rw-r--r--src/node/transaction.cpp29
-rw-r--r--src/optional.h20
-rw-r--r--src/protocol.cpp3
-rw-r--r--src/psbt.h5
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/addresstablemodel.cpp44
-rw-r--r--src/qt/bantablemodel.cpp15
-rw-r--r--src/qt/bitcoin.cpp13
-rw-r--r--src/qt/bitcoin.qrc3
-rw-r--r--src/qt/bitcoingui.cpp3
-rw-r--r--src/qt/clientmodel.cpp8
-rw-r--r--src/qt/forms/debugwindow.ui162
-rw-r--r--src/qt/forms/optionsdialog.ui100
-rw-r--r--src/qt/forms/overviewpage.ui66
-rw-r--r--src/qt/guiutil.cpp163
-rw-r--r--src/qt/guiutil.h49
-rw-r--r--src/qt/networkstyle.cpp13
-rw-r--r--src/qt/optionsdialog.cpp15
-rw-r--r--src/qt/optionsdialog.h2
-rw-r--r--src/qt/optionsmodel.cpp13
-rw-r--r--src/qt/optionsmodel.h4
-rw-r--r--src/qt/overviewpage.cpp17
-rw-r--r--src/qt/overviewpage.h1
-rw-r--r--src/qt/peertablemodel.cpp48
-rw-r--r--src/qt/peertablemodel.h15
-rw-r--r--src/qt/platformstyle.cpp15
-rw-r--r--src/qt/psbtoperationsdialog.cpp4
-rw-r--r--src/qt/qrimagewidget.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp72
-rw-r--r--src/qt/receivecoinsdialog.h6
-rw-r--r--src/qt/res/fonts/RobotoMono-Bold.ttfbin0 -> 87008 bytes
-rw-r--r--src/qt/rpcconsole.cpp62
-rw-r--r--src/qt/rpcconsole.h10
-rw-r--r--src/qt/sendcoinsdialog.cpp6
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/test_main.cpp7
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/transactiontablemodel.cpp30
-rw-r--r--src/qt/transactionview.cpp77
-rw-r--r--src/qt/transactionview.h5
-rw-r--r--src/qt/walletview.cpp2
-rw-r--r--src/random.cpp3
-rw-r--r--src/rest.cpp20
-rw-r--r--src/rpc/blockchain.cpp251
-rw-r--r--src/rpc/blockchain.h8
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp172
-rw-r--r--src/rpc/misc.cpp23
-rw-r--r--src/rpc/net.cpp35
-rw-r--r--src/rpc/rawtransaction.cpp57
-rw-r--r--src/rpc/request.h6
-rw-r--r--src/rpc/server.cpp33
-rw-r--r--src/rpc/server.h2
-rw-r--r--src/rpc/util.cpp36
-rw-r--r--src/rpc/util.h22
-rw-r--r--src/script/bitcoinconsensus.h12
-rw-r--r--src/script/descriptor.cpp158
-rw-r--r--src/script/descriptor.h7
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/script/sigcache.cpp12
-rw-r--r--src/script/sign.cpp9
-rw-r--r--src/script/standard.cpp31
-rw-r--r--src/shutdown.cpp56
-rw-r--r--src/signet.cpp12
-rw-r--r--src/signet.h4
-rw-r--r--src/test/addrman_tests.cpp2
-rw-r--r--src/test/allocator_tests.cpp3
-rw-r--r--src/test/base32_tests.cpp2
-rw-r--r--src/test/base64_tests.cpp2
-rw-r--r--src/test/bech32_tests.cpp58
-rw-r--r--src/test/blockfilter_index_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp15
-rw-r--r--src/test/cuckoocache_tests.cpp19
-rw-r--r--src/test/data/key_io_invalid.json146
-rw-r--r--src/test/data/key_io_valid.json571
-rw-r--r--src/test/data/tx_invalid.json155
-rw-r--r--src/test/data/tx_valid.json304
-rw-r--r--src/test/dbwrapper_tests.cpp5
-rw-r--r--src/test/denialofservice_tests.cpp92
-rw-r--r--src/test/descriptor_tests.cpp83
-rw-r--r--src/test/fs_tests.cpp18
-rw-r--r--src/test/fuzz/FuzzedDataProvider.h1
-rw-r--r--src/test/fuzz/addrman.cpp2
-rw-r--r--src/test/fuzz/asmap_direct.cpp3
-rw-r--r--src/test/fuzz/autofile.cpp3
-rw-r--r--src/test/fuzz/banman.cpp5
-rw-r--r--src/test/fuzz/bech32.cpp32
-rw-r--r--src/test/fuzz/bloom_filter.cpp2
-rw-r--r--src/test/fuzz/buffered_file.cpp3
-rw-r--r--src/test/fuzz/coins_view.cpp7
-rw-r--r--src/test/fuzz/connman.cpp11
-rw-r--r--src/test/fuzz/crypto.cpp15
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp2
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp2
-rw-r--r--src/test/fuzz/data_stream.cpp5
-rw-r--r--src/test/fuzz/descriptor_parse.cpp1
-rw-r--r--src/test/fuzz/deserialize.cpp8
-rw-r--r--src/test/fuzz/eval_script.cpp1
-rw-r--r--src/test/fuzz/fuzz.cpp20
-rw-r--r--src/test/fuzz/fuzz.h39
-rw-r--r--src/test/fuzz/integer.cpp6
-rw-r--r--src/test/fuzz/key.cpp1
-rw-r--r--src/test/fuzz/kitchen_sink.cpp2
-rw-r--r--src/test/fuzz/load_external_block_file.cpp4
-rw-r--r--src/test/fuzz/merkleblock.cpp2
-rw-r--r--src/test/fuzz/muhash.cpp12
-rw-r--r--src/test/fuzz/net.cpp6
-rw-r--r--src/test/fuzz/netaddress.cpp3
-rw-r--r--src/test/fuzz/netbase_dns_lookup.cpp71
-rw-r--r--src/test/fuzz/node_eviction.cpp25
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp3
-rw-r--r--src/test/fuzz/parse_numbers.cpp3
-rw-r--r--src/test/fuzz/parse_univalue.cpp1
-rw-r--r--src/test/fuzz/policy_estimator.cpp5
-rw-r--r--src/test/fuzz/policy_estimator_io.cpp5
-rw-r--r--src/test/fuzz/pow.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp88
-rw-r--r--src/test/fuzz/process_messages.cpp8
-rw-r--r--src/test/fuzz/psbt.cpp5
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp2
-rw-r--r--src/test/fuzz/script.cpp3
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp10
-rw-r--r--src/test/fuzz/script_descriptor_cache.cpp2
-rw-r--r--src/test/fuzz/script_flags.cpp1
-rw-r--r--src/test/fuzz/script_ops.cpp2
-rw-r--r--src/test/fuzz/scriptnum_ops.cpp2
-rw-r--r--src/test/fuzz/signature_checker.cpp3
-rw-r--r--src/test/fuzz/signet.cpp5
-rw-r--r--src/test/fuzz/socks5.cpp45
-rw-r--r--src/test/fuzz/string.cpp8
-rw-r--r--src/test/fuzz/strprintf.cpp2
-rw-r--r--src/test/fuzz/system.cpp8
-rw-r--r--src/test/fuzz/torcontrol.cpp80
-rw-r--r--src/test/fuzz/transaction.cpp15
-rw-r--r--src/test/fuzz/util.h174
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp34
-rw-r--r--src/test/fuzz/versionbits.cpp345
-rw-r--r--src/test/hash_tests.cpp4
-rw-r--r--src/test/miner_tests.cpp62
-rw-r--r--src/test/net_tests.cpp89
-rw-r--r--src/test/netbase_tests.cpp19
-rw-r--r--src/test/rpc_tests.cpp11
-rw-r--r--src/test/sanity_tests.cpp3
-rw-r--r--src/test/script_standard_tests.cpp16
-rw-r--r--src/test/scriptnum_tests.cpp8
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/sock_tests.cpp180
-rw-r--r--src/test/system_tests.cpp10
-rw-r--r--src/test/transaction_tests.cpp178
-rw-r--r--src/test/txvalidation_tests.cpp16
-rw-r--r--src/test/txvalidationcache_tests.cpp11
-rw-r--r--src/test/util/logging.cpp1
-rw-r--r--src/test/util/mining.cpp2
-rw-r--r--src/test/util/script.h21
-rw-r--r--src/test/util/setup_common.cpp101
-rw-r--r--src/test/util/setup_common.h47
-rw-r--r--src/test/util_tests.cpp95
-rw-r--r--src/test/validation_block_tests.cpp31
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp187
-rw-r--r--src/test/validation_tests.cpp24
-rw-r--r--src/test/versionbits_tests.cpp86
-rw-r--r--src/torcontrol.cpp168
-rw-r--r--src/torcontrol.h136
-rw-r--r--src/txdb.cpp17
-rw-r--r--src/txmempool.cpp69
-rw-r--r--src/txmempool.h77
-rw-r--r--src/txorphanage.cpp202
-rw-r--r--src/txorphanage.h85
-rw-r--r--src/txrequest.cpp3
-rw-r--r--src/util/epochguard.h91
-rw-r--r--src/util/error.cpp4
-rw-r--r--src/util/error.h2
-rw-r--r--src/util/getuniquepath.cpp10
-rw-r--r--src/util/getuniquepath.h19
-rw-r--r--src/util/memory.h20
-rw-r--r--src/util/moneystr.cpp12
-rw-r--r--src/util/moneystr.h2
-rw-r--r--src/util/readwritefile.cpp47
-rw-r--r--src/util/readwritefile.h28
-rw-r--r--src/util/sock.cpp333
-rw-r--r--src/util/sock.h169
-rw-r--r--src/util/strencodings.cpp32
-rw-r--r--src/util/strencodings.h11
-rw-r--r--src/util/string.h8
-rw-r--r--src/util/system.cpp76
-rw-r--r--src/util/system.h33
-rw-r--r--src/util/time.cpp91
-rw-r--r--src/util/time.h40
-rw-r--r--src/util/tokenpipe.cpp109
-rw-r--r--src/util/tokenpipe.h127
-rw-r--r--src/validation.cpp707
-rw-r--r--src/validation.h159
-rw-r--r--src/versionbits.h3
-rw-r--r--src/wallet/bdb.cpp8
-rw-r--r--src/wallet/coincontrol.h11
-rw-r--r--src/wallet/coinselection.cpp5
-rw-r--r--src/wallet/db.h7
-rw-r--r--src/wallet/external_signer.cpp119
-rw-r--r--src/wallet/external_signer.h73
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp81
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h34
-rw-r--r--src/wallet/feebumper.cpp2
-rw-r--r--src/wallet/init.cpp5
-rw-r--r--src/wallet/interfaces.cpp14
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/rpcdump.cpp132
-rw-r--r--src/wallet/rpcsigner.cpp108
-rw-r--r--src/wallet/rpcsigner.h25
-rw-r--r--src/wallet/rpcwallet.cpp360
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/salvage.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.cpp43
-rw-r--r--src/wallet/scriptpubkeyman.h12
-rw-r--r--src/wallet/sqlite.cpp5
-rw-r--r--src/wallet/test/coinselector_tests.cpp24
-rw-r--r--src/wallet/test/db_tests.cpp8
-rw-r--r--src/wallet/test/init_tests.cpp8
-rw-r--r--src/wallet/test/wallet_tests.cpp8
-rw-r--r--src/wallet/wallet.cpp248
-rw-r--r--src/wallet/wallet.h44
-rw-r--r--src/wallet/walletdb.cpp29
-rw-r--r--src/wallet/wallettool.cpp10
-rw-r--r--src/wallet/wallettool.h2
-rw-r--r--src/wallet/walletutil.h3
-rw-r--r--src/zmq/zmqabstractnotifier.h3
-rw-r--r--test/config.ini.in1
-rw-r--r--test/functional/README.md11
-rw-r--r--test/functional/data/invalid_txs.py2
-rwxr-xr-xtest/functional/example_test.py3
-rwxr-xr-xtest/functional/feature_asmap.py1
-rwxr-xr-xtest/functional/feature_assumevalid.py30
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py134
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py65
-rwxr-xr-xtest/functional/feature_config_args.py83
-rwxr-xr-xtest/functional/feature_dbcrash.py1
-rwxr-xr-xtest/functional/feature_filelock.py22
-rwxr-xr-xtest/functional/feature_includeconf.py1
-rwxr-xr-xtest/functional/feature_notifications.py88
-rwxr-xr-xtest/functional/feature_proxy.py45
-rwxr-xr-xtest/functional/feature_taproot.py21
-rwxr-xr-xtest/functional/feature_utxo_set_hash.py86
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py2
-rwxr-xr-xtest/functional/interface_rpc.py26
-rwxr-xr-xtest/functional/interface_zmq.py112
-rwxr-xr-xtest/functional/mempool_persist.py6
-rwxr-xr-xtest/functional/mocks/signer.py102
-rwxr-xr-xtest/functional/p2p_add_connections.py1
-rwxr-xr-xtest/functional/p2p_addr_relay.py1
-rwxr-xr-xtest/functional/p2p_blocksonly.py1
-rwxr-xr-xtest/functional/p2p_filter.py16
-rwxr-xr-xtest/functional/p2p_getaddr_caching.py1
-rwxr-xr-xtest/functional/p2p_invalid_locator.py1
-rwxr-xr-xtest/functional/p2p_invalid_tx.py2
-rwxr-xr-xtest/functional/p2p_leak.py70
-rwxr-xr-xtest/functional/p2p_message_capture.py6
-rwxr-xr-xtest/functional/p2p_tx_download.py1
-rwxr-xr-xtest/functional/rpc_blockchain.py31
-rwxr-xr-xtest/functional/rpc_estimatefee.py1
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py22
-rwxr-xr-xtest/functional/rpc_getblockfilter.py6
-rwxr-xr-xtest/functional/rpc_help.py5
-rwxr-xr-xtest/functional/rpc_invalid_address_message.py20
-rwxr-xr-xtest/functional/rpc_net.py13
-rwxr-xr-xtest/functional/rpc_psbt.py1
-rwxr-xr-xtest/functional/rpc_signmessage.py22
-rwxr-xr-xtest/functional/rpc_uptime.py5
-rw-r--r--test/functional/test-shell.md2
-rw-r--r--test/functional/test_framework/bdb.py1
-rw-r--r--test/functional/test_framework/key.py6
-rwxr-xr-xtest/functional/test_framework/messages.py39
-rwxr-xr-xtest/functional/test_framework/p2p.py20
-rw-r--r--test/functional/test_framework/script.py12
-rw-r--r--test/functional/test_framework/segwit_addr.py54
-rwxr-xr-xtest/functional/test_framework/test_framework.py49
-rwxr-xr-xtest/functional/test_framework/test_node.py16
-rw-r--r--test/functional/test_framework/util.py1
-rw-r--r--test/functional/test_framework/wallet.py9
-rwxr-xr-xtest/functional/test_runner.py74
-rwxr-xr-xtest/functional/tool_wallet.py11
-rwxr-xr-xtest/functional/wallet_avoidreuse.py1
-rwxr-xr-xtest/functional/wallet_createwallet.py1
-rwxr-xr-xtest/functional/wallet_descriptor.py70
-rwxr-xr-xtest/functional/wallet_groups.py16
-rwxr-xr-xtest/functional/wallet_labels.py10
-rwxr-xr-xtest/functional/wallet_listdescriptors.py16
-rwxr-xr-xtest/functional/wallet_send.py101
-rwxr-xr-xtest/functional/wallet_signer.py217
-rwxr-xr-xtest/functional/wallet_txn_clone.py7
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py7
-rwxr-xr-xtest/functional/wallet_upgradewallet.py1
-rwxr-xr-xtest/functional/wallet_watchonly.py8
-rwxr-xr-xtest/fuzz/test_runner.py56
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh2
-rwxr-xr-xtest/lint/lint-includes.sh2
-rwxr-xr-xtest/lint/lint-python-dead-code.sh23
-rw-r--r--test/sanitizer_suppressions/tsan1
461 files changed, 13142 insertions, 6672 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 7250d4ad94..097874b17a 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -10,7 +10,7 @@ environment:
QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt598x64_vs2019_v1681/qt598_x64_vs2019_1681.zip'
QT_DOWNLOAD_HASH: '00cf7327818c07d74e0b1a4464ffe987c2728b00d49d4bf333065892af0515c3'
QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019'
- VCPKG_TAG: '2020.11-1'
+ VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
# - cmd: pip install zmq
diff --git a/.cirrus.yml b/.cirrus.yml
index 801164c368..691582239e 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -82,10 +82,10 @@ task:
FILE_ENV: "./ci/test/00_setup_env_arm.sh"
task:
- name: 'Win64 [unit tests, no gui tests, no boost::process, no functional tests] [bionic]'
+ name: 'Win64 [unit tests, no gui tests, no boost::process, no functional tests] [focal]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:bionic
+ image: ubuntu:focal
env:
FILE_ENV: "./ci/test/00_setup_env_win64.sh"
@@ -160,10 +160,10 @@ task:
FILE_ENV: "./ci/test/00_setup_env_native_nowallet.sh"
task:
- name: 'macOS 10.14 [gui, no tests] [bionic]'
+ name: 'macOS 10.14 [gui, no tests] [focal]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:bionic
+ image: ubuntu:focal
env:
FILE_ENV: "./ci/test/00_setup_env_mac.sh"
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..4967e675f6
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,26 @@
+# This is the top-most EditorConfig file.
+root = true
+
+# For all files.
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+# Source code files
+[*.{h,cpp,py,sh}]
+indent_size = 4
+
+# .cirrus.yml, .appveyor.yml, .fuzzbuzz.yml, etc.
+[*.yml]
+indent_size = 2
+
+# Makefiles
+[{*.am,Makefile.*.include}]
+indent_style = tab
+
+# Autoconf scripts
+[configure.ac]
+indent_size = 2
diff --git a/.fuzzbuzz.yml b/.fuzzbuzz.yml
index be9a1cd4e1..e40b4df165 100644
--- a/.fuzzbuzz.yml
+++ b/.fuzzbuzz.yml
@@ -5,7 +5,7 @@ environment:
- CXXFLAGS=-fcoverage-mapping -fno-omit-frame-pointer -fprofile-instr-generate -gline-tables-only -O1
setup:
- sudo apt-get update
- - sudo apt-get install -y autoconf bsdmainutils clang git libboost-all-dev libc++1 libc++abi1 libc++abi-dev libc++-dev libclang1 libclang-dev libdb5.3++ libevent-dev libllvm-ocaml-dev libomp5 libomp-dev libqt5core5a libqt5dbus5 libqt5gui5 libtool llvm llvm-dev llvm-runtime pkg-config qttools5-dev qttools5-dev-tools software-properties-common
+ - sudo apt-get install -y autoconf bsdmainutils clang git libboost-system-dev libboost-filesystem-dev libboost-test-dev libc++1 libc++abi1 libc++abi-dev libc++-dev libclang1 libclang-dev libdb5.3++ libevent-dev libllvm-ocaml-dev libomp5 libomp-dev libqt5core5a libqt5dbus5 libqt5gui5 libtool llvm llvm-dev llvm-runtime pkg-config qttools5-dev qttools5-dev-tools software-properties-common
- ./autogen.sh
- CC=clang CXX=clang++ ./configure --enable-fuzz --with-sanitizers=address,fuzzer,undefined --enable-danger-fuzz-link-all
- make
diff --git a/.gitignore b/.gitignore
index 010586ee5a..c0b5661464 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,8 +10,7 @@ src/bitcoin-node
src/bitcoin-tx
src/bitcoin-util
src/bitcoin-wallet
-src/test/fuzz/*
-!src/test/fuzz/*.*
+src/test/fuzz/fuzz
src/test/test_bitcoin
src/qt/test/test_bitcoin-qt
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2e11474382..ae2379fbd5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -197,7 +197,7 @@ Note: Code review is a burdensome but important part of the development process,
If your pull request contains fixup commits (commits that change the same line of code repeatedly) or too fine-grained
commits, you may be asked to [squash](https://git-scm.com/docs/git-rebase#_interactive_mode) your commits
-before it will be merged. The basic squashing workflow is shown below.
+before it will be reviewed. The basic squashing workflow is shown below.
git checkout your_branch_name
git rebase -i HEAD~n
diff --git a/Makefile.am b/Makefile.am
index f6b824faaa..66be768277 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
ACLOCAL_AMFLAGS = -I build-aux/m4
SUBDIRS = src
diff --git a/README.md b/README.md
index c73e25c429..56132d7496 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,11 @@ Bitcoin Core integration/staging tree
https://bitcoincore.org
+For an immediately usable, binary version of the Bitcoin Core software, see
+https://bitcoincore.org/en/download/.
+
+Further information about Bitcoin Core is available in the [doc folder](/doc).
+
What is Bitcoin?
----------------
@@ -12,9 +17,7 @@ with no central authority: managing transactions and issuing money are carried
out collectively by the network. Bitcoin Core is the name of open source
software which enables the use of this currency.
-For more information, as well as an immediately usable, binary version of
-the Bitcoin Core software, see https://bitcoincore.org/en/download/, or read the
-[original whitepaper](https://bitcoincore.org/bitcoin.pdf).
+For more information read the original Bitcoin whitepaper.
License
-------
@@ -53,7 +56,7 @@ submit new unit tests for old code. Unit tests can be compiled and run
and extending unit tests can be found in [/src/test/README.md](/src/test/README.md).
There are also [regression and integration tests](/test), written
-in Python, that are run automatically on the build server.
+in Python.
These tests can be run (if the [test dependencies](/test) are installed) with: `test/functional/test_runner.py`
The CI (Continuous Integration) systems make sure that every pull request is built for Windows, Linux, and macOS,
@@ -77,5 +80,3 @@ Translations are periodically pulled from Transifex and merged into the git repo
**Important**: We do not accept translation changes as GitHub pull requests because the next
pull from Transifex would automatically overwrite them again.
-
-Translators should also subscribe to the [mailing list](https://groups.google.com/forum/#!forum/bitcoin-translators).
diff --git a/build-aux/m4/ax_boost_process.m4 b/build-aux/m4/ax_boost_process.m4
deleted file mode 100644
index 5d20e67464..0000000000
--- a/build-aux/m4/ax_boost_process.m4
+++ /dev/null
@@ -1,121 +0,0 @@
-# ===========================================================================
-# https://www.gnu.org/software/autoconf-archive/ax_boost_process.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_BOOST_PROCESS
-#
-# DESCRIPTION
-#
-# Test for Process library from the Boost C++ libraries. The macro
-# requires a preceding call to AX_BOOST_BASE. Further documentation is
-# available at <http://randspringer.de/boost/index.html>.
-#
-# This macro calls:
-#
-# AC_SUBST(BOOST_PROCESS_LIB)
-#
-# And sets:
-#
-# HAVE_BOOST_PROCESS
-#
-# LICENSE
-#
-# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
-# Copyright (c) 2008 Michael Tindal
-# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 2
-
-AC_DEFUN([AX_BOOST_PROCESS],
-[
- AC_ARG_WITH([boost-process],
- AS_HELP_STRING([--with-boost-process@<:@=special-lib@:>@],
- [use the Process library from boost - it is possible to specify a certain library for the linker
- e.g. --with-boost-process=boost_process-gcc-mt ]),
- [
- if test "$withval" = "no"; then
- want_boost_process="no"
- elif test "$withval" = "yes"; then
- want_boost_process="yes"
- ax_boost_user_process_lib=""
- else
- want_boost_process="yes"
- ax_boost_user_process_lib="$withval"
- fi
- ],
- [want_boost_process="yes"]
- )
-
- if test "x$want_boost_process" = "xyes"; then
- AC_REQUIRE([AC_PROG_CC])
- AC_REQUIRE([AC_CANONICAL_BUILD])
- CPPFLAGS_SAVED="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- export CPPFLAGS
-
- LDFLAGS_SAVED="$LDFLAGS"
- LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
- export LDFLAGS
-
- AC_CACHE_CHECK(whether the Boost::Process library is available,
- ax_cv_boost_process,
- [AC_LANG_PUSH([C++])
- CXXFLAGS_SAVE=$CXXFLAGS
- CXXFLAGS=
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/process.hpp>]],
- [[boost::process::child* child = new boost::process::child; delete child;]])],
- ax_cv_boost_process=yes, ax_cv_boost_process=no)
- CXXFLAGS=$CXXFLAGS_SAVE
- AC_LANG_POP([C++])
- ])
- if test "x$ax_cv_boost_process" = "xyes"; then
- AC_SUBST(BOOST_CPPFLAGS)
-
- AC_DEFINE(HAVE_BOOST_PROCESS,,[define if the Boost::Process library is available])
- BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
-
- LDFLAGS_SAVE=$LDFLAGS
- if test "x$ax_boost_user_process_lib" = "x"; then
- for libextension in `ls -r $BOOSTLIBDIR/libboost_process* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_PROCESS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROCESS_LIB) link_process="yes"; break],
- [link_process="no"])
- done
- if test "x$link_process" != "xyes"; then
- for libextension in `ls -r $BOOSTLIBDIR/boost_process* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_PROCESS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROCESS_LIB) link_process="yes"; break],
- [link_process="no"])
- done
- fi
-
- else
- for ax_lib in $ax_boost_user_process_lib boost_process-$ax_boost_user_process_lib; do
- AC_CHECK_LIB($ax_lib, exit,
- [BOOST_PROCESS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROCESS_LIB) link_process="yes"; break],
- [link_process="no"])
- done
-
- fi
- if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the Boost::Process library!)
- fi
- if test "x$link_process" = "xno"; then
- AC_MSG_ERROR(Could not link against $ax_lib !)
- fi
- fi
-
- CPPFLAGS="$CPPFLAGS_SAVED"
- LDFLAGS="$LDFLAGS_SAVED"
- fi
-])
diff --git a/build-aux/m4/ax_boost_thread.m4 b/build-aux/m4/ax_boost_thread.m4
deleted file mode 100644
index 75e80e6e75..0000000000
--- a/build-aux/m4/ax_boost_thread.m4
+++ /dev/null
@@ -1,187 +0,0 @@
-# ===========================================================================
-# https://www.gnu.org/software/autoconf-archive/ax_boost_thread.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_BOOST_THREAD
-#
-# DESCRIPTION
-#
-# Test for Thread library from the Boost C++ libraries. The macro requires
-# a preceding call to AX_BOOST_BASE. Further documentation is available at
-# <http://randspringer.de/boost/index.html>.
-#
-# This macro calls:
-#
-# AC_SUBST(BOOST_THREAD_LIB)
-#
-# And sets:
-#
-# HAVE_BOOST_THREAD
-#
-# LICENSE
-#
-# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
-# Copyright (c) 2009 Michael Tindal
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 33
-
-AC_DEFUN([AX_BOOST_THREAD],
-[
- AC_ARG_WITH([boost-thread],
- AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@],
- [use the Thread library from boost -
- it is possible to specify a certain library for the linker
- e.g. --with-boost-thread=boost_thread-gcc-mt ]),
- [
- if test "$withval" = "yes"; then
- want_boost="yes"
- ax_boost_user_thread_lib=""
- else
- want_boost="yes"
- ax_boost_user_thread_lib="$withval"
- fi
- ],
- [want_boost="yes"]
- )
-
- if test "x$want_boost" = "xyes"; then
- AC_REQUIRE([AC_PROG_CC])
- AC_REQUIRE([AC_CANONICAL_BUILD])
- CPPFLAGS_SAVED="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- export CPPFLAGS
-
- LDFLAGS_SAVED="$LDFLAGS"
- LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
- export LDFLAGS
-
- AC_CACHE_CHECK(whether the Boost::Thread library is available,
- ax_cv_boost_thread,
- [AC_LANG_PUSH([C++])
- CXXFLAGS_SAVE=$CXXFLAGS
-
- case "x$host_os" in
- xsolaris )
- CXXFLAGS="-pthreads $CXXFLAGS"
- break;
- ;;
- xmingw32 )
- CXXFLAGS="-mthreads $CXXFLAGS"
- break;
- ;;
- *android* )
- break;
- ;;
- * )
- CXXFLAGS="-pthread $CXXFLAGS"
- break;
- ;;
- esac
-
- AC_COMPILE_IFELSE([
- AC_LANG_PROGRAM(
- [[@%:@include <boost/thread/thread.hpp>]],
- [[boost::thread_group thrds;
- return 0;]])],
- ax_cv_boost_thread=yes, ax_cv_boost_thread=no)
- CXXFLAGS=$CXXFLAGS_SAVE
- AC_LANG_POP([C++])
- ])
- if test "x$ax_cv_boost_thread" = "xyes"; then
- case "x$host_os" in
- xsolaris )
- BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
- break;
- ;;
- xmingw32 )
- BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
- break;
- ;;
- *android* )
- break;
- ;;
- * )
- BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
- break;
- ;;
- esac
-
- AC_SUBST(BOOST_CPPFLAGS)
-
- AC_DEFINE(HAVE_BOOST_THREAD,,
- [define if the Boost::Thread library is available])
- BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
-
- LDFLAGS_SAVE=$LDFLAGS
- case "x$host_os" in
- *bsd* )
- LDFLAGS="-pthread $LDFLAGS"
- break;
- ;;
- esac
- if test "x$ax_boost_user_thread_lib" = "x"; then
- for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [link_thread="yes"; break],
- [link_thread="no"])
- done
- if test "x$link_thread" != "xyes"; then
- for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do
- ax_lib=${libextension}
- AC_CHECK_LIB($ax_lib, exit,
- [link_thread="yes"; break],
- [link_thread="no"])
- done
- fi
-
- else
- for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do
- AC_CHECK_LIB($ax_lib, exit,
- [link_thread="yes"; break],
- [link_thread="no"])
- done
-
- fi
- if test "x$ax_lib" = "x"; then
- AC_MSG_ERROR(Could not find a version of the Boost::Thread library!)
- fi
- if test "x$link_thread" = "xno"; then
- AC_MSG_ERROR(Could not link against $ax_lib !)
- else
- BOOST_THREAD_LIB="-l$ax_lib"
- case "x$host_os" in
- *bsd* )
- BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS"
- break;
- ;;
- xsolaris )
- BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread"
- break;
- ;;
- xmingw32 )
- break;
- ;;
- *android* )
- break;
- ;;
- * )
- BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread"
- break;
- ;;
- esac
- AC_SUBST(BOOST_THREAD_LIB)
- fi
- fi
-
- CPPFLAGS="$CPPFLAGS_SAVED"
- LDFLAGS="$LDFLAGS_SAVED"
- fi
-])
diff --git a/build-aux/m4/ax_gcc_func_attribute.m4 b/build-aux/m4/ax_gcc_func_attribute.m4
deleted file mode 100644
index c788ca9bd4..0000000000
--- a/build-aux/m4/ax_gcc_func_attribute.m4
+++ /dev/null
@@ -1,223 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE)
-#
-# DESCRIPTION
-#
-# This macro checks if the compiler supports one of GCC's function
-# attributes; many other compilers also provide function attributes with
-# the same syntax. Compiler warnings are used to detect supported
-# attributes as unsupported ones are ignored by default so quieting
-# warnings when using this macro will yield false positives.
-#
-# The ATTRIBUTE parameter holds the name of the attribute to be checked.
-#
-# If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_<ATTRIBUTE>.
-#
-# The macro caches its result in the ax_cv_have_func_attribute_<attribute>
-# variable.
-#
-# The macro currently supports the following function attributes:
-#
-# alias
-# aligned
-# alloc_size
-# always_inline
-# artificial
-# cold
-# const
-# constructor
-# constructor_priority for constructor attribute with priority
-# deprecated
-# destructor
-# dllexport
-# dllimport
-# error
-# externally_visible
-# flatten
-# format
-# format_arg
-# gnu_inline
-# hot
-# ifunc
-# leaf
-# malloc
-# noclone
-# noinline
-# nonnull
-# noreturn
-# nothrow
-# optimize
-# pure
-# unused
-# used
-# visibility
-# warning
-# warn_unused_result
-# weak
-# weakref
-#
-# Unsuppored function attributes will be tested with a prototype returning
-# an int and not accepting any arguments and the result of the check might
-# be wrong or meaningless so use with care.
-#
-# LICENSE
-#
-# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 3
-
-AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [
- AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1])
-
- AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [
- AC_LINK_IFELSE([AC_LANG_PROGRAM([
- m4_case([$1],
- [alias], [
- int foo( void ) { return 0; }
- int bar( void ) __attribute__(($1("foo")));
- ],
- [aligned], [
- int foo( void ) __attribute__(($1(32)));
- ],
- [alloc_size], [
- void *foo(int a) __attribute__(($1(1)));
- ],
- [always_inline], [
- inline __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [artificial], [
- inline __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [cold], [
- int foo( void ) __attribute__(($1));
- ],
- [const], [
- int foo( void ) __attribute__(($1));
- ],
- [constructor_priority], [
- int foo( void ) __attribute__((__constructor__(65535/2)));
- ],
- [constructor], [
- int foo( void ) __attribute__(($1));
- ],
- [deprecated], [
- int foo( void ) __attribute__(($1("")));
- ],
- [destructor], [
- int foo( void ) __attribute__(($1));
- ],
- [dllexport], [
- __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [dllimport], [
- int foo( void ) __attribute__(($1));
- ],
- [error], [
- int foo( void ) __attribute__(($1("")));
- ],
- [externally_visible], [
- int foo( void ) __attribute__(($1));
- ],
- [flatten], [
- int foo( void ) __attribute__(($1));
- ],
- [format], [
- int foo(const char *p, ...) __attribute__(($1(printf, 1, 2)));
- ],
- [format_arg], [
- char *foo(const char *p) __attribute__(($1(1)));
- ],
- [gnu_inline], [
- inline __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [hot], [
- int foo( void ) __attribute__(($1));
- ],
- [ifunc], [
- int my_foo( void ) { return 0; }
- static int (*resolve_foo(void))(void) { return my_foo; }
- int foo( void ) __attribute__(($1("resolve_foo")));
- ],
- [leaf], [
- __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [malloc], [
- void *foo( void ) __attribute__(($1));
- ],
- [noclone], [
- int foo( void ) __attribute__(($1));
- ],
- [noinline], [
- __attribute__(($1)) int foo( void ) { return 0; }
- ],
- [nonnull], [
- int foo(char *p) __attribute__(($1(1)));
- ],
- [noreturn], [
- void foo( void ) __attribute__(($1));
- ],
- [nothrow], [
- int foo( void ) __attribute__(($1));
- ],
- [optimize], [
- __attribute__(($1(3))) int foo( void ) { return 0; }
- ],
- [pure], [
- int foo( void ) __attribute__(($1));
- ],
- [unused], [
- int foo( void ) __attribute__(($1));
- ],
- [used], [
- int foo( void ) __attribute__(($1));
- ],
- [visibility], [
- int foo_def( void ) __attribute__(($1("default")));
- int foo_hid( void ) __attribute__(($1("hidden")));
- int foo_int( void ) __attribute__(($1("internal")));
- int foo_pro( void ) __attribute__(($1("protected")));
- ],
- [warning], [
- int foo( void ) __attribute__(($1("")));
- ],
- [warn_unused_result], [
- int foo( void ) __attribute__(($1));
- ],
- [weak], [
- int foo( void ) __attribute__(($1));
- ],
- [weakref], [
- static int foo( void ) { return 0; }
- static int bar( void ) __attribute__(($1("foo")));
- ],
- [
- m4_warn([syntax], [Unsupported attribute $1, the test may fail])
- int foo( void ) __attribute__(($1));
- ]
- )], [])
- ],
- dnl GCC doesn't exit with an error if an unknown attribute is
- dnl provided but only outputs a warning, so accept the attribute
- dnl only if no warning were issued.
- [AS_IF([test -s conftest.err],
- [AS_VAR_SET([ac_var], [no])],
- [AS_VAR_SET([ac_var], [yes])])],
- [AS_VAR_SET([ac_var], [no])])
- ])
-
- AS_IF([test yes = AS_VAR_GET([ac_var])],
- [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1,
- [Define to 1 if the system has the `$1' function attribute])], [])
-
- AS_VAR_POPDEF([ac_var])
-])
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 658dfc5476..cce06e2fff 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -64,6 +64,13 @@ AC_DEFUN([BITCOIN_QT_INIT],[
],
[bitcoin_qt_want_version=auto])
+ AS_IF([test "x$with_gui" = xqt5_debug],
+ [AS_CASE([$host],
+ [*darwin*], [qt_lib_suffix=_debug],
+ [*mingw*], [qt_lib_suffix=d],
+ [qt_lib_suffix= ]); bitcoin_qt_want_version=qt5],
+ [qt_lib_suffix= ])
+
AC_ARG_WITH([qt-incdir],[AS_HELP_STRING([--with-qt-incdir=INC_DIR],[specify qt include path (overridden by pkgconfig)])], [qt_include_path=$withval], [])
AC_ARG_WITH([qt-libdir],[AS_HELP_STRING([--with-qt-libdir=LIB_DIR],[specify qt lib path (overridden by pkgconfig)])], [qt_lib_path=$withval], [])
AC_ARG_WITH([qt-plugindir],[AS_HELP_STRING([--with-qt-plugindir=PLUGIN_DIR],[specify qt plugin path (overridden by pkgconfig)])], [qt_plugin_path=$withval], [])
@@ -104,9 +111,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
dnl For Qt5, we can check a header to find out whether Qt is build
dnl statically. When Qt is built statically, some plugins must be linked into
dnl the final binary as well.
- dnl With Qt5, languages moved into core and the WindowsIntegration plugin was
- dnl added.
- dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the
+ dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN does a quick link-check and appends the
dnl results to QT_LIBS.
BITCOIN_QT_CHECK([
TEMP_CPPFLAGS=$CPPFLAGS
@@ -115,20 +120,46 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
CXXFLAGS="$PIC_FLAGS $CXXFLAGS"
_BITCOIN_QT_IS_STATIC
if test "x$bitcoin_cv_static_qt" = xyes; then
- _BITCOIN_QT_FIND_STATIC_PLUGINS
+ _BITCOIN_QT_CHECK_STATIC_LIBS
+
+ if test "x$qt_plugin_path" != x; then
+ if test -d "$qt_plugin_path/platforms"; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms"
+ fi
+ if test -d "$qt_plugin_path/styles"; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/styles"
+ fi
+ if test -d "$qt_plugin_path/accessible"; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible"
+ fi
+ if test -d "$qt_plugin_path/platforms/android"; then
+ QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL"
+ fi
+ fi
+
AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static])
if test "x$TARGET_OS" != xandroid; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMinimalIntegrationPlugin], [-lqminimal])
AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
fi
if test "x$TARGET_OS" = xwindows; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
+ dnl Linking against wtsapi32 is required. See #17749 and
+ dnl https://bugreports.qt.io/browse/QTBUG-27097.
+ AX_CHECK_LINK_FLAG([-lwtsapi32], [QT_LIBS="$QT_LIBS -lwtsapi32"], [AC_MSG_ERROR([could not link against -lwtsapi32])])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsIntegrationPlugin], [-lqwindows])
AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
elif test "x$TARGET_OS" = xlinux; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static])
+ dnl workaround for https://bugreports.qt.io/browse/QTBUG-74874
+ AX_CHECK_LINK_FLAG([-lxcb-shm], [QT_LIBS="-lxcb-shm $QT_LIBS"], [AC_MSG_ERROR([could not link against -lxcb-shm])])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb])
AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb])
elif test "x$TARGET_OS" = xdarwin; then
- _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa])
+ AX_CHECK_LINK_FLAG([[-framework Carbon]],[QT_LIBS="$QT_LIBS -framework Carbon"],[AC_MSG_ERROR(could not link against Carbon framework)])
+ AX_CHECK_LINK_FLAG([[-framework IOSurface]],[QT_LIBS="$QT_LIBS -framework IOSurface"],[AC_MSG_ERROR(could not link against IOSurface framework)])
+ AX_CHECK_LINK_FLAG([[-framework Metal]],[QT_LIBS="$QT_LIBS -framework Metal"],[AC_MSG_ERROR(could not link against Metal framework)])
+ AX_CHECK_LINK_FLAG([[-framework QuartzCore]],[QT_LIBS="$QT_LIBS -framework QuartzCore"],[AC_MSG_ERROR(could not link against QuartzCore framework)])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QCocoaIntegrationPlugin], [-lqcocoa])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMacStylePlugin], [-lqmacstyle])
AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa])
elif test "x$TARGET_OS" = xandroid; then
QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype -lQt5EglSupport $QT_LIBS"
@@ -277,51 +308,50 @@ AC_DEFUN([_BITCOIN_QT_IS_STATIC],[
])
])
-dnl Internal. Check if the link-requirements for static plugins are met.
+dnl Internal. Check if the link-requirements for a static plugin are met.
+dnl
+dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN(PLUGIN, LIBRARIES)
+dnl --------------------------------------------------
+dnl
dnl Requires: INCLUDES and LIBS must be populated as necessary.
-dnl Inputs: $1: A series of Q_IMPORT_PLUGIN().
+dnl Inputs: $1: A static plugin name.
dnl Inputs: $2: The libraries that resolve $1.
dnl Output: QT_LIBS is prepended or configure exits.
-AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGINS],[
- AC_MSG_CHECKING(for static Qt plugins: $2)
+AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGIN], [
+ AC_MSG_CHECKING([for $1 ($2)])
CHECK_STATIC_PLUGINS_TEMP_LIBS="$LIBS"
- LIBS="$2 $QT_LIBS $LIBS"
+ LIBS="$2${qt_lib_suffix} $QT_LIBS $LIBS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #define QT_STATICPLUGIN
- #include <QtPlugin>
- $1]],
- [[return 0;]])],
- [AC_MSG_RESULT(yes); QT_LIBS="$2 $QT_LIBS"],
- [AC_MSG_RESULT(no); BITCOIN_QT_FAIL(Could not resolve: $2)])
+ #include <QtPlugin>
+ Q_IMPORT_PLUGIN($1)
+ ]])],
+ [AC_MSG_RESULT([yes]); QT_LIBS="$2${qt_lib_suffix} $QT_LIBS"],
+ [AC_MSG_RESULT([no]); BITCOIN_QT_FAIL([$1 not found.])])
LIBS="$CHECK_STATIC_PLUGINS_TEMP_LIBS"
])
-dnl Internal. Find paths necessary for linking qt static plugins
-dnl Inputs: qt_plugin_path. optional.
-dnl Outputs: QT_LIBS is appended
-AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
- if test "x$qt_plugin_path" != x; then
- QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms"
- if test -d "$qt_plugin_path/accessible"; then
- QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible"
- fi
- if test -d "$qt_plugin_path/platforms/android"; then
- QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL"
- fi
- PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
- if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
- elif test "x$TARGET_OS" = xdarwin; then
- PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"])
- fi
- fi
+dnl Internal. Check Qt static libs with PKG_CHECK_MODULES.
+dnl
+dnl _BITCOIN_QT_CHECK_STATIC_LIBS
+dnl -----------------------------
+dnl
+dnl Inputs: no inputs.
+dnl Outputs: QT_LIBS is prepended.
+AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [
+ PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FontDatabaseSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="-lQt5EventDispatcherSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ThemeSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="-lQt5DeviceDiscoverySupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="-lQt5AccessibilitySupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTFB], [Qt5FbSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FbSupport${qt_lib_suffix} $QT_LIBS"])
+ if test "x$TARGET_OS" = xlinux; then
+ PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
+ elif test "x$TARGET_OS" = xdarwin; then
+ PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"])
+ elif test "x$TARGET_OS" = xwindows; then
+ PKG_CHECK_MODULES([QTWINDOWSUIAUTOMATION], [Qt5WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="-lQt5WindowsUIAutomationSupport${qt_lib_suffix} $QT_LIBS"])
+ fi
])
dnl Internal. Find Qt libraries using pkg-config.
@@ -329,26 +359,26 @@ dnl Outputs: All necessary QT_* variables are set.
dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no.
AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core $qt_version], [],
- [BITCOIN_QT_FAIL([${qt_lib_prefix}Core $qt_version not found])])
+ PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Core${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui $qt_version], [],
- [BITCOIN_QT_FAIL([${qt_lib_prefix}Gui $qt_version not found])])
+ PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets $qt_version], [],
- [BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets $qt_version not found])])
+ PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network $qt_version], [],
- [BITCOIN_QT_FAIL([${qt_lib_prefix}Network $qt_version not found])])
+ PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Network${qt_lib_suffix} $qt_version not found])])
])
QT_INCLUDES="$QT_CORE_CFLAGS $QT_GUI_CFLAGS $QT_WIDGETS_CFLAGS $QT_NETWORK_CFLAGS"
QT_LIBS="$QT_CORE_LIBS $QT_GUI_LIBS $QT_WIDGETS_LIBS $QT_NETWORK_LIBS"
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
+ PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test${qt_lib_suffix} $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
if test "x$use_dbus" != xno; then
PKG_CHECK_MODULES([QT_DBUS], [${qt_lib_prefix}DBus $qt_version], [QT_DBUS_INCLUDES="$QT_DBUS_CFLAGS"; have_qt_dbus=yes], [have_qt_dbus=no])
fi
diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h
index 53aead38b5..dd01cb29eb 100644
--- a/build_msvc/bitcoin_config.h
+++ b/build_msvc/bitcoin_config.h
@@ -50,15 +50,12 @@
/* define if the Boost::Filesystem library is available */
#define HAVE_BOOST_FILESYSTEM /**/
-/* define if the Boost::Process library is available */
-#define HAVE_BOOST_PROCESS /**/
+/* define if external signer support is enabled (requires Boost::Process) */
+#define ENABLE_EXTERNAL_SIGNER /**/
/* define if the Boost::System library is available */
#define HAVE_BOOST_SYSTEM /**/
-/* define if the Boost::Thread library is available */
-#define HAVE_BOOST_THREAD /**/
-
/* define if the Boost::Unit_Test_Framework library is available */
#define HAVE_BOOST_UNIT_TEST_FRAMEWORK /**/
@@ -95,9 +92,9 @@
don't. */
#define HAVE_DECL_BSWAP_64 0
-/* Define to 1 if you have the declaration of `daemon', and to 0 if you don't.
+/* Define to 1 if you have the declaration of `fork', and to 0 if you don't.
*/
-#define HAVE_DECL_DAEMON 0
+#define HAVE_DECL_FORK 0
/* Define to 1 if you have the declaration of `htobe16', and to 0 if you
don't. */
@@ -135,6 +132,10 @@
don't. */
#define HAVE_DECL_LE64TOH 0
+/* Define to 1 if you have the declaration of `setsid', and to 0 if you don't.
+ */
+#define HAVE_DECL_SETSID 0
+
/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
don't. */
#define HAVE_DECL_STRERROR_R 0
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index 9c589bccbc..a3487c7a0d 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -96,7 +96,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
- <AdditionalOptions>/utf-8 /std:c++17 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalOptions>/utf-8 /Zc:__cplusplus /std:c++17 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
diff --git a/build_msvc/vcpkg.json b/build_msvc/vcpkg.json
index dfd3929c44..42b9a5d16f 100644
--- a/build_msvc/vcpkg.json
+++ b/build_msvc/vcpkg.json
@@ -8,7 +8,6 @@
"boost-process",
"boost-signals2",
"boost-test",
- "boost-thread",
"sqlite3",
"double-conversion",
{
diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh
index a0b579de1e..343b82a1ad 100755
--- a/ci/lint/04_install.sh
+++ b/ci/lint/04_install.sh
@@ -15,6 +15,7 @@ ${CI_RETRY_EXE} pip3 install codespell==2.0.0
${CI_RETRY_EXE} pip3 install flake8==3.8.3
${CI_RETRY_EXE} pip3 install yq
${CI_RETRY_EXE} pip3 install mypy==0.781
+${CI_RETRY_EXE} pip3 install vulture==2.3
SHELLCHECK_VERSION=v0.7.1
curl -sL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index 72e29141a6..87cf8538f6 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -65,7 +65,7 @@ export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_SCRATCH_DIR/out/$HOST}
export BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build}
export PREVIOUS_RELEASES_DIR=${PREVIOUS_RELEASES_DIR:-$BASE_ROOT_DIR/releases/$HOST}
export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks}
-export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps}
+export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps bison}
export GOAL=${GOAL:-install}
export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_SCRATCH_DIR}/qa-assets}
export PATH=${BASE_ROOT_DIR}/ci/retry:$PATH
diff --git a/ci/test/00_setup_env_i686_centos.sh b/ci/test/00_setup_env_i686_centos.sh
index 1ec44a7a1a..07e7c2dc27 100644
--- a/ci/test/00_setup_env_i686_centos.sh
+++ b/ci/test/00_setup_env_i686_centos.sh
@@ -9,7 +9,7 @@ export LC_ALL=C.UTF-8
export HOST=i686-pc-linux-gnu
export CONTAINER_NAME=ci_i686_centos_8
export DOCKER_NAME_TAG=centos:8
-export DOCKER_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-zmq which patch lbzip2 dash rsync coreutils"
+export DOCKER_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-zmq which patch lbzip2 dash rsync coreutils bison"
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports --with-boost-process"
export CONFIG_SHELL="/bin/dash"
diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh
index b0de2ec0bb..6ce3812447 100644
--- a/ci/test/00_setup_env_mac.sh
+++ b/ci/test/00_setup_env_mac.sh
@@ -7,9 +7,9 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_macos_cross
-export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic can cross-compile to macos (bionic is used in the gitian build as well)
+export DOCKER_NAME_TAG=ubuntu:20.04 # Check that Focal can cross-compile to macos (Focal is used in the gitian build as well)
export HOST=x86_64-apple-darwin18
-export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools xorriso"
+export PACKAGES="cmake imagemagick librsvg2-bin libz-dev libtiff-tools libtinfo5 python3-setuptools xorriso"
export XCODE_VERSION=11.3.1
export XCODE_BUILD_ID=11C505
export RUN_UNIT_TESTS=false
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index f682486088..e47119e6fa 100644
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_asan
-export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev"
+export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev"
export DOCKER_NAME_TAG=ubuntu:20.04
export NO_DEPENDS=1
export GOAL="install"
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index b7157c608d..ebb5a1cabe 100644
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8
export DOCKER_NAME_TAG="ubuntu:20.04"
export CONTAINER_NAME=ci_native_fuzz
-export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev"
+export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev"
export NO_DEPENDS=1
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
index e06a40eb23..2cf672b91e 100644
--- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8
export DOCKER_NAME_TAG="ubuntu:20.04"
export CONTAINER_NAME=ci_native_fuzz_valgrind
-export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev valgrind"
+export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev valgrind"
export NO_DEPENDS=1
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
diff --git a/ci/test/00_setup_env_native_multiprocess.sh b/ci/test/00_setup_env_native_multiprocess.sh
index 522a5d9fc2..c5692d786a 100644
--- a/ci/test/00_setup_env_native_multiprocess.sh
+++ b/ci/test/00_setup_env_native_multiprocess.sh
@@ -13,3 +13,4 @@ export DEP_OPTS="MULTIPROCESS=1"
export GOAL="install"
export BITCOIN_CONFIG="--with-boost-process"
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
+export RUN_SECURITY_TESTS="true"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 567145fe47..4c42605e9a 100644
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -11,9 +11,9 @@ export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic gcc-7 can compile our c
export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
export DEP_OPTS="NO_QT=1 NO_UPNP=1 NO_NATPMP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
-export RUN_SECURITY_TESTS="true"
export RUN_UNIT_TESTS_SEQUENTIAL="true"
export RUN_UNIT_TESTS="false"
export GOAL="install"
export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.2 v0.16.3 v0.17.2 v0.18.1 v0.19.1"
-export BITCOIN_CONFIG="--enable-zmq --with-libs=no --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --with-boost-process"
+export BITCOIN_CONFIG="--enable-zmq --with-libs=no --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports
+--enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --with-boost-process"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index f0c153158b..e079a7057c 100644
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_valgrind
-export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev"
+export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libsqlite3-dev"
export USE_VALGRIND=1
export NO_DEPENDS=1
export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
index affaaaa1aa..1e68d2a61a 100644
--- a/ci/test/00_setup_env_win64.sh
+++ b/ci/test/00_setup_env_win64.sh
@@ -7,11 +7,11 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_win64
-export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic can cross-compile to win64 (bionic is used in the gitian build as well)
+export DOCKER_NAME_TAG=ubuntu:20.04 # Check that Focal can cross-compile to win64 (Focal is used in the gitian build as well)
export HOST=x86_64-w64-mingw32
-export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 file"
+export DPKG_ADD_ARCH="i386"
+export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 wine32 file"
export RUN_FUNCTIONAL_TESTS=false
-export RUN_SECURITY_TESTS="true"
export GOAL="deploy"
export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests --without-boost-process"
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index 4644f28a4e..f69afd8a26 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -19,7 +19,7 @@ OSX_SDK_BASENAME="Xcode-${XCODE_VERSION}-${XCODE_BUILD_ID}-extracted-SDK-with-li
OSX_SDK_PATH="${DEPENDS_DIR}/sdk-sources/${OSX_SDK_BASENAME}"
if [ -n "$XCODE_VERSION" ] && [ ! -f "$OSX_SDK_PATH" ]; then
- curl --location --fail "${SDK_URL}/${OSX_SDK_BASENAME}" -o "$OSX_SDK_PATH"
+ DOCKER_EXEC curl --location --fail "${SDK_URL}/${OSX_SDK_BASENAME}" -o "$OSX_SDK_PATH"
fi
if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then
diff --git a/ci/test/wrap-wine.sh b/ci/test/wrap-wine.sh
index 58a8983e6e..82964897e1 100755
--- a/ci/test/wrap-wine.sh
+++ b/ci/test/wrap-wine.sh
@@ -13,7 +13,7 @@ for b_name in {"${BASE_OUTDIR}/bin"/*,src/secp256k1/*tests,src/univalue/{no_nul,
echo "Wrap $b ..."
mv "$b" "${b}_orig"
echo '#!/usr/bin/env bash' > "$b"
- echo "wine64 \"${b}_orig\" \"\$@\"" >> "$b"
+ echo "( wine \"${b}_orig\" \"\$@\" ) || ( sleep 1 && wine \"${b}_orig\" \"\$@\" )" >> "$b"
chmod +x "$b"
fi
done
diff --git a/configure.ac b/configure.ac
index dca4c5edd8..0b176e6039 100644
--- a/configure.ac
+++ b/configure.ac
@@ -184,10 +184,16 @@ AC_ARG_ENABLE([extended-functional-tests],
AC_ARG_ENABLE([fuzz],
AS_HELP_STRING([--enable-fuzz],
- [enable building of fuzz targets (default no). enabling this will disable all other targets]),
+ [build for fuzzing (default no). enabling this will disable all other targets and override --{enable,disable}-fuzz-binary]),
[enable_fuzz=$enableval],
[enable_fuzz=no])
+AC_ARG_ENABLE([fuzz-binary],
+ AS_HELP_STRING([--enable-fuzz-binary],
+ [enable building of fuzz binary (default yes).]),
+ [enable_fuzz_binary=$enableval],
+ [enable_fuzz_binary=yes])
+
AC_ARG_ENABLE([danger_fuzz_link_all],
AS_HELP_STRING([--enable-danger-fuzz-link-all],
[Danger! Modifies source code. Needs git and gnu sed installed. Link each fuzz target (default no).]),
@@ -332,6 +338,11 @@ AC_ARG_ENABLE([werror],
[enable_werror=$enableval],
[enable_werror=no])
+AC_ARG_ENABLE([external-signer],
+ [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is no, requires Boost::Process)])],
+ [use_external_signer=$enableval],
+ [use_external_signer=no])
+
AC_LANG_PUSH([C++])
dnl Check for a flag to turn compiler warnings into errors. This is helpful for checks which may
@@ -651,7 +662,7 @@ case $host in
AC_MSG_ERROR("windres not found")
fi
- CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN"
+ CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN"
dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against.
dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override
@@ -807,10 +818,6 @@ if test x$ac_cv_sys_large_files != x &&
CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=$ac_cv_sys_large_files"
fi
-AX_GCC_FUNC_ATTRIBUTE([visibility])
-AX_GCC_FUNC_ATTRIBUTE([dllexport])
-AX_GCC_FUNC_ATTRIBUTE([dllimport])
-
if test x$use_glibc_compat != xno; then
AX_CHECK_LINK_FLAG([[-Wl,--wrap=__divmoddi4]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=__divmoddi4"])
AX_CHECK_LINK_FLAG([[-Wl,--wrap=log2f]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=log2f"])
@@ -854,7 +861,10 @@ if test x$use_hardening != xno; then
AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"])
- AX_CHECK_COMPILE_FLAG([-fcf-protection=full],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"])
+ dnl -fcf-protection used with Clang 7 causes ld to emit warnings:
+ dnl ld: error: ... <corrupt x86 feature size: 0x8>
+ dnl Use CHECK_LINK_FLAG & --fatal-warnings to ensure we won't use the flag in this case.
+ AX_CHECK_LINK_FLAG([-fcf-protection=full],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"],, [[$LDFLAG_WERROR]])
dnl stack-clash-protection does not work properly when building for Windows.
dnl We use the test case from https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458
@@ -912,8 +922,11 @@ AC_CHECK_DECLS([getifaddrs, freeifaddrs],,,
)
AC_CHECK_DECLS([strnlen])
-dnl Check for daemon(3), unrelated to --with-daemon (although used by it)
-AC_CHECK_DECLS([daemon])
+dnl These are used for daemonization in bitcoind
+AC_CHECK_DECLS([fork])
+AC_CHECK_DECLS([setsid])
+
+AC_CHECK_DECLS([pipe2])
AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,,
[#if HAVE_ENDIAN_H
@@ -975,13 +988,13 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
[ AC_MSG_RESULT(no)]
)
-AC_MSG_CHECKING([for visibility attribute])
-AC_LINK_IFELSE([AC_LANG_SOURCE([
- int foo_def( void ) __attribute__((visibility("default")));
+AC_MSG_CHECKING([for default visibility attribute])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ int foo(void) __attribute__((visibility("default")));
int main(){}
])],
[
- AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE,1,[Define if the visibility attribute is supported.])
+ AC_DEFINE(HAVE_DEFAULT_VISIBILITY_ATTRIBUTE,1,[Define if the visibility attribute is supported.])
AC_MSG_RESULT(yes)
],
[
@@ -992,6 +1005,18 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
]
)
+AC_MSG_CHECKING([for dllexport attribute])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ __declspec(dllexport) int foo(void);
+ int main(){}
+ ])],
+ [
+ AC_DEFINE(HAVE_DLLEXPORT_ATTRIBUTE,1,[Define if the dllexport attribute is supported.])
+ AC_MSG_RESULT(yes)
+ ],
+ [AC_MSG_RESULT(no)]
+)
+
dnl thread_local is currently disabled when building with glibc back compat.
dnl Our minimum supported glibc is 2.17, however support for thread_local
dnl did not arrive in glibc until 2.18.
@@ -1127,6 +1152,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>]],
[ AC_MSG_RESULT(yes); HAVE_O_CLOEXEC=1 ],
[ AC_MSG_RESULT(no); HAVE_O_CLOEXEC=0 ]
)
+AC_DEFINE_UNQUOTED([HAVE_O_CLOEXEC], [$HAVE_O_CLOEXEC], [Define to 1 if O_CLOEXEC flag is available.])
dnl crc32c platform checks
AC_MSG_CHECKING(for __builtin_prefetch)
@@ -1174,12 +1200,6 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
[ AC_MSG_RESULT(no); HAVE_WEAK_GETAUXVAL=0 ]
)
-dnl Check for reduced exports
-if test x$use_reduce_exports = xyes; then
- AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"],
- [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])])
-fi
-
AC_MSG_CHECKING([for std::system])
AC_LINK_IFELSE(
[ AC_LANG_PROGRAM(
@@ -1224,7 +1244,7 @@ AC_DEFUN([SUPPRESS_WARNINGS],
dnl enable-fuzz should disable all other targets
if test "x$enable_fuzz" = "xyes"; then
- AC_MSG_WARN(enable-fuzz will disable all other targets)
+ AC_MSG_WARN(enable-fuzz will disable all other targets and force --enable-fuzz-binary=yes)
build_bitcoin_utils=no
build_bitcoin_cli=no
build_bitcoin_tx=no
@@ -1237,30 +1257,34 @@ if test "x$enable_fuzz" = "xyes"; then
bitcoin_enable_qt_dbus=no
enable_wallet=no
use_bench=no
+ use_external_signer=no
use_upnp=no
use_natpmp=no
use_zmq=no
+ enable_fuzz_binary=yes
AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]])
- AC_MSG_CHECKING([whether main function is needed])
+ AC_MSG_CHECKING([whether main function is needed for fuzz binary])
AX_CHECK_LINK_FLAG(
[[-fsanitize=$use_sanitizers]],
[AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes])
- CPPFLAGS="$CPPFLAGS -DPROVIDE_MAIN_FUNCTION"],
+ CPPFLAGS="$CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"],
[],
[AC_LANG_PROGRAM([[
#include <cstdint>
#include <cstddef>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; }
- /* unterminated comment to remove the main function ...
- ]],[[]])])
+ /* comment to remove the main function ...
+ ]],[[
+ */ int not_main() {
+ ]])])
else
BITCOIN_QT_INIT
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
- BITCOIN_QT_CONFIGURE([5.5.1])
+ BITCOIN_QT_CONFIGURE([5.9.5])
dnl Keep a copy of the original $QT_INCLUDES and use it when invoking qt's moc
QT_INCLUDES_UNSUPPRESSED=$QT_INCLUDES
@@ -1269,6 +1293,8 @@ else
QT_DBUS_INCLUDES=SUPPRESS_WARNINGS($QT_DBUS_INCLUDES)
QT_TEST_INCLUDES=SUPPRESS_WARNINGS($QT_TEST_INCLUDES)
fi
+
+ CPPFLAGS="$CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"
fi
if test x$enable_wallet != xno; then
@@ -1354,43 +1380,52 @@ if test "x$use_natpmp" != xno; then
fi
if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then
- use_boost=no
+ use_boost=no
else
- use_boost=yes
+ use_boost=yes
fi
if test x$use_boost = xyes; then
-dnl Minimum required Boost version
-define(MINIMUM_REQUIRED_BOOST, 1.58.0)
-
-dnl Check for Boost libs
-AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST])
-if test x$want_boost = xno; then
+ dnl Check for Boost headers
+ AX_BOOST_BASE([1.58.0],[],[AC_MSG_ERROR([Boost is not available!])])
+ if test x$want_boost = xno; then
AC_MSG_ERROR([[only libbitcoinconsensus can be built without boost]])
-fi
-AX_BOOST_SYSTEM
-AX_BOOST_FILESYSTEM
-AX_BOOST_THREAD
-
-dnl Opt-in to boost-process
-AS_IF([ test x$with_boost_process != x ], [ AX_BOOST_PROCESS ], [ ax_cv_boost_process=no ] )
+ fi
+ AX_BOOST_SYSTEM
+ AX_BOOST_FILESYSTEM
+
+ dnl Opt-in to Boost Process if external signer support is requested
+ if test "x$use_external_signer" != xno; then
+ AC_MSG_CHECKING(for Boost Process)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <boost/process.hpp>]],
+ [[ boost::process::child* child = new boost::process::child; delete child; ]])],
+ [ AC_MSG_RESULT(yes)
+ AC_DEFINE([ENABLE_EXTERNAL_SIGNER],,[define if external signer support is enabled])
+ ],
+ [ AC_MSG_ERROR([Boost::Process is required for external signer support, but not available!])]
+ )
+ fi
-if test x$suppress_external_warnings != xno; then
+ if test x$suppress_external_warnings != xno; then
BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS)
-fi
+ fi
-dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic
-dnl counter implementations. In 1.63 and later the std::atomic approach is default.
-m4_pattern_allow(DBOOST_AC_USE_STD_ATOMIC) dnl otherwise it's treated like a macro
-BOOST_CPPFLAGS="-DBOOST_SP_USE_STD_ATOMIC -DBOOST_AC_USE_STD_ATOMIC $BOOST_CPPFLAGS"
+ dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic
+ dnl counter implementations. In 1.63 and later the std::atomic approach is default.
+ m4_pattern_allow(DBOOST_AC_USE_STD_ATOMIC) dnl otherwise it's treated like a macro
+ BOOST_CPPFLAGS="-DBOOST_SP_USE_STD_ATOMIC -DBOOST_AC_USE_STD_ATOMIC $BOOST_CPPFLAGS"
-BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB"
+ BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB"
fi
+AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "x$use_external_signer" = "xyes"])
+
+dnl Check for reduced exports
if test x$use_reduce_exports = xyes; then
- CXXFLAGS="$CXXFLAGS $RE_CXXFLAGS"
- AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]], [RELDFLAGS="-Wl,--exclude-libs,ALL"],, [[$LDFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],
+ [AC_MSG_ERROR([Cannot set hidden symbol visibility. Use --disable-reduce-exports.])],[[$CXXFLAG_WERROR]])
+ AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]],[RELDFLAGS="-Wl,--exclude-libs,ALL"],,[[$LDFLAG_WERROR]])
fi
if test x$use_tests = xyes; then
@@ -1401,25 +1436,25 @@ if test x$use_tests = xyes; then
if test x$use_boost = xyes; then
- AX_BOOST_UNIT_TEST_FRAMEWORK
-
- dnl Determine if -DBOOST_TEST_DYN_LINK is needed
- AC_MSG_CHECKING([for dynamic linked boost test])
- TEMP_LIBS="$LIBS"
- LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB"
- TEMP_CPPFLAGS="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
- AC_LINK_IFELSE([AC_LANG_SOURCE([
- #define BOOST_TEST_DYN_LINK
- #define BOOST_TEST_MAIN
- #include <boost/test/unit_test.hpp>
-
- ])],
- [AC_MSG_RESULT(yes)]
- [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"],
- [AC_MSG_RESULT(no)])
- LIBS="$TEMP_LIBS"
- CPPFLAGS="$TEMP_CPPFLAGS"
+ AX_BOOST_UNIT_TEST_FRAMEWORK
+
+ dnl Determine if -DBOOST_TEST_DYN_LINK is needed
+ AC_MSG_CHECKING([for dynamic linked boost test])
+ TEMP_LIBS="$LIBS"
+ LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB"
+ TEMP_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([
+ #define BOOST_TEST_DYN_LINK
+ #define BOOST_TEST_MAIN
+ #include <boost/test/unit_test.hpp>
+
+ ])],
+ [AC_MSG_RESULT(yes)]
+ [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"],
+ [AC_MSG_RESULT(no)])
+ LIBS="$TEMP_LIBS"
+ CPPFLAGS="$TEMP_CPPFLAGS"
fi
fi
@@ -1635,6 +1670,9 @@ else
fi
AC_MSG_RESULT($use_natpmp_default)
AC_DEFINE_UNQUOTED([USE_NATPMP], [$natpmp_setting], [NAT-PMP support not compiled if undefined, otherwise value (0 or 1) determines default state])
+ if test x$TARGET_OS = xwindows; then
+ NATPMP_CPPFLAGS="-DSTATICLIB -DNATPMP_STATICLIB"
+ fi
else
AC_MSG_RESULT([no])
fi
@@ -1714,6 +1752,7 @@ AM_CONDITIONAL([USE_BDB], [test "x$use_bdb" = "xyes"])
AM_CONDITIONAL([ENABLE_TRACING],[test x$have_sdt = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
AM_CONDITIONAL([ENABLE_FUZZ],[test x$enable_fuzz = xyes])
+AM_CONDITIONAL([ENABLE_FUZZ_BINARY],[test x$enable_fuzz_binary = xyes])
AM_CONDITIONAL([ENABLE_FUZZ_LINK_ALL],[test x$enable_danger_fuzz_link_all = xyes])
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes])
@@ -1730,6 +1769,8 @@ AM_CONDITIONAL([ENABLE_SHANI],[test x$enable_shani = xyes])
AM_CONDITIONAL([ENABLE_ARM_CRC],[test x$enable_arm_crc = xyes])
AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes])
AM_CONDITIONAL([WORDS_BIGENDIAN],[test x$ac_cv_c_bigendian = xyes])
+AM_CONDITIONAL([USE_NATPMP],[test x$use_natpmp = xyes])
+AM_CONDITIONAL([USE_UPNP],[test x$use_upnp = xyes])
AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version])
AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version])
@@ -1781,6 +1822,7 @@ AC_SUBST(ARM_CRC_CXXFLAGS)
AC_SUBST(LIBTOOL_APP_LDFLAGS)
AC_SUBST(USE_SQLITE)
AC_SUBST(USE_BDB)
+AC_SUBST(ENABLE_EXTERNAL_SIGNER)
AC_SUBST(USE_UPNP)
AC_SUBST(USE_QRCODE)
AC_SUBST(BOOST_LIBS)
@@ -1788,6 +1830,7 @@ AC_SUBST(SQLITE_LIBS)
AC_SUBST(TESTDEFS)
AC_SUBST(MINIUPNPC_CPPFLAGS)
AC_SUBST(MINIUPNPC_LIBS)
+AC_SUBST(NATPMP_CPPFLAGS)
AC_SUBST(NATPMP_LIBS)
AC_SUBST(EVENT_LIBS)
AC_SUBST(EVENT_PTHREADS_LIBS)
@@ -1856,42 +1899,43 @@ esac
echo
echo "Options used to compile and link:"
-echo " boost process = $ax_cv_boost_process"
-echo " multiprocess = $build_multiprocess"
-echo " with wallet = $enable_wallet"
+echo " external signer = $use_external_signer"
+echo " multiprocess = $build_multiprocess"
+echo " with libs = $build_bitcoin_libs"
+echo " with wallet = $enable_wallet"
if test "x$enable_wallet" != "xno"; then
- echo " with sqlite = $use_sqlite"
- echo " with bdb = $use_bdb"
+ echo " with sqlite = $use_sqlite"
+ echo " with bdb = $use_bdb"
fi
-echo " with gui / qt = $bitcoin_enable_qt"
+echo " with gui / qt = $bitcoin_enable_qt"
if test x$bitcoin_enable_qt != xno; then
- echo " with qr = $use_qr"
+ echo " with qr = $use_qr"
fi
-echo " with zmq = $use_zmq"
+echo " with zmq = $use_zmq"
if test x$enable_fuzz == xno; then
- echo " with test = $use_tests"
+ echo " with test = $use_tests"
else
- echo " with test = not building test_bitcoin because fuzzing is enabled"
- echo " with fuzz = $enable_fuzz"
+ echo " with test = not building test_bitcoin because fuzzing is enabled"
+ echo " with fuzz = $enable_fuzz"
fi
-echo " with bench = $use_bench"
-echo " with upnp = $use_upnp"
-echo " with natpmp = $use_natpmp"
-echo " use asm = $use_asm"
-echo " ebpf tracing = $have_sdt"
-echo " sanitizers = $use_sanitizers"
-echo " debug enabled = $enable_debug"
-echo " gprof enabled = $enable_gprof"
-echo " werror = $enable_werror"
+echo " with bench = $use_bench"
+echo " with upnp = $use_upnp"
+echo " with natpmp = $use_natpmp"
+echo " use asm = $use_asm"
+echo " ebpf tracing = $have_sdt"
+echo " sanitizers = $use_sanitizers"
+echo " debug enabled = $enable_debug"
+echo " gprof enabled = $enable_gprof"
+echo " werror = $enable_werror"
echo
-echo " target os = $TARGET_OS"
-echo " build os = $build_os"
+echo " target os = $TARGET_OS"
+echo " build os = $build_os"
echo
-echo " CC = $CC"
-echo " CFLAGS = $PTHREAD_CFLAGS $CFLAGS"
-echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS"
-echo " CXX = $CXX"
-echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $GPROF_CXXFLAGS $CXXFLAGS"
-echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS"
-echo " ARFLAGS = $ARFLAGS"
+echo " CC = $CC"
+echo " CFLAGS = $PTHREAD_CFLAGS $CFLAGS"
+echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS"
+echo " CXX = $CXX"
+echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $GPROF_CXXFLAGS $CXXFLAGS"
+echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS"
+echo " ARFLAGS = $ARFLAGS"
echo
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index a18c5bccc5..7ee7f056d9 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -87,6 +87,10 @@ Files: src/qt/res/icons/proxy.png
Copyright: Cristian Mircea Messel
License: public-domain
+Files: src/qt/fonts/RobotoMono-Bold.ttf
+License: Apache-2.0
+Comment: Site: https://fonts.google.com/specimen/Roboto+Mono
+
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a
@@ -144,3 +148,14 @@ Comment:
License: public-domain
This work is in the public domain.
+
+License: Apache-2.0
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index bdff7a84b0..1fa850af1a 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -7,7 +7,8 @@ clang-format-diff.py
A script to format unified git diffs according to [.clang-format](../../src/.clang-format).
-Requires `clang-format`, installed e.g. via `brew install clang-format` on macOS.
+Requires `clang-format`, installed e.g. via `brew install clang-format` on macOS,
+or `sudo apt install clang-format` on Debian/Ubuntu.
For instance, to format the last commit with 0 lines of context,
the script should be called from the git root folder as follows.
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index b30ed62521..436f179d61 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -73,6 +73,8 @@ ELF_ALLOWED_LIBRARIES = {
'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker
# bitcoin-qt only
'libxcb.so.1', # part of X11
+'libxkbcommon.so.0', # keyboard keymapping
+'libxkbcommon-x11.so.0', # keyboard keymapping
'libfontconfig.so.1', # font support
'libfreetype.so.6', # font parsing
'libdl.so.2' # programming interface to dynamic linker
@@ -98,10 +100,15 @@ MACHO_ALLOWED_LIBRARIES = {
'CoreGraphics', # 2D rendering
'CoreServices', # operating system services
'CoreText', # interface for laying out text and handling fonts.
+'CoreVideo', # video processing
'Foundation', # base layer functionality for apps/frameworks
'ImageIO', # read and write image file formats.
'IOKit', # user-space access to hardware devices and drivers.
+'IOSurface', # cross process image/drawing buffers
'libobjc.A.dylib', # Objective-C runtime library
+'Metal', # 3D graphics
+'Security', # access control and authentication
+'QuartzCore', # animation
}
PE_ALLOWED_LIBRARIES = {
@@ -116,12 +123,15 @@ PE_ALLOWED_LIBRARIES = {
'dwmapi.dll', # desktop window manager
'GDI32.dll', # graphics device interface
'IMM32.dll', # input method editor
+'NETAPI32.dll',
'ole32.dll', # component object model
'OLEAUT32.dll', # OLE Automation API
'SHLWAPI.dll', # light weight shell API
+'USERENV.dll',
'UxTheme.dll',
'VERSION.dll', # version checking
'WINMM.dll', # WinMM audio API
+'WTSAPI32.dll',
}
class CPPFilt(object):
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index ec2d886653..28b5f57489 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -5,6 +5,7 @@
'''
Test script for security-check.py
'''
+import os
import subprocess
import unittest
@@ -19,6 +20,10 @@ def write_testcode(filename):
}
''')
+def clean_files(source, executable):
+ os.remove(source)
+ os.remove(executable)
+
def call_security_check(cc, source, executable, options):
subprocess.run([cc,source,'-o',executable] + options, check=True)
p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
@@ -44,6 +49,8 @@ class TestSecurityChecks(unittest.TestCase):
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
(0, ''))
+ clean_files(source, executable)
+
def test_PE(self):
source = 'test1.c'
executable = 'test1.exe'
@@ -61,6 +68,8 @@ class TestSecurityChecks(unittest.TestCase):
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE']),
(0, ''))
+ clean_files(source, executable)
+
def test_MACHO(self):
source = 'test1.c'
executable = 'test1'
@@ -80,6 +89,8 @@ class TestSecurityChecks(unittest.TestCase):
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all']),
(0, ''))
+ clean_files(source, executable)
+
if __name__ == '__main__':
unittest.main()
diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py
index 18ed7d61e0..106dfd2c5a 100755
--- a/contrib/devtools/test-symbol-check.py
+++ b/contrib/devtools/test-symbol-check.py
@@ -5,47 +5,43 @@
'''
Test script for symbol-check.py
'''
+import os
import subprocess
import unittest
def call_symbol_check(cc, source, executable, options):
subprocess.run([cc,source,'-o',executable] + options, check=True)
p = subprocess.run(['./contrib/devtools/symbol-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
+ os.remove(source)
+ os.remove(executable)
return (p.returncode, p.stdout.rstrip())
-def get_machine(cc):
- p = subprocess.run([cc,'-dumpmachine'], stdout=subprocess.PIPE, universal_newlines=True)
- return p.stdout.rstrip()
-
class TestSymbolChecks(unittest.TestCase):
def test_ELF(self):
source = 'test1.c'
executable = 'test1'
cc = 'gcc'
- # there's no way to do this test for RISC-V at the moment; bionic's libc is 2.27
- # and we allow all symbols from 2.27.
- if 'riscv' in get_machine(cc):
- self.skipTest("test not available for RISC-V")
-
- # memfd_create was introduced in GLIBC 2.27, so is newer than the upper limit of
- # all but RISC-V but still available on bionic
+ # renameat2 was introduced in GLIBC 2.28, so is newer than the upper limit
+ # of glibc for all platforms
with open(source, 'w', encoding="utf8") as f:
f.write('''
#define _GNU_SOURCE
- #include <sys/mman.h>
+ #include <stdio.h>
+ #include <linux/fs.h>
- int memfd_create(const char *name, unsigned int flags);
+ int renameat2(int olddirfd, const char *oldpath,
+ int newdirfd, const char *newpath, unsigned int flags);
int main()
{
- memfd_create("test", 0);
+ renameat2(0, "test", 0, "test_", RENAME_EXCHANGE);
return 0;
}
''')
self.assertEqual(call_symbol_check(cc, source, executable, []),
- (1, executable + ': symbol memfd_create from unsupported version GLIBC_2.27\n' +
+ (1, executable + ': symbol renameat2 from unsupported version GLIBC_2.28\n' +
executable + ': failed IMPORTED_SYMBOLS'))
# -lutil is part of the libc6 package so a safe bet that it's installed
diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py
index 06b15574a7..60acb0d593 100755
--- a/contrib/gitian-build.py
+++ b/contrib/gitian-build.py
@@ -35,14 +35,14 @@ def setup():
if not os.path.isdir('bitcoin'):
subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin/bitcoin.git'])
os.chdir('gitian-builder')
- make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64']
+ make_image_prog = ['bin/make-base-vm', '--suite', 'focal', '--arch', 'amd64']
if args.docker:
make_image_prog += ['--docker']
elif not args.kvm:
- make_image_prog += ['--lxc']
+ make_image_prog += ['--lxc', '--disksize', '13000']
subprocess.check_call(make_image_prog)
os.chdir(workdir)
- if args.is_bionic and not args.kvm and not args.docker:
+ if args.is_focal and not args.kvm and not args.docker:
subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net'])
print('Reboot is required')
sys.exit(0)
@@ -176,7 +176,7 @@ def main():
args = parser.parse_args()
workdir = os.getcwd()
- args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs'])
+ args.is_focal = b'focal' in subprocess.check_output(['lsb_release', '-cs'])
if args.kvm and args.docker:
raise Exception('Error: cannot have both kvm and docker')
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index a0ff87b531..52e2a0514a 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -3,7 +3,7 @@ name: "bitcoin-core-linux-22"
enable_cache: true
distro: "ubuntu"
suites:
-- "bionic"
+- "focal"
architectures:
- "amd64"
packages:
@@ -11,10 +11,13 @@ packages:
- "autoconf"
- "automake"
- "binutils"
+- "bison"
- "bsdmainutils"
- "ca-certificates"
- "curl"
- "faketime"
+- "g++-8"
+- "gcc-8"
- "git"
- "libtool"
- "patch"
@@ -45,7 +48,7 @@ script: |
WRAP_DIR=$HOME/wrapped
HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu powerpc64-linux-gnu powerpc64le-linux-gnu riscv64-linux-gnu"
- CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests"
+ CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary"
FAKETIME_HOST_PROGS="gcc g++"
FAKETIME_PROGS="date ar ranlib nm"
HOST_CFLAGS="-O2 -g"
@@ -107,7 +110,7 @@ script: |
BASEPREFIX="${PWD}/depends"
# Build dependencies for each host
for i in $HOSTS; do
- make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}"
+ make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" CC=${i}-gcc-8 CXX=${i}-g++-8
done
# Faketime for binaries
@@ -130,7 +133,7 @@ script: |
# Extract the git archive into a dir for each host and build
for i in ${HOSTS}; do
export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH}
- if [ "${i}" = "riscv64-linux-gnu" ] || [ "${i}" = "powerpc64-linux-gnu" ] || [ "${i}" = "powerpc64le-linux-gnu" ]; then
+ if [ "${i}" = "powerpc64-linux-gnu" ]; then
# Workaround for https://bugs.launchpad.net/ubuntu/+source/gcc-8-cross-ports/+bug/1853740
# TODO: remove this when no longer needed
HOST_LDFLAGS="${HOST_LDFLAGS_BASE} -Wl,-z,noexecstack"
@@ -144,7 +147,7 @@ script: |
tar --strip-components=1 -xf "${GIT_ARCHIVE}"
./autogen.sh
- CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}"
+ CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}" CC=${i}-gcc-8 CXX=${i}-g++-8
make ${MAKEOPTS}
make ${MAKEOPTS} -C src check-security
make ${MAKEOPTS} -C src check-symbols
diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml
index 6fcb21f729..3f0c0c3332 100644
--- a/contrib/gitian-descriptors/gitian-osx-signer.yml
+++ b/contrib/gitian-descriptors/gitian-osx-signer.yml
@@ -2,7 +2,7 @@
name: "bitcoin-dmg-signer"
distro: "ubuntu"
suites:
-- "bionic"
+- "focal"
architectures:
- "amd64"
packages:
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index 2a47e90e6e..c37ad5b842 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -3,7 +3,7 @@ name: "bitcoin-core-osx-22"
enable_cache: true
distro: "ubuntu"
suites:
-- "bionic"
+- "focal"
architectures:
- "amd64"
packages:
@@ -21,14 +21,12 @@ packages:
- "bsdmainutils"
- "cmake"
- "imagemagick"
-- "libcap-dev"
- "libz-dev"
-- "libbz2-dev"
- "python3"
-- "python3-dev"
- "python3-setuptools"
- "fonts-tuffy"
- "xorriso"
+- "libtinfo5"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
@@ -39,7 +37,7 @@ script: |
WRAP_DIR=$HOME/wrapped
HOSTS="x86_64-apple-darwin18"
- CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests XORRISOFS=${WRAP_DIR}/xorrisofs DMG=${WRAP_DIR}/dmg"
+ CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary XORRISOFS=${WRAP_DIR}/xorrisofs DMG=${WRAP_DIR}/dmg"
FAKETIME_HOST_PROGS=""
FAKETIME_PROGS="ar ranlib date dmg xorrisofs"
diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml
index 6bcd126662..c13c24c3cc 100644
--- a/contrib/gitian-descriptors/gitian-win-signer.yml
+++ b/contrib/gitian-descriptors/gitian-win-signer.yml
@@ -2,7 +2,7 @@
name: "bitcoin-win-signer"
distro: "ubuntu"
suites:
-- "bionic"
+- "focal"
architectures:
- "amd64"
packages:
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 1edd8b2e81..95cf0185e2 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -3,7 +3,7 @@ name: "bitcoin-core-win-22"
enable_cache: true
distro: "ubuntu"
suites:
-- "bionic"
+- "focal"
architectures:
- "amd64"
packages:
@@ -31,7 +31,7 @@ script: |
WRAP_DIR=$HOME/wrapped
HOSTS="x86_64-w64-mingw32"
- CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
+ CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary"
FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip"
HOST_CFLAGS="-O2 -g -fno-ident"
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
index 5870deee39..1122ec9ba5 100644
--- a/contrib/guix/README.md
+++ b/contrib/guix/README.md
@@ -13,11 +13,9 @@ We achieve bootstrappability by using Guix as a functional package manager.
Conservatively, a x86_64 machine with:
-- 4GB of free disk space on the partition that /gnu/store will reside in
-- 24GB of free disk space on the partition that the Bitcoin Core git repository
- resides in
-
-> Note: these requirements are slightly less onerous than those of Gitian builds
+- 16GB of free disk space on the partition that /gnu/store will reside in
+- 8GB of free disk space per platform triple you're planning on building (see
+ the `HOSTS` environment variable description)
## Setup
@@ -105,8 +103,8 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
Override the space-separated list of platform triples for which to perform a
bootstrappable build. _(defaults to "x86\_64-linux-gnu arm-linux-gnueabihf
- aarch64-linux-gnu riscv64-linux-gnu x86_64-w64-mingw32
- x86_64-apple-darwin18")_
+ aarch64-linux-gnu riscv64-linux-gnu powerpc64-linux-gnu powerpc64le-linux-gnu
+ x86\_64-w64-mingw32 x86\_64-apple-darwin18")_
* _**SOURCES_PATH**_
@@ -114,6 +112,19 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
depends tree. Setting this to the same directory across multiple builds of the
depends tree can eliminate unnecessary redownloading of package sources.
+* _**BASE_CACHE**_
+
+ Set the depends tree cache for built packages. This is passed through to the
+ depends tree. Setting this to the same directory across multiple builds of the
+ depends tree can eliminate unnecessary building of packages.
+
+* _**SDK_PATH**_
+
+ Set the path where _extracted_ SDKs can be found. This is passed through to
+ the depends tree. Note that this is should be set to the _parent_ directory of
+ the actual SDK (e.g. SDK_PATH=$HOME/Downloads/macOS-SDKs instead of
+ $HOME/Downloads/macOS-SDKs/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers).
+
* _**MAX_JOBS**_
Override the maximum number of jobs to run simultaneously, you might want to
@@ -217,11 +228,11 @@ As mentioned at the bottom of [this manual page][guix/bin-install]:
>
> make guix-binary.x86_64-linux.tar.xz
-### When will Guix be packaged in debian?
+### Is Guix packaged in my operating system?
-Thanks to Vagrant Cascadian's diligent work, Guix is now [in debian
-experimental][debian/guix-experimental]! Hopefully it will make its way into a
-release soon.
+Guix is shipped starting with [Debian Bullseye][debian/guix-bullseye] and
+[Ubuntu 21.04 "Hirsute Hippo"][ubuntu/guix-hirsute]. Other operating systems
+are working on packaging Guix as well.
[b17e]: http://bootstrappable.org/
[r12e/source-date-epoch]: https://reproducible-builds.org/docs/source-date-epoch/
@@ -233,5 +244,6 @@ release soon.
[guix/substitute-server-auth]: https://www.gnu.org/software/guix/manual/en/html_node/Substitute-Server-Authorization.html
[guix/time-machine]: https://guix.gnu.org/manual/en/html_node/Invoking-guix-time_002dmachine.html
-[debian/guix-experimental]: https://packages.debian.org/experimental/guix
+[debian/guix-bullseye]: https://packages.debian.org/bullseye/guix
+[ubuntu/guix-hirsute]: https://packages.ubuntu.com/hirsute/guix
[fanquake/guix-docker]: https://github.com/fanquake/core-review/tree/master/guix
diff --git a/contrib/guix/guix-build.sh b/contrib/guix/guix-build.sh
index da6bd13f6a..7c32fb5726 100755
--- a/contrib/guix/guix-build.sh
+++ b/contrib/guix/guix-build.sh
@@ -9,7 +9,7 @@ set -e -o pipefail
################
# Check 1: Make sure that we can invoke required tools
################
-for cmd in git make guix cat mkdir; do
+for cmd in git make guix cat mkdir curl; do
if ! command -v "$cmd" > /dev/null 2>&1; then
echo "ERR: This script requires that '$cmd' is installed and available in your \$PATH"
exit 1
@@ -69,7 +69,7 @@ fi
################
# Default to building for all supported HOSTs (overridable by environment)
-export HOSTS="${HOSTS:-x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu
+export HOSTS="${HOSTS:-x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu powerpc64-linux-gnu powerpc64le-linux-gnu
x86_64-w64-mingw32
x86_64-apple-darwin18}"
@@ -136,9 +136,24 @@ done
# environment)
MAX_JOBS="${MAX_JOBS:-$(nproc)}"
+# Usage: host_to_commonname HOST
+#
+# HOST: The current platform triple we're building for
+#
+host_to_commonname() {
+ case "$1" in
+ *darwin*) echo osx ;;
+ *mingw*) echo win ;;
+ *linux*) echo linux ;;
+ *) exit 1 ;;
+ esac
+}
+
# Download the depends sources now as we won't have internet access in the build
# container
-make -C "${PWD}/depends" -j"$MAX_JOBS" download ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
+for host in $HOSTS; do
+ make -C "${PWD}/depends" -j"$MAX_JOBS" download-"$(host_to_commonname "$host")" ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
+done
# Determine the reference time used for determinism (overridable by environment)
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log --format=%at -1)}"
@@ -148,8 +163,9 @@ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log --format=%at -1)}"
time-machine() {
# shellcheck disable=SC2086
guix time-machine --url=https://github.com/dongcarl/guix.git \
- --commit=b066c25026f21fb57677aa34692a5034338e7ee3 \
+ --commit=490e39ff303f4f6873a04bfb8253755bdae1b29c \
--max-jobs="$MAX_JOBS" \
+ --keep-failed \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} \
-- "$@"
@@ -180,6 +196,12 @@ and untracked files and directories will be wiped, allowing you to start anew.
EOF
}
+# Create SOURCES_PATH, BASE_CACHE, and SDK_PATH if they are non-empty so that we
+# can map them into the container
+[ -z "$SOURCES_PATH" ] || mkdir -p "$SOURCES_PATH"
+[ -z "$BASE_CACHE" ] || mkdir -p "$BASE_CACHE"
+[ -z "$SDK_PATH" ] || mkdir -p "$SDK_PATH"
+
# Deterministically build Bitcoin Core
# shellcheck disable=SC2153
for host in $HOSTS; do
@@ -254,6 +276,12 @@ EOF
# make the downloaded depends sources available to it. The sources
# should have been downloaded prior to this invocation.
#
+ # --keep-failed keep build tree of failed builds
+ #
+ # When builds of the Guix environment itself (not Bitcoin Core)
+ # fail, it is useful for the build tree to be kept for debugging
+ # purposes.
+ #
# ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"}
#
# fetch substitute from SUBSTITUTE_URLS if they are
@@ -274,7 +302,10 @@ EOF
--share="$OUTDIR"=/outdir \
--expose="$(git rev-parse --git-common-dir)" \
${SOURCES_PATH:+--share="$SOURCES_PATH"} \
+ ${BASE_CACHE:+--share="$BASE_CACHE"} \
+ ${SDK_PATH:+--share="$SDK_PATH"} \
--max-jobs="$MAX_JOBS" \
+ --keep-failed \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
-- env HOST="$host" \
@@ -282,6 +313,8 @@ EOF
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \
${V:+V=1} \
${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
+ ${BASE_CACHE:+BASE_CACHE="$BASE_CACHE"} \
+ ${SDK_PATH:+SDK_PATH="$SDK_PATH"} \
DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \
OUTDIR=/outdir \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 39d3cb9b50..051066a6a0 100644
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -3,6 +3,16 @@ export LC_ALL=C
set -e -o pipefail
export TZ=UTC
+# Although Guix _does_ set umask when building its own packages (in our case,
+# this is all packages in manifest.scm), it does not set it for `guix
+# environment`. It does make sense for at least `guix environment --container`
+# to set umask, so if that change gets merged upstream and we bump the
+# time-machine to a commit which includes the aforementioned change, we can
+# remove this line.
+#
+# This line should be placed before any commands which creates files.
+umask 0022
+
if [ -n "$V" ]; then
# Print both unexpanded (-v) and expanded (-x) forms of commands as they are
# read from this file.
@@ -44,19 +54,14 @@ store_path() {
NATIVE_GCC="$(store_path gcc-toolchain)"
export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64"
export CPATH="${NATIVE_GCC}/include"
+unset C_INCLUDE_PATH
+unset CPLUS_INCLUDE_PATH
case "$HOST" in
*darwin*)
- # When targeting darwin, some native tools built by depends require
- # native packages not incorporated in depends
- #
- # libcap required by native_cdrkit/wodim
- # zlib, bzip2 required by native_cdrkit/genisoimage
- for native_pkg in libcap zlib bzip2; do
- native_pkg_store_path=$(store_path "$native_pkg")
- export LIBRARY_PATH="${native_pkg_store_path}/lib:${LIBRARY_PATH}"
- export CPATH="${native_pkg_store_path}/include:${CPATH}"
- done
- ;;
+ # When targeting darwin, zlib is required by native_libdmg-hfsplus.
+ zlib_store_path=$(store_path "zlib")
+ export LIBRARY_PATH="${zlib_store_path}/lib:${LIBRARY_PATH}"
+ export CPATH="${zlib_store_path}/include:${CPATH}"
esac
# Set environment variables to point the CROSS toolchain to the right
@@ -66,7 +71,8 @@ case "$HOST" in
# Determine output paths to use in CROSS_* environment variables
CROSS_GLIBC="$(store_path "mingw-w64-x86_64-winpthreads")"
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_STORE="$(store_path "gcc-cross-${HOST}" lib)"
+ CROSS_GCC_LIBS=( "${CROSS_GCC_LIB_STORE}/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)
# The search path ordering is generally:
@@ -75,7 +81,7 @@ case "$HOST" in
# 2. kernel-header-related search paths (not applicable to mingw-w64 hosts)
export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GCC_LIB}/include-fixed:${CROSS_GLIBC}/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"
+ export CROSS_LIBRARY_PATH="${CROSS_GCC_LIB_STORE}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib"
;;
*darwin*)
# The CROSS toolchain for darwin uses the SDK and ignores environment variables.
@@ -86,12 +92,13 @@ case "$HOST" in
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_STORE="$(store_path "gcc-cross-${HOST}" lib)"
+ CROSS_GCC_LIBS=( "${CROSS_GCC_LIB_STORE}/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)
export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GCC_LIB}/include-fixed:${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"
+ export CROSS_LIBRARY_PATH="${CROSS_GCC_LIB_STORE}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib"
;;
*)
exit 1 ;;
@@ -132,12 +139,14 @@ case "$HOST" in
*linux*)
glibc_dynamic_linker=$(
case "$HOST" in
- i686-linux-gnu) echo /lib/ld-linux.so.2 ;;
- x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;;
- arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;;
- aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;;
- riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;;
- *) exit 1 ;;
+ i686-linux-gnu) echo /lib/ld-linux.so.2 ;;
+ x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;;
+ arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;;
+ aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;;
+ riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;;
+ powerpc64-linux-gnu) echo /lib/ld64.so.1;;
+ powerpc64le-linux-gnu) echo /lib/ld64.so.2;;
+ *) exit 1 ;;
esac
)
;;
@@ -167,6 +176,8 @@ esac
make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
${V:+V=1} \
${SOURCES_PATH+SOURCES_PATH="$SOURCES_PATH"} \
+ ${BASE_CACHE+BASE_CACHE="$BASE_CACHE"} \
+ ${SDK_PATH+SDK_PATH="$SDK_PATH"} \
i686_linux_CC=i686-linux-gnu-gcc \
i686_linux_CXX=i686-linux-gnu-g++ \
i686_linux_AR=i686-linux-gnu-ar \
@@ -204,7 +215,7 @@ fi
###########################
# CONFIGFLAGS
-CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
+CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary"
case "$HOST" in
*linux*) CONFIGFLAGS+=" --enable-glibc-back-compat" ;;
esac
@@ -226,6 +237,10 @@ case "$HOST" in
*mingw*) HOST_LDFLAGS="-Wl,--no-insert-timestamp" ;;
esac
+case "$HOST" in
+ powerpc64-linux-*) HOST_LDFLAGS="${HOST_LDFLAGS} -Wl,-z,noexecstack" ;;
+esac
+
# Make $HOST-specific native binaries from depends available in $PATH
export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
mkdir -p "$DISTSRC"
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 3b89659263..4228532cb1 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -3,6 +3,7 @@
(gnu packages autotools)
(gnu packages base)
(gnu packages bash)
+ (gnu packages bison)
(gnu packages cdrom)
(gnu packages check)
(gnu packages cmake)
@@ -115,7 +116,8 @@ http://www.linuxfromscratch.org/hlfs/view/development/chapter05/gcc-pass1.html"
`(("binutils" ,xbinutils)
("libc" ,xlibc)
("libc:static" ,xlibc "static")
- ("gcc" ,xgcc)))
+ ("gcc" ,xgcc)
+ ("gcc-lib" ,xgcc "lib")))
(synopsis (string-append "Complete GCC tool chain for " target))
(description (string-append "This package provides a complete GCC tool
chain for " target " development."))
@@ -124,9 +126,9 @@ chain for " target " development."))
(define* (make-bitcoin-cross-toolchain target
#:key
- (base-gcc-for-libc gcc-5)
- (base-kernel-headers linux-libre-headers-4.19)
- (base-libc glibc-2.27)
+ (base-gcc-for-libc gcc-7)
+ (base-kernel-headers linux-libre-headers-5.4)
+ (base-libc glibc) ; glibc 2.31
(base-gcc (make-gcc-rpath-link gcc-9)))
"Convenience wrapper around MAKE-CROSS-TOOLCHAIN with default values
desirable for building Bitcoin Core release binaries."
@@ -159,7 +161,8 @@ desirable for building Bitcoin Core release binaries."
(propagated-inputs
`(("binutils" ,xbinutils)
("libc" ,pthreads-xlibc)
- ("gcc" ,pthreads-xgcc)))
+ ("gcc" ,pthreads-xgcc)
+ ("gcc-lib" ,pthreads-xgcc "lib")))
(synopsis (string-append "Complete GCC tool chain for " target))
(description (string-append "This package provides a complete GCC tool
chain for " target " development."))
@@ -217,9 +220,10 @@ chain for " target " development."))
autoconf
automake
pkg-config
+ bison
;; Scripting
perl
- python-3.7
+ python-3
;; Git
git
;; Native gcc 7 toolchain
@@ -230,11 +234,8 @@ chain for " target " development."))
(list zip
(make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32")
(make-nsis-with-sde-support nsis-x86_64)))
- ((string-contains target "riscv64-linux-")
- (list (make-bitcoin-cross-toolchain "riscv64-linux-gnu"
- #:base-gcc-for-libc gcc-7)))
((string-contains target "-linux-")
(list (make-bitcoin-cross-toolchain target)))
((string-contains target "darwin")
- (list clang-8 libcap binutils imagemagick libtiff librsvg font-tuffy cmake-3.15.5 xorriso))
+ (list clang-8 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso))
(else '())))))
diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc
index 86222295db..013a1a6070 100644
--- a/contrib/init/bitcoind.openrc
+++ b/contrib/init/bitcoind.openrc
@@ -60,16 +60,17 @@ start_pre() {
"${BITCOIND_PIDDIR}"
checkpath -f \
- -o ${BITCOIND_USER}:${BITCOIND_GROUP} \
+ -o "${BITCOIND_USER}:${BITCOIND_GROUP}" \
-m 0660 \
- ${BITCOIND_CONFIGFILE}
+ "${BITCOIND_CONFIGFILE}"
checkconfig || return 1
}
checkconfig()
{
- if ! grep -qs '^rpcpassword=' "${BITCOIND_CONFIGFILE}" ; then
+ if grep -qs '^rpcuser=' "${BITCOIND_CONFIGFILE}" && \
+ ! grep -qs '^rpcpassword=' "${BITCOIND_CONFIGFILE}" ; then
eerror ""
eerror "ERROR: You must set a secure rpcpassword to run bitcoind."
eerror "The setting must appear in ${BITCOIND_CONFIGFILE}"
diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md
index eaca473b40..fcc5a378e2 100644
--- a/contrib/testgen/README.md
+++ b/contrib/testgen/README.md
@@ -4,5 +4,5 @@ Utilities to generate test vectors for the data-driven Bitcoin tests.
Usage:
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py
index 8a3918da6b..74918cfb04 100755
--- a/contrib/testgen/gen_key_io_test_vectors.py
+++ b/contrib/testgen/gen_key_io_test_vectors.py
@@ -3,11 +3,11 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
-Generate valid and invalid base58 address and private key test vectors.
+Generate valid and invalid base58/bech32(m) address and private key test vectors.
Usage:
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
'''
# 2012 Wladimir J. van der Laan
# Released under MIT License
@@ -15,7 +15,7 @@ import os
from itertools import islice
from base58 import b58encode_chk, b58decode_chk, b58chars
import random
-from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET
+from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET, Encoding
# key types
PUBKEY_ADDRESS = 0
@@ -32,6 +32,7 @@ PRIVKEY_REGTEST = 239
OP_0 = 0x00
OP_1 = 0x51
OP_2 = 0x52
+OP_3 = 0x53
OP_16 = 0x60
OP_DUP = 0x76
OP_EQUAL = 0x87
@@ -44,6 +45,7 @@ script_prefix = (OP_HASH160, 20)
script_suffix = (OP_EQUAL,)
p2wpkh_prefix = (OP_0, 20)
p2wsh_prefix = (OP_0, 32)
+p2tr_prefix = (OP_1, 32)
metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip']
# templates for valid sequences
@@ -54,40 +56,58 @@ templates = [
((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix),
((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'test', None, None), pubkey_prefix, pubkey_suffix),
((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'test', None, None), script_prefix, script_suffix),
+ ((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), pubkey_prefix, pubkey_suffix),
+ ((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), script_prefix, script_suffix),
((PUBKEY_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), pubkey_prefix, pubkey_suffix),
((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix),
((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()),
((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()),
((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()),
((PRIVKEY_TEST,), 32, (1,), (True, 'test', True, None), (), ()),
+ ((PRIVKEY_TEST,), 32, (), (True, 'signet', False, None), (), ()),
+ ((PRIVKEY_TEST,), 32, (1,), (True, 'signet', True, None), (), ()),
((PRIVKEY_REGTEST,), 32, (), (True, 'regtest', False, None), (), ()),
((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ())
]
# templates for valid bech32 sequences
bech32_templates = [
- # hrp, version, witprog_size, metadata, output_prefix
- ('bc', 0, 20, (False, 'main', None, True), p2wpkh_prefix),
- ('bc', 0, 32, (False, 'main', None, True), p2wsh_prefix),
- ('bc', 1, 2, (False, 'main', None, True), (OP_1, 2)),
- ('tb', 0, 20, (False, 'test', None, True), p2wpkh_prefix),
- ('tb', 0, 32, (False, 'test', None, True), p2wsh_prefix),
- ('tb', 2, 16, (False, 'test', None, True), (OP_2, 16)),
- ('bcrt', 0, 20, (False, 'regtest', None, True), p2wpkh_prefix),
- ('bcrt', 0, 32, (False, 'regtest', None, True), p2wsh_prefix),
- ('bcrt', 16, 40, (False, 'regtest', None, True), (OP_16, 40))
+ # hrp, version, witprog_size, metadata, encoding, output_prefix
+ ('bc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix),
+ ('bc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix),
+ ('bc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix),
+ ('bc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)),
+ ('tb', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix),
+ ('tb', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix),
+ ('tb', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix),
+ ('tb', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)),
+ ('tb', 0, 20, (False, 'signet', None, True), Encoding.BECH32, p2wpkh_prefix),
+ ('tb', 0, 32, (False, 'signet', None, True), Encoding.BECH32, p2wsh_prefix),
+ ('tb', 1, 32, (False, 'signet', None, True), Encoding.BECH32M, p2tr_prefix),
+ ('tb', 3, 32, (False, 'signet', None, True), Encoding.BECH32M, (OP_3, 32)),
+ ('bcrt', 0, 20, (False, 'regtest', None, True), Encoding.BECH32, p2wpkh_prefix),
+ ('bcrt', 0, 32, (False, 'regtest', None, True), Encoding.BECH32, p2wsh_prefix),
+ ('bcrt', 1, 32, (False, 'regtest', None, True), Encoding.BECH32M, p2tr_prefix),
+ ('bcrt', 16, 40, (False, 'regtest', None, True), Encoding.BECH32M, (OP_16, 40))
]
# templates for invalid bech32 sequences
bech32_ng_templates = [
- # hrp, version, witprog_size, invalid_bech32, invalid_checksum, invalid_char
- ('tc', 0, 20, False, False, False),
- ('tb', 17, 32, False, False, False),
- ('bcrt', 3, 1, False, False, False),
- ('bc', 15, 41, False, False, False),
- ('tb', 0, 16, False, False, False),
- ('bcrt', 0, 32, True, False, False),
- ('bc', 0, 16, True, False, False),
- ('tb', 0, 32, False, True, False),
- ('bcrt', 0, 20, False, False, True)
+ # hrp, version, witprog_size, encoding, invalid_bech32, invalid_checksum, invalid_char
+ ('tc', 0, 20, Encoding.BECH32, False, False, False),
+ ('bt', 1, 32, Encoding.BECH32M, False, False, False),
+ ('tb', 17, 32, Encoding.BECH32M, False, False, False),
+ ('bcrt', 3, 1, Encoding.BECH32M, False, False, False),
+ ('bc', 15, 41, Encoding.BECH32M, False, False, False),
+ ('tb', 0, 16, Encoding.BECH32, False, False, False),
+ ('bcrt', 0, 32, Encoding.BECH32, True, False, False),
+ ('bc', 0, 16, Encoding.BECH32, True, False, False),
+ ('tb', 0, 32, Encoding.BECH32, False, True, False),
+ ('bcrt', 0, 20, Encoding.BECH32, False, False, True),
+ ('bc', 0, 20, Encoding.BECH32M, False, False, False),
+ ('tb', 0, 32, Encoding.BECH32M, False, False, False),
+ ('bcrt', 0, 20, Encoding.BECH32M, False, False, False),
+ ('bc', 1, 32, Encoding.BECH32, False, False, False),
+ ('tb', 2, 16, Encoding.BECH32, False, False, False),
+ ('bcrt', 16, 20, Encoding.BECH32, False, False, False),
]
def is_valid(v):
@@ -127,8 +147,9 @@ def gen_valid_bech32_vector(template):
hrp = template[0]
witver = template[1]
witprog = bytearray(os.urandom(template[2]))
- dst_prefix = bytearray(template[4])
- rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
+ encoding = template[4]
+ dst_prefix = bytearray(template[5])
+ rv = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
return rv, dst_prefix + witprog
def gen_valid_vectors():
@@ -186,22 +207,23 @@ def gen_invalid_bech32_vector(template):
hrp = template[0]
witver = template[1]
witprog = bytearray(os.urandom(template[2]))
+ encoding = template[3]
if no_data:
- rv = bech32_encode(hrp, [])
+ rv = bech32_encode(encoding, hrp, [])
else:
data = [witver] + convertbits(witprog, 8, 5)
- if template[3] and not no_data:
+ if template[4] and not no_data:
if template[2] % 5 in {2, 4}:
data[-1] |= 1
else:
data.append(0)
- rv = bech32_encode(hrp, data)
+ rv = bech32_encode(encoding, hrp, data)
- if template[4]:
+ if template[5]:
i = len(rv) - random.randrange(1, 7)
rv = rv[:i] + random.choice(CHARSET.replace(rv[i], '')) + rv[i + 1:]
- if template[5]:
+ if template[6]:
i = len(hrp) + 1 + random.randrange(0, len(rv) - len(hrp) - 4)
rv = rv[:i] + rv[i:i + 4].upper() + rv[i + 4:]
diff --git a/contrib/verifybinaries/verify.py b/contrib/verifybinaries/verify.py
index b97ad11b8e..6cbaf2dc0c 100755
--- a/contrib/verifybinaries/verify.py
+++ b/contrib/verifybinaries/verify.py
@@ -51,7 +51,7 @@ def download_with_wget(remote_file, local_file=None):
if local_file:
wget_args = ['wget', '-O', local_file, remote_file]
else:
- # use timestamping mechanism if local filename is not explicitely set
+ # use timestamping mechanism if local filename is not explicitly set
wget_args = ['wget', '-N', remote_file]
result = subprocess.run(wget_args,
@@ -85,7 +85,7 @@ def main(args):
print("Error: need to specify a version on the command line")
return 3
- # determine remote dir dependend on provided version string
+ # determine remote dir dependent on provided version string
version_base, version_rc, os_filter = parse_version_string(args[0])
remote_dir = f"/bin/{VERSIONPREFIX}{version_base}/"
if version_rc:
diff --git a/depends/Makefile b/depends/Makefile
index 596a46d4a2..4cd4d72fc2 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -2,7 +2,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
# When invoking a sub-make, keep only the command line variable definitions
# matching the pattern in the filter function.
@@ -112,19 +112,27 @@ include builders/$(build_os).mk
include builders/default.mk
include packages/packages.mk
+full_env=$(shell printenv)
+
build_id_string:=$(BUILD_ID_SALT)
-build_id_string+=$(shell $(build_CC) --version 2>/dev/null)
-build_id_string+=$(shell $(build_AR) --version 2>/dev/null)
-build_id_string+=$(shell $(build_CXX) --version 2>/dev/null)
-build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null)
-build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null)
+
+# GCC only prints COLLECT_LTO_WRAPPER when invoked with just "-v", but we want
+# the information from "-v -E -" as well, so just include both.
+#
+# '3>&1 1>&2 2>&3 > /dev/null' is supposed to swap stdin and stdout and silence
+# stdin, since we only want the stderr output
+build_id_string+=$(shell $(build_CC) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(build_CC) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+build_id_string+=$(shell $(build_AR) --version 2>/dev/null) $(filter AR_%,$(full_env)) ZERO_AR_DATE=$(ZERO_AR_DATE)
+build_id_string+=$(shell $(build_CXX) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(build_CXX) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null) $(filter RANLIB_%,$(full_env))
+build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null) $(filter STRIP_%,$(full_env))
$(host_arch)_$(host_os)_id_string:=$(HOST_ID_SALT)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(host_CC) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null) $(filter AR_%,$(full_env)) ZERO_AR_DATE=$(ZERO_AR_DATE)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(host_CXX) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(filter RANLIB_%,$(full_env))
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) $(filter STRIP_%,$(full_env))
ifneq ($(strip $(FORCE_USE_SYSTEM_CLANG)),)
# Make sure that cache is invalidated when switching between system and
@@ -133,6 +141,9 @@ build_id_string+=system_clang
$(host_arch)_$(host_os)_id_string+=system_clang
endif
+build_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
+$(host_arch)_$(host_os)_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
+
qrencode_packages_$(NO_QR) = $(qrencode_packages)
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_)
@@ -265,7 +276,7 @@ install: check-packages $(host_prefix)/share/config.site
download-one: check-sources $(all_sources)
download-osx:
- @$(MAKE) -s HOST=x86_64-apple-darwin14 download-one
+ @$(MAKE) -s HOST=x86_64-apple-darwin download-one
download-linux:
@$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one
download-win:
diff --git a/depends/README.md b/depends/README.md
index 9e9878c595..a1d4a99084 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -44,7 +44,7 @@ The paths are automatically configured and no other options are needed unless ta
#### For macOS cross compilation
- sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python3-setuptools libtinfo5 xorriso
+ sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libz-dev python3-setuptools libtinfo5 xorriso
#### For Win64 cross compilation
@@ -54,7 +54,7 @@ The paths are automatically configured and no other options are needed unless ta
Common linux dependencies:
- sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3 patch
+ sudo apt-get install make automake cmake curl g++-multilib libtool binutils-gold bsdmainutils pkg-config python3 patch bison
For linux ARM cross compilation:
diff --git a/depends/config.site.in b/depends/config.site.in
index 391767357a..f1a59a5861 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -54,6 +54,10 @@ if test -z "$with_gui" && test -n "@no_qt@"; then
with_gui=no
fi
+if test -n "@debug@" && test -z "@no_qt@" && test "x$with_gui" != xno; then
+ with_gui=qt5_debug
+fi
+
if test -z "$enable_zmq" && test -n "@no_zmq@"; then
enable_zmq=no
fi
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index ff8a252db9..29a3efdfe6 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -22,7 +22,7 @@ $(package)_toolset_$(host_os)=clang
else
$(package)_toolset_$(host_os)=gcc
endif
-$(package)_config_libraries=filesystem,system,thread,test
+$(package)_config_libraries=filesystem,system,test
$(package)_cxxflags=-std=c++17 -fvisibility=hidden
$(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_android=-fPIC
diff --git a/depends/packages/libnatpmp.mk b/depends/packages/libnatpmp.mk
index a24f201859..cdcf8c0bf2 100644
--- a/depends/packages/libnatpmp.mk
+++ b/depends/packages/libnatpmp.mk
@@ -1,11 +1,14 @@
package=libnatpmp
-$(package)_version=20150609
-$(package)_download_path=https://miniupnp.tuxfamily.org/files/
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=e1aa9c4c4219bc06943d6b2130f664daee213fb262fcb94dd355815b8f4536b0
+$(package)_version=4536032ae32268a45c073a4d5e91bbab4534773a
+$(package)_download_path=https://github.com/miniupnp/libnatpmp/archive
+$(package)_file_name=$($(package)_version).tar.gz
+$(package)_sha256_hash=543b460aab26acf91e11d15e17d8798f845304199eea2d76c2f444ec749c5383
define $(package)_set_vars
$(package)_build_opts=CC="$($(package)_cc)"
+ $(package)_build_opts_mingw32=CPPFLAGS=-DNATPMP_STATICLIB
+ $(package)_build_opts_darwin=LIBTOOL="$($(package)_libtool)"
+ $(package)_build_env+=CFLAGS="$($(package)_cflags) $($(package)_cppflags)" AR="$($(package)_ar)"
endef
define $(package)_build_cmds
diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk
index 2204b38195..710f43959c 100644
--- a/depends/packages/libxcb.mk
+++ b/depends/packages/libxcb.mk
@@ -15,25 +15,20 @@ $(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-shape --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-xinerama --disable-xinput
$(package)_config_opts += --disable-xprint --disable-selinux --disable-xtest
$(package)_config_opts += --disable-xv --disable-xvmc
endef
define $(package)_preprocess_cmds
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux &&\
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux && \
sed "s/pthread-stubs//" -i configure
endef
-# Don't install xcb headers to the default path in order to work around a qt
-# build issue: https://bugreports.qt.io/browse/QTBUG-34748
-# When using qt's internal libxcb, it may end up finding the real headers in
-# depends staging. Use a non-default path to avoid that.
-
define $(package)_config_cmds
- $($(package)_autoconf) --includedir=$(host_prefix)/include/xcb-shared
+ $($(package)_autoconf)
endef
define $(package)_build_cmds
@@ -45,5 +40,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- rm -rf share/man share/doc lib/*.la
+ rm -rf share lib/*.la
endef
diff --git a/depends/packages/libxkbcommon.mk b/depends/packages/libxkbcommon.mk
new file mode 100644
index 0000000000..8c6c56545f
--- /dev/null
+++ b/depends/packages/libxkbcommon.mk
@@ -0,0 +1,32 @@
+package=libxkbcommon
+$(package)_version=0.8.4
+$(package)_download_path=https://xkbcommon.org/download/
+$(package)_file_name=$(package)-$($(package)_version).tar.xz
+$(package)_sha256_hash=60ddcff932b7fd352752d51a5c4f04f3d0403230a584df9a2e0d5ed87c486c8b
+$(package)_dependencies=libxcb
+
+define $(package)_set_vars
+$(package)_config_opts = --enable-option-checking --disable-dependency-tracking
+$(package)_config_opts += --disable-static --disable-docs
+endef
+
+define $(package)_preprocess_cmds
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux
+endef
+
+define $(package)_config_cmds
+ $($(package)_autoconf)
+endef
+
+define $(package)_build_cmds
+ $(MAKE)
+endef
+
+define $(package)_stage_cmds
+ $(MAKE) DESTDIR=$($(package)_staging_dir) install
+endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
+
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 0f35ca0d2d..810068b686 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -1,10 +1,8 @@
packages:=boost libevent
-qt_packages = zlib
-
qrencode_packages = qrencode
-qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig
+qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon
qt_android_packages=qt
qt_darwin_packages=qt
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 7d122ea5f9..0ac6f496c4 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -1,23 +1,21 @@
PACKAGE=qt
-$(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)_version=5.12.10
+$(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules
+$(package)_suffix=everywhere-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
-$(package)_sha256_hash=9b9dec1f67df1f94bce2955c5604de992d529dde72050239154c56352da0907d
-$(package)_dependencies=zlib
-$(package)_linux_dependencies=freetype fontconfig libxcb
+$(package)_sha256_hash=8088f174e6d28e779516c083b6087b6a9e3c8322b4bc161fd1b54195e3c86940
+$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon
$(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
-$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
+$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
-$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch
-$(package)_patches+= fix_mingw_cross_compile.patch fix_qpainter_non_determinism.patch
+$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch
+$(package)_patches+= fix_qpainter_non_determinism.patch fix_lib_paths.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
-$(package)_qttranslations_sha256_hash=fb5a47799754af73d3bf501fe513342cfe2fc37f64e80df5533f6110e804220c
+$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de
$(package)_qttools_file_name=qttools-$($(package)_suffix)
-$(package)_qttools_sha256_hash=a97556eb7b2f30252cdd8a598c396cfce2b2f79d2bae883af6d3b26a2cdcc63c
+$(package)_qttools_sha256_hash=b0cfa6e7aac41b7c61fc59acc04843d7a98f9e1840370611751bcfc1834a636c
$(package)_extra_sources = $($(package)_qttranslations_file_name)
$(package)_extra_sources += $($(package)_qttools_file_name)
@@ -26,6 +24,7 @@ define $(package)_set_vars
$(package)_config_opts_release = -release
$(package)_config_opts_release += -silent
$(package)_config_opts_debug = -debug
+$(package)_config_opts_debug += -optimized-tools
$(package)_config_opts += -bindir $(build_prefix)/bin
$(package)_config_opts += -c++std c++1z
$(package)_config_opts += -confirm-license
@@ -49,7 +48,6 @@ $(package)_config_opts += -no-mtdev
$(package)_config_opts += -no-openssl
$(package)_config_opts += -no-openvg
$(package)_config_opts += -no-reduce-relocations
-$(package)_config_opts += -no-qml-debug
$(package)_config_opts += -no-sctp
$(package)_config_opts += -no-securetransport
$(package)_config_opts += -no-sql-db2
@@ -63,17 +61,15 @@ $(package)_config_opts += -no-sql-sqlite
$(package)_config_opts += -no-sql-sqlite2
$(package)_config_opts += -no-system-proxies
$(package)_config_opts += -no-use-gold-linker
-$(package)_config_opts += -no-xinput2
$(package)_config_opts += -nomake examples
$(package)_config_opts += -nomake tests
$(package)_config_opts += -opensource
-$(package)_config_opts += -optimized-tools
$(package)_config_opts += -pkg-config
$(package)_config_opts += -prefix $(host_prefix)
$(package)_config_opts += -qt-libpng
$(package)_config_opts += -qt-pcre
$(package)_config_opts += -qt-harfbuzz
-$(package)_config_opts += -system-zlib
+$(package)_config_opts += -qt-zlib
$(package)_config_opts += -static
$(package)_config_opts += -v
$(package)_config_opts += -no-feature-bearermanagement
@@ -94,10 +90,10 @@ $(package)_config_opts += -no-feature-printdialog
$(package)_config_opts += -no-feature-printer
$(package)_config_opts += -no-feature-printpreviewdialog
$(package)_config_opts += -no-feature-printpreviewwidget
-$(package)_config_opts += -no-feature-regularexpression
$(package)_config_opts += -no-feature-sessionmanager
$(package)_config_opts += -no-feature-socks5
$(package)_config_opts += -no-feature-sql
+$(package)_config_opts += -no-feature-sqlmodel
$(package)_config_opts += -no-feature-statemachine
$(package)_config_opts += -no-feature-syntaxhighlighter
$(package)_config_opts += -no-feature-textbrowser
@@ -115,6 +111,7 @@ $(package)_config_opts += -no-feature-xml
$(package)_config_opts_darwin = -no-dbus
$(package)_config_opts_darwin += -no-opengl
$(package)_config_opts_darwin += -pch
+$(package)_config_opts_darwin += -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=$(OSX_MIN_VERSION)
ifneq ($(build_os),darwin)
$(package)_config_opts_darwin += -xplatform macx-clang-linux
@@ -129,13 +126,13 @@ endif
# for macOS on Apple Silicon (ARM) see https://bugreports.qt.io/browse/QTBUG-85279
$(package)_config_opts_arm_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64
-$(package)_config_opts_linux = -qt-xkbcommon-x11
-$(package)_config_opts_linux += -qt-xcb
+$(package)_config_opts_linux = -qt-xcb
$(package)_config_opts_linux += -no-xcb-xlib
$(package)_config_opts_linux += -no-feature-xlib
$(package)_config_opts_linux += -system-freetype
$(package)_config_opts_linux += -fontconfig
$(package)_config_opts_linux += -no-opengl
+$(package)_config_opts_linux += -no-feature-vulkan
$(package)_config_opts_linux += -dbus-runtime
$(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++
$(package)_config_opts_i686_linux = -xplatform linux-g++-32
@@ -220,21 +217,16 @@ endef
# 8. Adjust a regex in toolchain.prf, to accommodate Guix's usage of
# CROSS_LIBRARY_PATH. See #15277.
define $(package)_preprocess_cmds
- patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \
patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \
patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_configure_mac.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_rcc_determinism.patch && \
- patch -p1 -i $($(package)_patch_dir)/xkb-default.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_qmake_conf.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_riscv64_arch.patch && \
patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \
- patch -p1 -i $($(package)_patch_dir)/fix_mingw_cross_compile.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_qpainter_non_determinism.patch &&\
+ patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \
+ patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\
@@ -247,8 +239,8 @@ define $(package)_preprocess_cmds
sed -i.old "s|QMAKE_CFLAGS += |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
sed -i.old "s|QMAKE_CXXFLAGS += |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
sed -i.old "0,/^QMAKE_LFLAGS_/s|^QMAKE_LFLAGS_|!host_build: QMAKE_LFLAGS = $($(package)_ldflags)\n&|" qtbase/mkspecs/win32-g++/qmake.conf && \
- sed -i.old "s|QMAKE_CC = clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \
- sed -i.old "s|QMAKE_CXX = clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \
+ sed -i.old "s|QMAKE_CC = \$$$$\$$$${CROSS_COMPILE}clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \
+ sed -i.old "s|QMAKE_CXX = \$$$$\$$$${CROSS_COMPILE}clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \
sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf
endef
@@ -258,8 +250,6 @@ define $(package)_config_cmds
export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \
cd qtbase && \
./configure $($(package)_config_opts) && \
- echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \
- echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \
cd .. && \
$(MAKE) -C qtbase sub-src-clean && \
qtbase/bin/qmake -o qttranslations/Makefile qttranslations/qttranslations.pro && \
diff --git a/depends/packages/zlib.mk b/depends/packages/zlib.mk
deleted file mode 100644
index acb02020a8..0000000000
--- a/depends/packages/zlib.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-package=zlib
-$(package)_version=1.2.11
-$(package)_download_path=https://www.zlib.net
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
-
-define $(package)_set_vars
-$(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"
-$(package)_config_opts_android+=CHOST=$(host)
-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
- env $($(package)_config_opts) ./configure --static --prefix=$(host_prefix)
-endef
-
-define $(package)_build_cmds
- $(MAKE) libz.a
-endef
-
-define $(package)_stage_cmds
- $(MAKE) DESTDIR=$($(package)_staging_dir) install
-endef
-
diff --git a/depends/patches/qt/drop_lrelease_dependency.patch b/depends/patches/qt/drop_lrelease_dependency.patch
index f6b2c9fc80..9b918af77c 100644
--- a/depends/patches/qt/drop_lrelease_dependency.patch
+++ b/depends/patches/qt/drop_lrelease_dependency.patch
@@ -14,7 +14,7 @@ diff --git a/qttranslations/translations/translations.pro b/qttranslations/trans
index 694544c..eff339d 100644
--- a/qttranslations/translations/translations.pro
+++ b/qttranslations/translations/translations.pro
-@@ -109,3 +109,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT}
+@@ -107,3 +107,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT}
silent:updateqm.commands = @echo lrelease ${QMAKE_FILE_IN} && $$updateqm.commands
-updateqm.depends = $$LRELEASE_EXE
updateqm.name = LRELEASE ${QMAKE_FILE_IN}
diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch
index 2f6ff00f40..f891da6ddf 100644
--- a/depends/patches/qt/fix_android_jni_static.patch
+++ b/depends/patches/qt/fix_android_jni_static.patch
@@ -1,6 +1,6 @@
--- old/qtbase/src/plugins/platforms/android/androidjnimain.cpp
+++ new/qtbase/src/plugins/platforms/android/androidjnimain.cpp
-@@ -890,6 +890,14 @@
+@@ -897,6 +897,14 @@
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}
diff --git a/depends/patches/qt/fix_android_qmake_conf.patch b/depends/patches/qt/fix_android_qmake_conf.patch
index 13bfff9776..3a8753fd1d 100644
--- a/depends/patches/qt/fix_android_qmake_conf.patch
+++ b/depends/patches/qt/fix_android_qmake_conf.patch
@@ -1,20 +1,10 @@
--- old/qtbase/mkspecs/android-clang/qmake.conf
+++ new/qtbase/mkspecs/android-clang/qmake.conf
-@@ -30,7 +30,7 @@
- QMAKE_CFLAGS += -target mips64el-none-linux-android
+@@ -47,7 +47,7 @@ ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so
+ ANDROID_USE_LLVM = true
- QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH
--QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a
-+QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -nostdlib++
- QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \
- -isystem $$NDK_ROOT/sysroot/usr/include/$$NDK_TOOLS_PREFIX \
- -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++/include \
-@@ -40,7 +40,7 @@
- ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH
-
- ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so
--ANDROID_CXX_STL_LIBS = -lc++
-+ANDROID_CXX_STL_LIBS = -lc++_shared
-
- QMAKE_ARM_CFLAGS_RELEASE = -Oz
- QMAKE_ARM_CFLAGS_RELEASE_WITH_DEBUGINFO = -g -Oz
+ exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \
+- ANDROID_CXX_STL_LIBS = -lc++
++ ANDROID_CXX_STL_LIBS = -lc++_shared
+ else: \
+ ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "")
diff --git a/depends/patches/qt/fix_configure_mac.patch b/depends/patches/qt/fix_configure_mac.patch
deleted file mode 100644
index 0d7dd647de..0000000000
--- a/depends/patches/qt/fix_configure_mac.patch
+++ /dev/null
@@ -1,50 +0,0 @@
---- old/qtbase/mkspecs/features/mac/sdk.prf 2018-02-08 10:24:48.000000000 -0800
-+++ new/qtbase/mkspecs/features/mac/sdk.prf 2018-03-23 10:38:56.000000000 -0700
-@@ -8,21 +8,21 @@
- defineReplace(xcodeSDKInfo) {
- info = $$1
- equals(info, "Path"): \
-- info = --show-sdk-path
-+ infoarg = --show-sdk-path
- equals(info, "PlatformPath"): \
-- info = --show-sdk-platform-path
-+ infoarg = --show-sdk-platform-path
- equals(info, "SDKVersion"): \
-- info = --show-sdk-version
-+ infoarg = --show-sdk-version
- sdk = $$2
- isEmpty(sdk): \
- sdk = $$QMAKE_MAC_SDK
-
- isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}) {
-- QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$info 2>/dev/null")
-+ QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$infoarg 2>/dev/null")
- # --show-sdk-platform-path won't work for Command Line Tools; this is fine
- # only used by the XCTest backend to testlib
-- isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(info, "--show-sdk-platform-path")): \
-- error("Could not resolve SDK $$info for \'$$sdk\'")
-+ isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(infoarg, "--show-sdk-platform-path")): \
-+ error("Could not resolve SDK $$info for \'$$sdk\' using $$infoarg")
- cache(QMAKE_MAC_SDK.$${sdk}.$${info}, set stash, QMAKE_MAC_SDK.$${sdk}.$${info})
- }
-
---- old/qtbase/configure 2018-02-08 10:24:48.000000000 -0800
-+++ new/qtbase/configure 2018-03-23 05:42:29.000000000 -0700
-@@ -232,8 +232,13 @@
-
- sdk=$(getSingleQMakeVariable "QMAKE_MAC_SDK" "$1")
- if [ -z "$sdk" ]; then echo "QMAKE_MAC_SDK must be set when building on Mac" >&2; exit 1; fi
-- sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null)
-- if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi
-+ sysroot=$(getSingleQMakeVariable "QMAKE_MAC_SDK_PATH" "$1")
-+
-+ echo "sysroot pre-configured as $sysroot";
-+ if [ -z "$sysroot" ]; then
-+ sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null)
-+ if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi
-+ fi
-
- case "$sdk" in
- macosx*)
-
-
diff --git a/depends/patches/qt/fix_lib_paths.patch b/depends/patches/qt/fix_lib_paths.patch
new file mode 100644
index 0000000000..d1a15373f4
--- /dev/null
+++ b/depends/patches/qt/fix_lib_paths.patch
@@ -0,0 +1,193 @@
+--- old/qtbase/mkspecs/common/mac.conf
++++ new/qtbase/mkspecs/common/mac.conf
+@@ -14,7 +14,6 @@
+
+ QMAKE_RESOURCE = /Developer/Tools/Rez
+ QMAKE_EXTENSION_SHLIB = dylib
+-QMAKE_EXTENSIONS_AUX_SHLIB = tbd
+ QMAKE_LIBDIR =
+
+ # sdk.prf will prefix the proper SDK sysroot
+
+--- old/qtbase/mkspecs/features/qmake_use.prf
++++ new/qtbase/mkspecs/features/qmake_use.prf
+@@ -22,6 +22,8 @@
+ !defined(QMAKE_LIBS_$$nu, var): \
+ error("Library '$$lower($$replace(nu, _, -))' is not defined.")
+
++ QMAKE_LIBDIR += $$eval(QMAKE_LIBDIR_$$nu)
++
+ debug: \
+ LIBS$${suffix} += $$eval(QMAKE_LIBS_$${nu}_DEBUG) $$eval(QMAKE_LIBS_$$nu)
+ else: \
+
+--- old/qtbase/mkspecs/features/qt_configure.prf
++++ new/qtbase/mkspecs/features/qt_configure.prf
+@@ -526,98 +526,23 @@
+ return($$sysrootified)
+ }
+
+-# libs-var, libs, in-paths, out-paths-var
++# libs-var, libs, in-paths
+ defineTest(qtConfResolveLibs) {
+- ret = true
+- paths = $$3
+- out =
+- copy = false
+- for (l, 2) {
+- $$copy {
+- copy = false
+- out += $$l
+- } else: equals(l, "-s") {
+- # em++ flag to link libraries from emscripten-ports; passed on literally.
+- copy = true
+- out += $$l
+- } else: contains(l, "^-L.*") {
+- lp = $$replace(l, "^-L", )
+- gcc: lp = $$qtGccSysrootifiedPath($$lp)
+- !exists($$lp/.) {
+- qtLog("Library path $$val_escape(lp) is invalid.")
+- ret = false
+- } else {
+- paths += $$lp
+- }
+- } else: contains(l, "^-l.*") {
+- lib = $$replace(l, "^-l", )
+- lcan =
+- integrity:contains(lib, "^.*\\.a") {
+- # INTEGRITY compiler searches for exact filename
+- # if -l argument has .a suffix
+- lcan += $${lib}
+- } else: contains(lib, "^:.*") {
+- # Use exact filename when -l:filename syntax is used.
+- lib ~= s/^://
+- lcan += $${lib}
+- } else: unix {
+- # Under UNIX, we look for actual shared libraries, in addition
+- # to static ones.
+- shexts = $$QMAKE_EXTENSION_SHLIB $$QMAKE_EXTENSIONS_AUX_SHLIB
+- for (ext, shexts) {
+- lcan += $${QMAKE_PREFIX_SHLIB}$${lib}.$${ext}
+- }
+- lcan += \
+- $${QMAKE_PREFIX_STATICLIB}$${lib}.$${QMAKE_EXTENSION_STATICLIB}
+- } else {
+- # Under Windows, we look only for static libraries, as even for DLLs
+- # one actually links against a static import library.
+- mingw {
+- lcan += \
+- # MinGW supports UNIX-style library naming in addition to
+- # the MSVC style.
+- lib$${lib}.dll.a lib$${lib}.a \
+- # Fun fact: prefix-less libraries are also supported.
+- $${lib}.dll.a $${lib}.a
+- }
+- lcan += $${lib}.lib
+- }
+- l = $$qtConfFindInPathList($$lcan, $$paths $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS)
+- isEmpty(l) {
+- qtLog("None of [$$val_escape(lcan)] found in [$$val_escape(paths)] and global paths.")
+- ret = false
+- } else {
+- out += $$l
+- }
+- } else {
+- out += $$l
+- }
+- }
+- $$1 = $$out
++ for (path, 3): \
++ pre_lflags += -L$$path
++ $$1 = $$pre_lflags $$2
+ export($$1)
+- !isEmpty(4) {
+- $$4 = $$paths
+- export($$4)
+- }
+- return($$ret)
+-}
+-
+-# source-var
+-defineTest(qtConfResolveAllLibs) {
+- ret = true
+- !qtConfResolveLibs($${1}.libs, $$eval($${1}.libs), , $${1}.libdirs): \
+- ret = false
+- for (b, $${1}.builds._KEYS_): \
+- !qtConfResolveLibs($${1}.builds.$${b}, $$eval($${1}.builds.$${b}), $$eval($${1}.libdirs), ): \
+- ret = false
+- return($$ret)
++ return(true)
+ }
+
+ # libs-var, in-paths, libs
+ defineTest(qtConfResolvePathLibs) {
+ ret = true
+- gcc: 2 = $$qtGccSysrootifiedPaths($$2)
+- for (libdir, 2) {
++ gcc: \
++ local_paths = $$qtGccSysrootifiedPaths($$2)
++ else: \
++ local_paths = $$2
++ for (libdir, local_paths) {
+ !exists($$libdir/.) {
+ qtLog("Library path $$val_escape(libdir) is invalid.")
+ ret = false
+@@ -667,8 +592,11 @@
+ # includes-var, in-paths, test-object-var
+ defineTest(qtConfResolvePathIncs) {
+ ret = true
+- gcc: 2 = $$qtGccSysrootifiedPaths($$2)
+- for (incdir, 2) {
++ gcc: \
++ local_paths = $$qtGccSysrootifiedPaths($$2)
++ else: \
++ local_paths = $$2
++ for (incdir, local_paths) {
+ !exists($$incdir/.) {
+ qtLog("Include path $$val_escape(incdir) is invalid.")
+ ret = false
+@@ -727,6 +655,7 @@
+ vars += $$eval(config.commandline.rev_assignments.$${iv})
+ defined(config.input.$${iv}, var) {
+ eval($${1}.builds.$${b} = $$eval(config.input.$${iv}))
++ export($${1}.builds.$${b})
+ $${1}.builds._KEYS_ *= $${b}
+ any = true
+ } else {
+@@ -741,11 +670,14 @@
+ export($${1}.builds._KEYS_)
+ # we also reset the generic libs, to avoid surprises.
+ $${1}.libs =
++ export($${1}.libs)
+ }
+
+ # direct libs. overwrites inline libs.
+- defined(config.input.$${input}.libs, var): \
++ defined(config.input.$${input}.libs, var) {
+ eval($${1}.libs = $$eval(config.input.$${input}.libs))
++ export($${1}.libs)
++ }
+
+ includes = $$eval(config.input.$${input}.incdir)
+
+@@ -754,6 +686,7 @@
+ !isEmpty(prefix) {
+ includes += $$prefix/include
+ $${1}.libs = -L$$prefix/lib $$eval($${1}.libs)
++ export($${1}.libs)
+ }
+
+ libdir = $$eval(config.input.$${input}.libdir)
+@@ -762,11 +695,9 @@
+ for (ld, libdir): \
+ libs += -L$$ld
+ $${1}.libs = $$libs $$eval($${1}.libs)
++ export($${1}.libs)
+ }
+
+- !qtConfResolveAllLibs($$1): \
+- return(false)
+-
+ !qtConfResolvePathIncs($${1}.includedir, $$includes, $$2): \
+ return(false)
+
diff --git a/depends/patches/qt/fix_mingw_cross_compile.patch b/depends/patches/qt/fix_mingw_cross_compile.patch
deleted file mode 100644
index 67f76f1d85..0000000000
--- a/depends/patches/qt/fix_mingw_cross_compile.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-commit 5a992a549adfe5a587bbcd6cd2b2cee47d236e27
-Author: fanquake <fanquake@gmail.com>
-Date: Fri Sep 4 08:13:44 2020 +0800
-
- Work around broken mingw cross-compilation
-
- See upstream issues:
- https://bugreports.qt.io/browse/QTBUG-63637
- https://bugreports.qt.io/browse/QTBUG-63659
- https://codereview.qt-project.org/q/8bebded9
-
- We should be able to drop this once we are building qt 5.10.1 or later.
-
- Added in #12971.
-
-diff --git a/qtbase/mkspecs/win32-g++/qmake.conf b/qtbase/mkspecs/win32-g++/qmake.conf
-index e071a0d1..ad229b10 100644
---- a/qtbase/mkspecs/win32-g++/qmake.conf
-+++ b/qtbase/mkspecs/win32-g++/qmake.conf
-@@ -87,3 +87,5 @@ QMAKE_NM = $${CROSS_COMPILE}nm -P
- include(../common/angle.conf)
-
- load(qt_config)
-+QMAKE_LINK_OBJECT_MAX = 10
-+QMAKE_LINK_OBJECT_SCRIPT = object_script
diff --git a/depends/patches/qt/fix_no_printer.patch b/depends/patches/qt/fix_no_printer.patch
index f868ca2577..1372356138 100644
--- a/depends/patches/qt/fix_no_printer.patch
+++ b/depends/patches/qt/fix_no_printer.patch
@@ -10,10 +10,10 @@
--- x/qtbase/src/plugins/plugins.pro
+++ y/qtbase/src/plugins/plugins.pro
-@@ -8,6 +8,3 @@ qtHaveModule(gui) {
- qtConfig(imageformatplugin): SUBDIRS *= imageformats
+@@ -9,6 +9,3 @@ qtHaveModule(gui) {
!android:qtConfig(library): SUBDIRS *= generic
}
+ qtHaveModule(widgets): SUBDIRS += styles
-
-!winrt:qtHaveModule(printsupport): \
- SUBDIRS += printsupport
diff --git a/depends/patches/qt/fix_powerpc_libpng.patch b/depends/patches/qt/fix_powerpc_libpng.patch
deleted file mode 100644
index d37b6c7776..0000000000
--- a/depends/patches/qt/fix_powerpc_libpng.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-commit 6f9feb773a43c5abfa3455da2e324180e789285b
-Author: fanquake <fanquake@gmail.com>
-Date: Tue Sep 15 21:44:31 2020 +0800
-
- Fix PowerPC build of libpng
-
- See https://bugreports.qt.io/browse/QTBUG-66388.
-
- Can be dropped when we are building qt 5.12.0 or later.
-
-diff --git a/qtbase/src/3rdparty/libpng/libpng.pro b/qtbase/src/3rdparty/libpng/libpng.pro
-index 577b61d8..a2f56669 100644
---- a/qtbase/src/3rdparty/libpng/libpng.pro
-+++ b/qtbase/src/3rdparty/libpng/libpng.pro
-@@ -10,7 +10,7 @@ MODULE_INCLUDEPATH = $$PWD
-
- load(qt_helper_lib)
-
--DEFINES += PNG_ARM_NEON_OPT=0
-+DEFINES += PNG_ARM_NEON_OPT=0 PNG_POWERPC_VSX_OPT=0
- SOURCES += \
- png.c \
- pngerror.c \
diff --git a/depends/patches/qt/fix_qpainter_non_determinism.patch b/depends/patches/qt/fix_qpainter_non_determinism.patch
index 3cfcc22f03..44c45187c5 100644
--- a/depends/patches/qt/fix_qpainter_non_determinism.patch
+++ b/depends/patches/qt/fix_qpainter_non_determinism.patch
@@ -22,7 +22,7 @@ diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/pa
index 92ab6e8375..f018009e0b 100644
--- a/qtbase/src/gui/painting/qpaintengine_raster.cpp
+++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp
-@@ -3971,22 +3971,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
+@@ -4128,22 +4128,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
const QSpan *clipEnd = clip->m_spans + clip->count;
while (available && spans < end ) {
@@ -51,7 +51,7 @@ index 92ab6e8375..f018009e0b 100644
int sx1 = spans->x;
int sx2 = sx1 + spans->len;
-@@ -4005,7 +4006,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
+@@ -4162,7 +4163,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
if (len) {
out->x = qMax(sx1, cx1);
out->len = qMin(sx2, cx2) - out->x;
diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch
index 8c722ffb46..a5de2b4b9e 100644
--- a/depends/patches/qt/fix_qt_pkgconfig.patch
+++ b/depends/patches/qt/fix_qt_pkgconfig.patch
@@ -1,17 +1,17 @@
--- old/qtbase/mkspecs/features/qt_module.prf
+++ new/qtbase/mkspecs/features/qt_module.prf
-@@ -264,7 +264,7 @@
+@@ -269,7 +269,7 @@ load(qt_installs)
load(qt_targets)
# this builds on top of qt_common
--!internal_module:!lib_bundle:if(unix|mingw) {
+-!internal_module:if(unix|mingw) {
+if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) {
CONFIG += create_pc
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
host_build: \
-@@ -274,9 +274,9 @@
- QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_HEADERS/raw]
- QMAKE_PKGCONFIG_CFLAGS = -I${includedir}/$$MODULE_INCNAME
+@@ -284,9 +284,9 @@ load(qt_targets)
+ QMAKE_PKGCONFIG_CFLAGS = -D$$MODULE_DEFINE -I${includedir}/$$MODULE_INCNAME
+ }
QMAKE_PKGCONFIG_NAME = $$replace(TARGET, ^Qt, "Qt$$QT_MAJOR_VERSION ")
- QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)
+ QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)$$qtPlatformTargetSuffix()
@@ -20,4 +20,4 @@
+ QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))$$qtPlatformTargetSuffix()
isEmpty(QMAKE_PKGCONFIG_DESCRIPTION): \
QMAKE_PKGCONFIG_DESCRIPTION = $$replace(TARGET, ^Qt, "Qt ") module
- pclib_replace.match = $$lib_replace.match
+ !isEmpty(lib_replace0.match) {
diff --git a/depends/patches/qt/fix_rcc_determinism.patch b/depends/patches/qt/fix_rcc_determinism.patch
deleted file mode 100644
index c1b07fe23a..0000000000
--- a/depends/patches/qt/fix_rcc_determinism.patch
+++ /dev/null
@@ -1,15 +0,0 @@
---- old/qtbase/src/tools/rcc/rcc.cpp
-+++ new/qtbase/src/tools/rcc/rcc.cpp
-@@ -207,7 +207,11 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
- if (lib.formatVersion() >= 2) {
- // last modified time stamp
- const QDateTime lastModified = m_fileInfo.lastModified();
-- lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0));
-+ quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
-+ static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
-+ if (sourceDate != 0)
-+ lastmod = sourceDate;
-+ lib.writeNumber8(lastmod);
- if (text || pass1)
- lib.writeChar('\n');
- }
diff --git a/depends/patches/qt/fix_riscv64_arch.patch b/depends/patches/qt/fix_riscv64_arch.patch
deleted file mode 100644
index e7f29f01f9..0000000000
--- a/depends/patches/qt/fix_riscv64_arch.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-diff --git a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h
-index 20bfd36..93729fa 100644
---- a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h
-+++ b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h
-@@ -65,7 +65,8 @@
- defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
- defined(__SH4__) || defined(__alpha__) || \
- defined(_MIPS_ARCH_MIPS32R2) || \
-- defined(__AARCH64EL__)
-+ defined(__AARCH64EL__) || defined(__aarch64__) || \
-+ defined(__riscv)
- #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
- #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
- #if defined(_WIN32)
diff --git a/depends/patches/qt/freetype_back_compat.patch b/depends/patches/qt/freetype_back_compat.patch
deleted file mode 100644
index 1ca55f1ce3..0000000000
--- a/depends/patches/qt/freetype_back_compat.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-commit 14bc77db61bf9d56f9b6c8b84aa02573605c19c6
-Author: fanquake <fanquake@gmail.com>
-Date: Tue Aug 18 15:15:08 2020 +0800
-
- Fix backwards compatibility with older Freetype versions at runtime
-
- A few years ago, libfreetype introduced FT_Get_Font_Format() as an alias
- for FT_Get_X11_Font_Format(), but FT_Get_X11_Font_Format() was kept for abi
- backwards-compatibility.
-
- Qt 5.9 introduced a call to FT_Get_Font_Format(). Replace it with FT_Get_X11_Font_Format()
- in order to remain compatible with older freetype, which is still used by e.g. Ubuntu Trusty.
-
- See #14348.
-
-diff --git a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
-index 3f543755..8ecc1c8c 100644
---- a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
-+++ b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
-@@ -898,7 +898,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
- }
- }
- #if defined(FT_FONT_FORMATS_H)
-- const char *fmt = FT_Get_Font_Format(face);
-+ const char *fmt = FT_Get_X11_Font_Format(face);
- if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
- FT_Bool no_stem_darkening = true;
- FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch
index fe82c2c73c..f4a6f09ee4 100644
--- a/depends/patches/qt/no-xlib.patch
+++ b/depends/patches/qt/no-xlib.patch
@@ -22,15 +22,15 @@ index 7c62c2e2b3..c05c6c0a07 100644
#include <xcb/xfixes.h>
#include <xcb/xcb_image.h>
-@@ -384,6 +386,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget)
- w->setCursor(c, isBitmapCursor);
+@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
+ xcb_flush(xcb_connection());
}
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
static int cursorIdForShape(int cshape)
{
int cursorId = 0;
-@@ -437,6 +440,7 @@ static int cursorIdForShape(int cshape)
+@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape)
}
return cursorId;
}
@@ -38,7 +38,7 @@ index 7c62c2e2b3..c05c6c0a07 100644
xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
{
-@@ -558,7 +562,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
+@@ -556,7 +560,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
{
xcb_connection_t *conn = xcb_connection();
@@ -48,22 +48,23 @@ index 7c62c2e2b3..c05c6c0a07 100644
xcb_cursor_t cursor = XCB_NONE;
// Try Xcursor first
-@@ -589,6 +595,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+@@ -585,7 +591,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+
// Non-standard X11 cursors are created from bitmaps
cursor = createNonStandardCursor(cshape);
-
+-
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
// Create a glpyh cursor if everything else failed
if (!cursor && cursorId) {
cursor = xcb_generate_id(conn);
-@@ -596,6 +603,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
+@@ -593,6 +599,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
cursorId, cursorId + 1,
0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0);
}
+#endif
if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
- const char *name = cursorNames[cshape];
+ const char *name = cursorNames[cshape].front();
--
2.22.0
diff --git a/depends/patches/qt/no_sdk_version_check.patch b/depends/patches/qt/no_sdk_version_check.patch
new file mode 100644
index 0000000000..b16635b572
--- /dev/null
+++ b/depends/patches/qt/no_sdk_version_check.patch
@@ -0,0 +1,20 @@
+commit f5eb142cd04be2bc4ca610ed3b5b7e8ce3520ee3
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Jan 5 16:08:49 2021 +0800
+
+ Don't invoke macOS SDK version checking
+
+ This tries to use xcrun which is not available when cross-compiling.
+
+diff --git a/qtbase/mkspecs/features/mac/default_post.prf b/qtbase/mkspecs/features/mac/default_post.prf
+index 92a9112bca6..447e186eb26 100644
+--- a/qtbase/mkspecs/features/mac/default_post.prf
++++ b/qtbase/mkspecs/features/mac/default_post.prf
+@@ -8,7 +8,6 @@ contains(TEMPLATE, .*app) {
+ !macx-xcode:if(isEmpty(BUILDS)|build_pass) {
+ # Detect changes to the platform SDK
+ QMAKE_EXTRA_VARIABLES += QMAKE_MAC_SDK QMAKE_MAC_SDK_VERSION QMAKE_XCODE_DEVELOPER_PATH
+- QMAKE_EXTRA_INCLUDES += $$shell_quote($$PWD/sdk.mk)
+ }
+
+ # Detect incompatible SDK versions
diff --git a/depends/patches/qt/xkb-default.patch b/depends/patches/qt/xkb-default.patch
deleted file mode 100644
index 165abf3e2e..0000000000
--- a/depends/patches/qt/xkb-default.patch
+++ /dev/null
@@ -1,26 +0,0 @@
---- old/qtbase/src/gui/configure.pri 2018-06-06 17:28:10.000000000 -0400
-+++ new/qtbase/src/gui/configure.pri 2018-08-17 18:43:01.589384567 -0400
-@@ -43,18 +43,11 @@
- }
-
- defineTest(qtConfTest_xkbConfigRoot) {
-- qtConfTest_getPkgConfigVariable($${1}): return(true)
--
-- for (dir, $$list("/usr/share/X11/xkb", "/usr/local/share/X11/xkb")) {
-- exists($$dir) {
-- $${1}.value = $$dir
-- export($${1}.value)
-- $${1}.cache += value
-- export($${1}.cache)
-- return(true)
-- }
-- }
-- return(false)
-+ $${1}.value = "/usr/share/X11/xkb"
-+ export($${1}.value)
-+ $${1}.cache += value
-+ export($${1}.cache)
-+ return(true)
- }
-
- defineTest(qtConfTest_qpaDefaultPlatform) {
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 2f79168212..21bf587eaf 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -2073,7 +2073,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED = HAVE_BOOST_PROCESS
+PREDEFINED = ENABLE_EXTERNAL_SIGNER
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index 6237734390..ea06952af4 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -111,12 +111,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
Returns various information about the TX mempool.
Only supports JSON as output format.
-* loaded : (boolean) if the mempool is fully loaded
-* size : (numeric) the number of transactions in the TX mempool
-* bytes : (numeric) size of the TX mempool in bytes
-* usage : (numeric) total TX mempool memory usage
-* maxmempool : (numeric) maximum memory usage for the mempool in bytes
-* mempoolminfee : (numeric) minimum feerate (BTC per KB) for tx to be accepted
+Refer to the `getmempoolinfo` RPC for documentation of the fields.
`GET /rest/mempool/contents.json`
diff --git a/doc/bips.md b/doc/bips.md
index 8c20533c9b..f72dbead9d 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -1,4 +1,4 @@
-BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
+BIPs that are implemented by Bitcoin Core (up-to-date up to **v22.0**):
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575))
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
@@ -15,6 +15,9 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)). As of **v0.13.0**, this is only available for `NODE_BLOOM` (BIP 111) peers.
* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)). Disabled by default since **v0.19.0**, can be enabled by the `-peerbloomfilters` option.
* [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)).
+* [`BIP 43`](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 43. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
+* [`BIP 44`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 44. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
+* [`BIP 49`](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 49. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting **v0.17.0**, whether to send reject messages can be configured with the `-enablebip61` option, and support is deprecated (disabled by default) as of **v0.18.0**. Support was removed in **v0.20.0** ([PR #15437](https://github.com/bitcoin/bitcoin/pull/15437)).
* [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124).
* [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)).
@@ -24,6 +27,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
Support can be optionally disabled at build time since **v0.18.0** ([PR 14451](https://github.com/bitcoin/bitcoin/pull/14451)),
and it is disabled by default at build time since **v0.19.0** ([PR #15584](https://github.com/bitcoin/bitcoin/pull/15584)).
It has been removed as of **v0.20.0** ([PR 17165](https://github.com/bitcoin/bitcoin/pull/17165)).
+* [`BIP 84`](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 84. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
* [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)).
* [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)).
* [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)).
@@ -46,3 +50,4 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
* [`BIP 325`](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki): Signet test network is supported as of **v0.21.0** ([PR 18267](https://github.com/bitcoin/bitcoin/pull/18267)).
* [`BIP 339`](https://github.com/bitcoin/bips/blob/master/bip-0339.mediawiki): Relay of transactions by wtxid is supported as of **v0.21.0** ([PR 18044](https://github.com/bitcoin/bitcoin/pull/18044)).
* [`BIP 340`](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) [`341`](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) [`342`](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki): Validation rules for Taproot (including Schnorr signatures and Tapscript leaves) are implemented as of **v0.21.0** ([PR 19953](https://github.com/bitcoin/bitcoin/pull/19953)), without mainnet activation.
+* [`BIP 350`](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki): Addresses for native v1+ segregated Witness outputs use Bech32m instead of Bech32 as of **v22.0** ([PR 20861](https://github.com/bitcoin/bitcoin/pull/20861)).
diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md
index f4a8edec75..9a312bc33c 100644
--- a/doc/bitcoin-conf.md
+++ b/doc/bitcoin-conf.md
@@ -27,7 +27,7 @@ Comments may appear in two ways:
### Network specific options
Network specific options can be:
-- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`;
+- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`), `[signet]` or `[regtest]`;
- prefixed with a chain name; e.g., `regtest.maxmempool=100`.
Network specific options take precedence over non-network specific options.
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 04ee43f81d..16c6da66d5 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -1,116 +1,303 @@
-# macOS Build Instructions and Notes
+# macOS Build Guide
+
+**Updated for MacOS [11.2](https://www.apple.com/macos/big-sur/)**
+
+This guide describes how to build bitcoind, command-line utilities, and GUI on macOS
+
+**Note:** The following is for Intel Macs only!
+
+## Dependencies
+
+The following dependencies are **required**:
+
+Library | Purpose | Description
+-----------------------------------------------------------|------------|----------------------
+[automake](https://formulae.brew.sh/formula/automake) | Build | Generate makefile
+[libtool](https://formulae.brew.sh/formula/libtool) | Build | Shared library support
+[pkg-config](https://formulae.brew.sh/formula/pkg-config) | Build | Configure compiler and linker flags
+[boost](https://formulae.brew.sh/formula/boost) | Utility | Library for threading, data structures, etc
+[libevent](https://formulae.brew.sh/formula/libevent) | Networking | OS independent asynchronous networking
+
+The following dependencies are **optional**:
+
+Library | Purpose | Description
+--------------------------------------------------------------- |------------------|----------------------
+[berkeley-db@4](https://formulae.brew.sh/formula/berkeley-db@4) | Berkeley DB | Wallet storage (only needed when wallet enabled)
+[qt@5](https://formulae.brew.sh/formula/qt@5) | GUI | GUI toolkit (only needed when GUI enabled)
+[qrencode](https://formulae.brew.sh/formula/qrencode) | QR codes in GUI | Generating QR codes (only needed when GUI enabled)
+[zeromq](https://formulae.brew.sh/formula/zeromq) | ZMQ notification | Allows generating ZMQ notifications (requires ZMQ version >= 4.0.0)
+[sqlite](https://formulae.brew.sh/formula/sqlite) | SQLite DB | Wallet storage (only needed when wallet enabled)
+[miniupnpc](https://formulae.brew.sh/formula/miniupnpc) | UPnP Support | Firewall-jumping support (needed for port mapping support)
+[libnatpmp](https://formulae.brew.sh/formula/libnatpmp) | NAT-PMP Support | Firewall-jumping support (needed for port mapping support)
+[python3](https://formulae.brew.sh/formula/python@3.9) | Testing | Python Interpreter (only needed when running the test suite)
+
+The following dependencies are **optional** packages required for deploying:
+
+Library | Purpose | Description
+----------------------------------------------------|------------------|----------------------
+[librsvg](https://formulae.brew.sh/formula/librsvg) | Deploy Dependency| Library to render SVG files
+[ds_store](https://pypi.org/project/ds-store/) | Deploy Dependency| Examine and modify .DS_Store files
+[mac_alias](https://pypi.org/project/mac-alias/) | Deploy Dependency| Generate/Read binary alias and bookmark records
+
+See [dependencies.md](dependencies.md) for a complete overview.
+
+## Preparation
The commands in this guide should be executed in a Terminal application.
-The built-in one is located in
+macOS comes with a built-in Terminal located in:
+
```
/Applications/Utilities/Terminal.app
```
-## Preparation
-Install the macOS command line tools:
+### 1. Xcode Command Line Tools
-```shell
+The Xcode Command Line Tools are a collection of build tools for macOS.
+These tools must be installed in order to build Bitcoin Core from source.
+
+To install, run the following command from your terminal:
+
+``` bash
xcode-select --install
```
-When the popup appears, click `Install`.
+Upon running the command, you should see a popup appear.
+Click on `Install` to continue the installation process.
-Then install [Homebrew](https://brew.sh).
+### 2. Homebrew Package Manager
-## Dependencies
-```shell
-brew install automake libtool boost miniupnpc libnatpmp pkg-config python qt libevent qrencode
+Homebrew is a package manager for macOS that allows one to install packages from the command line easily.
+While several package managers are available for macOS, this guide will focus on Homebrew as it is the most popular.
+Since the examples in this guide which walk through the installation of a package will use Homebrew, it is recommended that you install it to follow along.
+Otherwise, you can adapt the commands to your package manager of choice.
+
+To install the Homebrew package manager, see: https://brew.sh
+
+Note: If you run into issues while installing Homebrew or pulling packages, refer to [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
+
+### 3. Install Required Dependencies
+
+The first step is to download the required dependencies.
+These dependencies represent the packages required to get a barebones installation up and running.
+To install, run the following from your terminal:
+
+``` bash
+brew install automake libtool boost pkg-config libevent
```
-If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
-See [dependencies.md](dependencies.md) for a complete overview.
+### 4. Clone Bitcoin repository
-If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG:
-```shell
-brew install librsvg
+`git` should already be installed by default on your system.
+Now that all the required dependencies are installed, let's clone the Bitcoin Core repository to a directory.
+All build scripts and commands will run from this directory.
+
+``` bash
+git clone https://github.com/bitcoin/bitcoin.git
```
-and [`macdeployqtplus`](../contrib/macdeploy/README.md) dependencies:
-```shell
-pip3 install ds_store mac_alias
+### 5. Install Optional Dependencies
+
+#### Wallet Dependencies
+
+It is not necessary to build wallet functionality to run `bitcoind` or `bitcoin-qt`.
+To enable legacy wallets, you must install `berkeley-db@4`.
+To enable [descriptor wallets](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md), `sqlite` is required.
+Skip `berkeley-db@4` if you intend to *exclusively* use descriptor wallets.
+
+###### Legacy Wallet Support
+
+`berkeley-db@4` is required to enable support for legacy wallets.
+Skip if you don't intend to use legacy wallets.
+
+``` bash
+brew install berkeley-db@4
```
-The wallet support requires one or both of the dependencies ([*SQLite*](#sqlite) and [*Berkeley DB*](#berkeley-db)) in the sections below.
-To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode).
+###### Descriptor Wallet Support
-#### SQLite
+Note: Apple has included a useable `sqlite` package since macOS 10.14.
+You may not need to install this package.
-Usually, macOS installation already has a suitable SQLite installation.
-Also, the Homebrew package could be installed:
+`sqlite` is required to enable support for descriptor wallets.
+Skip if you don't intend to use descriptor wallets.
-```shell
+``` bash
brew install sqlite
```
+---
-In that case the Homebrew package will prevail.
+#### GUI Dependencies
-#### Berkeley DB
+###### Qt
-It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
-you can use [this](/contrib/install_db4.sh) script to install it
-like so:
+Bitcoin Core includes a GUI built with the cross-platform Qt Framework.
+To compile the GUI, we need to install `qt@5`.
+Skip if you don't intend to use the GUI.
-```shell
-./contrib/install_db4.sh .
+``` bash
+brew install qt@5
```
-from the root of the repository.
+Note: Building with Qt binaries downloaded from the Qt website is not officially supported.
+See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714).
-Also, the Homebrew package could be installed:
+###### qrencode
-```shell
-brew install berkeley-db4
+The GUI can encode addresses in a QR Code. To build in QR support for the GUI, install `qrencode`.
+Skip if not using the GUI or don't want QR code functionality.
+
+``` bash
+brew install qrencode
```
+---
+
+#### Port Mapping Dependencies
+
+###### miniupnpc
-## Build Bitcoin Core
+miniupnpc may be used for UPnP port mapping.
+Skip if you do not need this functionality.
-1. Clone the Bitcoin Core source code:
- ```shell
- git clone https://github.com/bitcoin/bitcoin
- cd bitcoin
- ```
+``` bash
+brew install miniupnpc
+```
-2. Build Bitcoin Core:
+###### libnatpmp
- Configure and build the headless Bitcoin Core binaries as well as the GUI (if Qt is found).
+libnatpmp may be used for NAT-PMP port mapping.
+Skip if you do not need this functionality.
- You can disable the GUI build by passing `--without-gui` to configure.
- ```shell
- ./autogen.sh
- ./configure
- make
- ```
+``` bash
+brew install libnatpmp
+```
-3. It is recommended to build and run the unit tests:
- ```shell
- make check
- ```
+Note: UPnP and NAT-PMP support will be compiled in and disabled by default.
+Check out the [further configuration](#further-configuration) section for more information.
-4. You can also create a `.dmg` that contains the `.app` bundle (optional):
- ```shell
- make deploy
- ```
+---
-## Disable-wallet mode
-When the intention is to run only a P2P node without a wallet, Bitcoin Core may be
-compiled in disable-wallet mode with:
-```shell
-./configure --disable-wallet
+#### ZMQ Dependencies
+
+Support for ZMQ notifications requires the following dependency.
+Skip if you do not need ZMQ functionality.
+
+``` bash
+brew install zeromq
```
-In this case there is no dependency on [*Berkeley DB*](#berkeley-db) and [*SQLite*](#sqlite).
+ZMQ is automatically compiled in and enabled if the dependency is detected.
+Check out the [further configuration](#further-configuration) section for more information.
+
+For more information on ZMQ, see: [zmq.md](zmq.md)
-Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.
+---
-## Running
-Bitcoin Core is now available at `./src/bitcoind`
+#### Test Suite Dependencies
+
+There is an included test suite that is useful for testing code changes when developing.
+To run the test suite (recommended), you will need to have Python 3 installed:
+
+``` bash
+brew install python
+```
+
+---
+
+#### Deploy Dependencies
+
+You can deploy a `.dmg` containing the Bitcoin Core application using `make deploy`.
+This command depends on a couple of python packages, so it is required that you have `python` installed.
+
+Ensuring that `python` is installed, you can install the deploy dependencies by running the following commands in your terminal:
+
+``` bash
+brew install librsvg
+```
+
+``` bash
+pip3 install ds_store mac_alias
+```
+
+## Building Bitcoin Core
+
+### 1. Configuration
+
+There are many ways to configure Bitcoin Core, here are a few common examples:
+
+##### Wallet (BDB + SQlite) Support, No GUI:
+
+If `berkeley-db@4` is installed, then legacy wallet support will be built.
+If `berkeley-db@4` is not installed, then this will throw an error.
+If `sqlite` is installed, then descriptor wallet support will also be built.
+Additionally, this explicitly disables the GUI.
+
+``` bash
+./autogen.sh
+./configure --with-gui=no
+```
+
+##### Wallet (only SQlite) and GUI Support:
+
+This explicitly enables the GUI and disables legacy wallet support.
+If `qt` is not installed, this will throw an error.
+If `sqlite` is installed then descriptor wallet functionality will be built.
+If `sqlite` is not installed, then wallet functionality will be disabled.
+
+``` bash
+./autogen.sh
+./configure --without-bdb --with-gui=yes
+```
+
+##### No Wallet or GUI
+
+``` bash
+./autogen.sh
+./configure --without-wallet --with-gui=no
+```
+
+##### Further Configuration
+
+You may want to dig deeper into the configuration options to achieve your desired behavior.
+Examine the output of the following command for a full list of configuration options:
+
+``` bash
+./configure -help
+```
+
+### 2. Compile
+
+After configuration, you are ready to compile.
+Run the following in your terminal to compile Bitcoin Core:
+
+``` bash
+make -jx # use -jX here for parallelism
+make check # Run tests if Python 3 is available
+```
+
+### 3. Deploy (optional)
+
+You can also create a `.dmg` containing the `.app` bundle by running the following command:
+
+``` bash
+make deploy
+```
+
+## Running Bitcoin Core
+
+Bitcoin Core should now be available at `./src/bitcoind`.
+If you compiled support for the GUI, it should be available at `./src/qt/bitcoin-qt`.
+
+The first time you run `bitcoind` or `bitcoin-qt`, it will start downloading the blockchain.
+This process could take many hours, or even days on slower than average systems.
+
+By default, blockchain and wallet data files will be stored in:
+
+``` bash
+/Users/${USER}/Library/Application Support/Bitcoin/
+```
Before running, you may create an empty configuration file:
+
```shell
mkdir -p "/Users/${USER}/Library/Application Support/Bitcoin"
@@ -119,22 +306,17 @@ touch "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
```
-The first time you run bitcoind, it will start downloading the blockchain. This process could
-take many hours, or even days on slower than average systems.
-
You can monitor the download process by looking at the debug.log file:
+
```shell
tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log
```
## Other commands:
+
```shell
./src/bitcoind -daemon # Starts the bitcoin daemon.
./src/bitcoin-cli --help # Outputs a list of command-line options.
./src/bitcoin-cli help # Outputs a list of RPC commands when the daemon is running.
+./src/qt/bitcoin-qt -server # Starts the bitcoin-qt server mode, allows bitcoin-cli control
```
-
-## Notes
-* Tested on OS X 10.14 Mojave through macOS 11 Big Sur on 64-bit Intel
-processors only.
-* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714).
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 5c24886dbf..d7e0ff705d 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -9,7 +9,7 @@ Note
Always use absolute paths to configure and compile Bitcoin Core and the dependencies.
For example, when specifying the path of the dependency:
- ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX
+ ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX
Here BDB_PREFIX must be an absolute path - it is defined using $(pwd) which ensures
the usage of the absolute path.
@@ -82,25 +82,24 @@ Build requirements:
Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies:
- sudo apt-get install libevent-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev
+ sudo apt-get install libevent-dev libboost-dev libboost-system-dev libboost-filesystem-dev libboost-test-dev
-BerkeleyDB is required for the wallet.
+Berkeley DB is required for the wallet.
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
+Berkeley DB 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.
-Otherwise, you can build from self-compiled `depends` (see above).
+Otherwise, you can build Berkeley DB [yourself](#berkeley-db).
-SQLite is required for the wallet:
+SQLite is required for the descriptor wallet:
sudo apt install libsqlite3-dev
-To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
+To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode)
-
-Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`):
+Optional port mapping libraries (see: `--with-miniupnpc`, `--enable-upnp-default`, and `--with-natpmp`, `--enable-natpmp-default`):
sudo apt install libminiupnpc-dev libnatpmp-dev
@@ -132,9 +131,30 @@ built by default.
Build requirements:
- sudo dnf install gcc-c++ libtool make autoconf automake libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3
+ sudo dnf install gcc-c++ libtool make autoconf automake python3
+
+Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies:
+
+ sudo dnf install libevent-devel boost-devel
+
+Berkeley DB is required for the wallet:
+
+ sudo dnf install libdb4-devel libdb4-cxx-devel
+
+Newer Fedora releases, since Fedora 33, have only `libdb-devel` and `libdb-cxx-devel` packages, but these will install
+Berkeley DB 5.3 or later. This will break binary wallet compatibility with the distributed executables, which
+are based on Berkeley DB 4.8. If you do not care about wallet compatibility,
+pass `--with-incompatible-bdb` to configure.
+
+Otherwise, you can build Berkeley DB [yourself](#berkeley-db).
+
+SQLite is required for the descriptor wallet:
+
+ sudo dnf install sqlite-devel
+
+To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode)
-Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`):
+Optional port mapping libraries (see: `--with-miniupnpc`, `--enable-upnp-default`, and `--with-natpmp`, `--enable-natpmp-default`):
sudo dnf install miniupnpc-devel libnatpmp-devel
@@ -142,6 +162,12 @@ ZMQ dependencies (provides ZMQ API):
sudo dnf install zeromq-devel
+GUI dependencies:
+
+If you want to build bitcoin-qt, make sure that the required packages for Qt development
+are installed. Qt 5 is necessary to build the GUI.
+To build without GUI pass `--without-gui`.
+
To build with Qt 5 you need the following:
sudo dnf install qt5-qttools-devel qt5-qtbase-devel
@@ -150,9 +176,8 @@ libqrencode (optional) can be installed with:
sudo dnf install qrencode-devel
-SQLite can be installed with:
-
- sudo dnf install sqlite-devel
+Once these are installed, they will be found by configure and a bitcoin-qt executable will be
+built by default.
Notes
-----
@@ -166,9 +191,9 @@ miniupnpc
https://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and
turned off by default. See the configure options for UPnP behavior desired:
- --without-miniupnpc No UPnP support, miniupnp not required
- --disable-upnp-default (the default) UPnP support turned off by default at runtime
- --enable-upnp-default UPnP support turned on by default at runtime
+ --without-miniupnpc No UPnP support, miniupnp not required
+ --disable-upnp-default (the default) UPnP support turned off by default at runtime
+ --enable-upnp-default UPnP support turned on by default at runtime
libnatpmp
---------
@@ -177,9 +202,9 @@ libnatpmp
from [here](https://miniupnp.tuxfamily.org/files/). NAT-PMP support is compiled in and
turned off by default. See the configure options for NAT-PMP behavior desired:
- --without-natpmp No NAT-PMP support, libnatpmp not required
- --disable-natpmp-default (the default) NAT-PMP support turned off by default at runtime
- --enable-natpmp-default NAT-PMP support turned on by default at runtime
+ --without-natpmp No NAT-PMP support, libnatpmp not required
+ --disable-natpmp-default (the default) NAT-PMP support turned off by default at runtime
+ --enable-natpmp-default NAT-PMP support turned on by default at runtime
Berkeley DB
-----------
@@ -193,15 +218,17 @@ like so:
from the root of the repository.
-**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)).
+Otherwise, you can build Bitcoin Core from self-compiled [depends](/depends/README.md).
+
+**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](#disable-wallet-mode)).
Boost
-----
If you need to build Boost yourself:
- sudo su
- ./bootstrap.sh
- ./bjam install
+ sudo su
+ ./bootstrap.sh
+ ./bjam install
Security
@@ -212,8 +239,8 @@ This can be disabled with:
Hardening Flags:
- ./configure --enable-hardening
- ./configure --disable-hardening
+ ./configure --enable-hardening
+ ./configure --disable-hardening
Hardening enables the following features:
@@ -228,7 +255,7 @@ Hardening enables the following features:
To test that you have built PIE executable, install scanelf, part of paxutils, and use:
- scanelf -e ./bitcoin
+ scanelf -e ./bitcoin
The output should contain:
@@ -245,8 +272,8 @@ Hardening enables the following features:
`scanelf -e ./bitcoin`
The output should contain:
- STK/REL/PTL
- RW- R-- RW-
+ STK/REL/PTL
+ RW- R-- RW-
The STK RW- means that the stack is readable and writeable but not executable.
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 28b6aceb3c..d1b84eef42 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -103,7 +103,7 @@ Build using:
cd depends
make HOST=x86_64-w64-mingw32
cd ..
- ./autogen.sh # not required when building from tarball
+ ./autogen.sh
CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/
make
sudo bash -c "echo 1 > /proc/sys/fs/binfmt_misc/status" # Enable WSL support for Win32 applications.
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 6b2b904632..1397fe9d0c 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -14,19 +14,19 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| GCC | | [7+](https://gcc.gnu.org/) (C++17 support) | | | |
| HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| libevent | [2.1.11-stable](https://github.com/libevent/libevent/releases) | [2.0.21](https://github.com/bitcoin/bitcoin/pull/18676) | No | | |
-| libnatpmp | [20150609](https://miniupnp.tuxfamily.org/files) | | No | | |
+| libnatpmp | git commit [4536032...](https://github.com/miniupnp/libnatpmp/tree/4536032ae32268a45c073a4d5e91bbab4534773a) | | No | | |
| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| librsvg | | | | | |
| MiniUPnPc | [2.0.20180203](https://miniupnp.tuxfamily.org/files) | | No | | |
| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| Python (tests) | | [3.6](https://www.python.org/downloads) | | | |
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
-| Qt | [5.9.8](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | |
+| Qt | [5.12.10](https://download.qt.io/official_releases/qt/) | [5.9.5](https://github.com/bitcoin/bitcoin/issues/20104) | No | | |
| SQLite | [3.32.1](https://sqlite.org/download.html) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) | | | |
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | |
-| zlib | [1.2.11](https://zlib.net/) | | | | No |
+| zlib | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
Controlling dependencies
------------------------
@@ -43,3 +43,4 @@ Some dependencies are not needed in all configurations. The following are some f
#### Other
* librsvg is only needed if you need to run `make deploy` on (cross-compilation to) macOS.
+* Not-Qt-bundled zlib is required to build the [DMG tool](../contrib/macdeploy/README.md#deterministic-macos-dmg-notes) from the libdmg-hfsplus project.
diff --git a/doc/descriptors.md b/doc/descriptors.md
index 63acb9167f..c4fc2a66bf 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -191,7 +191,7 @@ steps, or for dumping wallet descriptors including private key material.
In order to easily represent the sets of scripts currently supported by
existing Bitcoin Core wallets, a convenience function `combo` is
provided, which takes as input a public key, and describes a set of P2PK,
-P2PKH, P2WPKH, and P2SH-P2WPH scripts for that key. In case the key is
+P2PKH, P2WPKH, and P2SH-P2WPKH scripts for that key. In case the key is
uncompressed, the set only includes P2PK and P2PKH scripts.
### Checksums
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 596f65cf10..0a78cdff20 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -75,6 +75,11 @@ tool to clean up patches automatically before submission.
on the same line as the `if`, without braces. In every other case,
braces are required, and the `then` and `else` clauses must appear
correctly indented on a new line.
+ - There's no hard limit on line width, but prefer to keep lines to <100
+ characters if doing so does not decrease readability. Break up long
+ function declarations over multiple lines using the Clang Format
+ [AlignAfterOpenBracket](https://clang.llvm.org/docs/ClangFormatStyleOptions.html)
+ style option.
- **Symbol naming conventions**. These are preferred in new code, but are not
required when doing so would need changes to significant pieces of existing
@@ -590,11 +595,6 @@ Common misconceptions are clarified in those sections:
- *Rationale*: This avoids memory and resource leaks, and ensures exception safety.
-- 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).
-
C++ data structures
--------------------
@@ -780,6 +780,11 @@ Threads and synchronization
get compile-time warnings about potential race conditions in code. Combine annotations in function declarations with
run-time asserts in function definitions:
+ - In functions that are declared separately from where they are defined, the
+ thread safety annotations should be added exclusively to the function
+ declaration. Annotations on the definition could lead to false positives
+ (lack of compile failure) at call sites between the two.
+
```C++
// txmempool.h
class CTxMemPool
diff --git a/doc/external-signer.md b/doc/external-signer.md
new file mode 100644
index 0000000000..2b3b378bee
--- /dev/null
+++ b/doc/external-signer.md
@@ -0,0 +1,171 @@
+# Support for signing transactions outside of Bitcoin Core
+
+Bitcoin Core can be launched with `-signer=<cmd>` where `<cmd>` is an external tool which can sign transactions and perform other functions. For example, it can be used to communicate with a hardware wallet.
+
+## Example usage
+
+The following example is based on the [HWI](https://github.com/bitcoin-core/HWI) tool. Version 2.0 or newer is required. Although this tool is hosted under the Bitcoin Core GitHub organization and maintained by Bitcoin Core developers, it should be used with caution. It is considered experimental and has far less review than Bitcoin Core itself. Be particularly careful when running tools such as these on a computer with private keys on it.
+
+When using a hardware wallet, consult the manufacturer website for (alternative) software they recommend. As long as their software conforms to the standard below, it should be able to work with Bitcoin Core.
+
+Start Bitcoin Core:
+
+```sh
+$ bitcoind -signer=../HWI/hwi.py
+```
+
+### Device setup
+
+Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/bitcoin-core/HWI).
+
+### Create wallet and import keys
+
+Get a list of signing devices / services:
+
+```
+$ bitcoin-cli enumeratesigners
+{
+ "signers": [
+ {
+ "fingerprint": "c8df832a"
+ }
+]
+```
+
+The master key fingerprint is used to identify a device.
+
+Create a wallet, this automatically imports the public keys:
+
+```sh
+$ bitcoin-cli createwallet "hww" true true "" true true true
+```
+
+### Verify an address
+
+Display an address on the device:
+
+```sh
+$ bitcoin-cli -rpcwallet=<wallet> getnewaddress
+$ bitcoin-cli -rpcwallet=<wallet> signerdisplayaddress <address>
+```
+
+Replace `<address>` with the result of `getnewaddress`.
+
+### Spending
+
+Under the hood this uses a [Partially Signed Bitcoin Transaction](psbt.md).
+
+```sh
+$ bitcoin-cli -rpcwallet=<wallet> sendtoaddress <address> <amount>
+```
+
+This prompts your hardware wallet to sign, and fail if it's not connected. If successful
+it automatically broadcasts the transaction.
+
+```sh
+{"complete": true, "txid": <txid>}
+```
+
+## Signer API
+
+In order to be compatible with Bitcoin Core any signer command should conform to the specification below. This specification is subject to change. Ideally a BIP should propose a standard so that other wallets can also make use of it.
+
+Prerequisite knowledge:
+* [Output Descriptors](descriptors.md)
+* Partially Signed Bitcoin Transaction ([PSBT](psbt.md))
+
+### `enumerate` (required)
+
+Usage:
+```
+$ <cmd> enumerate
+[
+ {
+ "fingerprint": "00000000"
+ }
+]
+```
+
+The command MUST return an (empty) array with at least a `fingerprint` field.
+
+A future extension could add an optional return field with device capabilities. Perhaps a descriptor with wildcards. For example: `["pkh("44'/0'/$'/{0,1}/*"), sh(wpkh("49'/0'/$'/{0,1}/*")), wpkh("84'/0'/$'/{0,1}/*")]`. This would indicate the device supports legacy, wrapped SegWit and native SegWit. In addition it restricts the derivation paths that can used for those, to maintain compatibility with other wallet software. It also indicates the device, or the driver, doesn't support multisig.
+
+A future extension could add an optional return field `reachable`, in case `<cmd>` knows a signer exists but can't currently reach it.
+
+### `signtransaction` (required)
+
+Usage:
+```
+$ <cmd> --fingerprint=<fingerprint> (--testnet) signtransaction <psbt>
+base64_encode_signed_psbt
+```
+
+The command returns a psbt with any signatures.
+
+The `psbt` SHOULD include bip32 derivations. The command SHOULD fail if none of the bip32 derivations match a key owned by the device.
+
+The command SHOULD fail if the user cancels.
+
+The command MAY complain if `--testnet` is set, but any of the BIP32 derivation paths contain a coin type other than `1h` (and vice versa).
+
+### `getdescriptors` (optional)
+
+Usage:
+
+```
+$ <cmd> --fingerprint=<fingerprint> (--testnet) getdescriptors <account>
+<xpub>
+```
+
+Returns descriptors supported by the device. Example:
+
+```
+$ <cmd> --fingerprint=00000000 --testnet getdescriptors
+{
+ "receive": [
+ "pkh([00000000/44h/0h/0h]xpub6C.../0/*)#fn95jwmg",
+ "sh(wpkh([00000000/49h/0h/0h]xpub6B..../0/*))#j4r9hntt",
+ "wpkh([00000000/84h/0h/0h]xpub6C.../0/*)#qw72dxa9"
+ ],
+ "internal": [
+ "pkh([00000000/44h/0h/0h]xpub6C.../1/*)#c8q40mts",
+ "sh(wpkh([00000000/49h/0h/0h]xpub6B..../1/*))#85dn0v75",
+ "wpkh([00000000/84h/0h/0h]xpub6C..../1/*)#36mtsnda"
+ ]
+}
+```
+
+### `displayaddress` (optional)
+
+Usage:
+```
+<cmd> --fingerprint=<fingerprint> (--testnet) displayaddress --desc descriptor
+```
+
+Example, display the first native SegWit receive address on Testnet:
+
+```
+<cmd> --fingerprint=00000000 --testnet displayaddress --desc "wpkh([00000000/84h/1h/0h]tpubDDUZ..../0/0)"
+```
+
+The command MUST be able to figure out the address type from the descriptor.
+
+If <descriptor> contains a master key fingerprint, the command MUST fail if it does not match the fingerprint known by the device.
+
+If <descriptor> contains an xpub, the command MUST fail if it does not match the xpub known by the device.
+
+The command MAY complain if `--testnet` is set, but the BIP32 coin type is not `1h` (and vice versa).
+
+## How Bitcoin Core uses the Signer API
+
+The `enumeratesigners` RPC simply calls `<cmd> enumerate`.
+
+The `createwallet` RPC calls:
+
+* `<cmd> --fingerprint=00000000 getdescriptors 0`
+
+It then imports descriptors for all support address types, in a BIP44/49/84 compatible manner.
+
+The `displayaddress` RPC reuses some code from `getaddressinfo` on the provided address and obtains the inferred descriptor. It then calls `<cmd> --fingerprint=00000000 displayaddress --desc=<descriptor>`.
+
+`sendtoaddress` and `sendmany` check `inputs->bip32_derivs` to see if any inputs have the same `master_fingerprint` as the signer. If so, it calls `<cmd> --fingerprint=00000000 signtransaction <psbt>`. It waits for the device to return a (partially) signed psbt, tries to finalize it and broadcasts the transaction.
diff --git a/doc/files.md b/doc/files.md
index 545e8fc92c..a80adf30ea 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -64,6 +64,7 @@ Subdirectory | File(s) | Description
`./` | `ip_asn.map` | IP addresses to Autonomous System Numbers (ASNs) mapping used for bucketing of the peers; path can be specified with the `-asmap` option
`./` | `mempool.dat` | Dump of the mempool's transactions
`./` | `onion_v3_private_key` | Cached Tor onion service private key for `-listenonion` option
+`./` | `i2p_private_key` | Private key that corresponds to our I2P address. When `-i2psam=` is specified the contents of this file is used to identify ourselves for making outgoing connections to I2P peers and possibly accepting incoming ones. Automatically generated if it does not exist.
`./` | `peers.dat` | Peer IP address database (custom format)
`./` | `settings.json` | Read-write settings set through GUI or RPC interfaces, augmenting manual settings from [bitcoin.conf](bitcoin-conf.md). File is created automatically if read-write settings storage is not disabled with `-nosettings` option. Path can be specified with `-settings` option
`./` | `.cookie` | Session RPC authentication cookie; if used, created at start and deleted on shutdown; can be specified by `-rpccookiefile` option
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
index 80ce821091..4d8825f4c2 100644
--- a/doc/fuzzing.md
+++ b/doc/fuzzing.md
@@ -108,33 +108,32 @@ Full configure that was tested on macOS Catalina with `brew` installed `llvm`:
Read the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html) for more information. This [libFuzzer tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) might also be of interest.
-# Fuzzing Bitcoin Core using american fuzzy lop (`afl-fuzz`)
+# Fuzzing Bitcoin Core using afl++
## Quickstart guide
-To quickly get started fuzzing Bitcoin Core using [`afl-fuzz`](https://github.com/google/afl):
+To quickly get started fuzzing Bitcoin Core using [afl++](https://github.com/AFLplusplus/AFLplusplus):
```sh
$ git clone https://github.com/bitcoin/bitcoin
$ cd bitcoin/
-$ git clone https://github.com/google/afl
-$ make -C afl/
-$ make -C afl/llvm_mode/
+$ git clone https://github.com/AFLplusplus/AFLplusplus
+$ make -C AFLplusplus/ source-only
$ ./autogen.sh
-# It is possible to compile with afl-gcc and afl-g++ instead of afl-clang. However, running afl-fuzz
-# may require more memory via the -m flag.
-$ CC=$(pwd)/afl/afl-clang-fast CXX=$(pwd)/afl/afl-clang-fast++ ./configure --enable-fuzz
+# If afl-clang-lto is not available, see
+# https://github.com/AFLplusplus/AFLplusplus#a-selecting-the-best-afl-compiler-for-instrumenting-the-target
+$ CC=$(pwd)/AFLplusplus/afl-clang-lto CXX=$(pwd)/AFLplusplus/afl-clang-lto++ ./configure --enable-fuzz
$ make
# For macOS you may need to ignore x86 compilation checks when running "make". If so,
# try compiling using: AFL_NO_X86=1 make
$ mkdir -p inputs/ outputs/
$ echo A > inputs/thin-air-input
-$ FUZZ=bech32 afl/afl-fuzz -i inputs/ -o outputs/ -- src/test/fuzz/fuzz
+$ FUZZ=bech32 AFLplusplus/afl-fuzz -i inputs/ -o outputs/ -- src/test/fuzz/fuzz
# You may have to change a few kernel parameters to test optimally - afl-fuzz
# will print an error and suggestion if so.
```
-Read the [`afl-fuzz` documentation](https://github.com/google/afl) for more information.
+Read the [afl++ documentation](https://github.com/AFLplusplus/AFLplusplus) for more information.
# Fuzzing Bitcoin Core using Honggfuzz
@@ -157,3 +156,77 @@ $ FUZZ=process_message honggfuzz/honggfuzz -i inputs/ -- src/test/fuzz/fuzz
```
Read the [Honggfuzz documentation](https://github.com/google/honggfuzz/blob/master/docs/USAGE.md) for more information.
+
+## Fuzzing the Bitcoin Core P2P layer using Honggfuzz NetDriver
+
+Honggfuzz NetDriver allows for very easy fuzzing of TCP servers such as Bitcoin
+Core without having to write any custom fuzzing harness. The `bitcoind` server
+process is largely fuzzed without modification.
+
+This makes the fuzzing highly realistic: a bug reachable by the fuzzer is likely
+also remotely triggerable by an untrusted peer.
+
+To quickly get started fuzzing the P2P layer using Honggfuzz NetDriver:
+
+```sh
+$ mkdir bitcoin-honggfuzz-p2p/
+$ cd bitcoin-honggfuzz-p2p/
+$ git clone https://github.com/bitcoin/bitcoin
+$ cd bitcoin/
+$ ./autogen.sh
+$ git clone https://github.com/google/honggfuzz
+$ cd honggfuzz/
+$ make
+$ cd ..
+$ CC=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang \
+ CXX=$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang++ \
+ ./configure --disable-wallet --with-gui=no \
+ --with-sanitizers=address,undefined
+$ git apply << "EOF"
+diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
+index 455a82e39..2faa3f80f 100644
+--- a/src/bitcoind.cpp
++++ b/src/bitcoind.cpp
+@@ -158,7 +158,11 @@ static bool AppInit(int argc, char* argv[])
+ return fRet;
+ }
+
++#ifdef HFND_FUZZING_ENTRY_FUNCTION_CXX
++HFND_FUZZING_ENTRY_FUNCTION_CXX(int argc, char* argv[])
++#else
+ int main(int argc, char* argv[])
++#endif
+ {
+ #ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+diff --git a/src/net.cpp b/src/net.cpp
+index cf987b699..636a4176a 100644
+--- a/src/net.cpp
++++ b/src/net.cpp
+@@ -709,7 +709,7 @@ int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes)
+ }
+
+ // Check start string, network magic
+- if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
++ if (false && memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) { // skip network magic checking
+ LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.pchMessageStart), m_node_id);
+ return -1;
+ }
+@@ -768,7 +768,7 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::mic
+ RandAddEvent(ReadLE32(hash.begin()));
+
+ // Check checksum and header command string
+- if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
++ if (false && memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { // skip checksum checking
+ LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
+ SanitizeString(msg->m_command), msg->m_message_size,
+ HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
+EOF
+$ make -C src/ bitcoind
+$ mkdir -p inputs/
+$ honggfuzz/honggfuzz --exit_upon_crash --quiet --timeout 4 -n 1 -Q \
+ -E HFND_TCP_PORT=18444 -f inputs/ -- \
+ src/bitcoind -regtest -discover=0 -dns=0 -dnsseed=0 -listenonion=0 \
+ -nodebuglogfile -bind=127.0.0.1:18444 -logthreadnames \
+ -debug
+```
diff --git a/doc/init.md b/doc/init.md
index 99aa0a0def..399b819bf4 100644
--- a/doc/init.md
+++ b/doc/init.md
@@ -53,11 +53,12 @@ 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`
-PID file: `/var/run/bitcoind/bitcoind.pid` (OpenRC and Upstart) or `/run/bitcoind/bitcoind.pid` (systemd)
-Lock file: `/var/lock/subsys/bitcoind` (CentOS)
+ 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)
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
@@ -83,10 +84,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/release-notes-18466.md b/doc/release-notes-18466.md
new file mode 100644
index 0000000000..e46d5064a3
--- /dev/null
+++ b/doc/release-notes-18466.md
@@ -0,0 +1,10 @@
+Low-level RPC changes
+---------------------
+
+- Error codes have been updated to be more accurate for the following error cases (#18466):
+ - `signmessage` now returns RPC_INVALID_ADDRESS_OR_KEY (-5) if the
+ passed address is invalid. Previously returned RPC_TYPE_ERROR (-3).
+ - `verifymessage` now returns RPC_INVALID_ADDRESS_OR_KEY (-5) if the
+ passed address is invalid. Previously returned RPC_TYPE_ERROR (-3).
+ - `verifymessage` now returns RPC_TYPE_ERROR (-3) if the passed signature
+ is malformed. Previously returned RPC_INVALID_ADDRESS_OR_KEY (-5).
diff --git a/doc/release-notes-20861.md b/doc/release-notes-20861.md
new file mode 100644
index 0000000000..5c68e4ab0c
--- /dev/null
+++ b/doc/release-notes-20861.md
@@ -0,0 +1,13 @@
+Updated RPCs
+------------
+
+- Due to [BIP 350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
+ being implemented, behavior for all RPCs that accept addresses is changed when
+ a native witness version 1 (or higher) is passed. These now require a Bech32m
+ encoding instead of a Bech32 one, and Bech32m encoding will be used for such
+ addresses in RPC output as well. No version 1 addresses should be created
+ for mainnet until consensus rules are adopted that give them meaning
+ (e.g. through [BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)).
+ Once that happens, Bech32m is expected to be used for them, so this shouldn't
+ affect any production systems, but may be observed on other networks where such
+ addresses already have meaning (like signet).
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 8f1e03e16b..0f248494c7 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -93,6 +93,10 @@ Tools and Utilities
Wallet
------
+- A new `listdescriptors` RPC is available to inspect the contents of descriptor-enabled wallets.
+ The RPC returns public versions of all imported descriptors, including their timestamp and flags.
+ For ranged descriptors, it also returns the range boundaries and the next index to generate addresses from. (#20226)
+
GUI changes
-----------
diff --git a/doc/tor.md b/doc/tor.md
index 8a2aef2d07..e38ada5bd6 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -21,39 +21,39 @@ information in the debug log about your Tor configuration.
The first step is running Bitcoin Core behind a Tor proxy. This will already anonymize all
outgoing connections, but more is possible.
- -proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
- server will be used to try to reach .onion addresses as well.
- You need to use -noonion or -onion=0 to explicitly disable
- outbound access to onion services.
-
- -onion=ip:port Set the proxy server to use for Tor onion services. You do not
- need to set this if it's the same as -proxy. You can use -onion=0
- to explicitly disable access to onion services.
- Note: Only the -proxy option sets the proxy for DNS requests;
- with -onion they will not route over Tor, so use -proxy if you
- have privacy concerns.
-
- -listen When using -proxy, listening is disabled by default. If you want
- to manually configure an onion service (see section 3), you'll
- need to enable it explicitly.
-
- -connect=X When behind a Tor proxy, you can specify .onion addresses instead
- -addnode=X of IP addresses or hostnames in these parameters. It requires
- -seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
- other P2P nodes.
-
- -onlynet=onion Make outgoing connections only to .onion addresses. Incoming
- connections are not affected by this option. This option can be
- specified multiple times to allow multiple network types, e.g.
- ipv4, ipv6 or onion. If you use this option with values other
- than onion you *cannot* disable onion connections; outgoing onion
- connections will be enabled when you use -proxy or -onion. Use
- -noonion or -onion=0 if you want to be sure there are no outbound
- onion connections over the default proxy or your defined -proxy.
+ -proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
+ server will be used to try to reach .onion addresses as well.
+ You need to use -noonion or -onion=0 to explicitly disable
+ outbound access to onion services.
+
+ -onion=ip:port Set the proxy server to use for Tor onion services. You do not
+ need to set this if it's the same as -proxy. You can use -onion=0
+ to explicitly disable access to onion services.
+ Note: Only the -proxy option sets the proxy for DNS requests;
+ with -onion they will not route over Tor, so use -proxy if you
+ have privacy concerns.
+
+ -listen When using -proxy, listening is disabled by default. If you want
+ to manually configure an onion service (see section 3), you'll
+ need to enable it explicitly.
+
+ -connect=X When behind a Tor proxy, you can specify .onion addresses instead
+ -addnode=X of IP addresses or hostnames in these parameters. It requires
+ -seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
+ other P2P nodes.
+
+ -onlynet=onion Make outgoing connections only to .onion addresses. Incoming
+ connections are not affected by this option. This option can be
+ specified multiple times to allow multiple network types, e.g.
+ ipv4, ipv6 or onion. If you use this option with values other
+ than onion you *cannot* disable onion connections; outgoing onion
+ connections will be enabled when you use -proxy or -onion. Use
+ -noonion or -onion=0 if you want to be sure there are no outbound
+ onion connections over the default proxy or your defined -proxy.
In a typical situation, this suffices to run behind a Tor proxy:
- ./bitcoind -proxy=127.0.0.1:9050
+ ./bitcoind -proxy=127.0.0.1:9050
## 2. Automatically create a Bitcoin Core onion service
@@ -152,57 +152,57 @@ details).
You can also manually configure your node to be reachable from the Tor network.
Add these lines to your `/etc/tor/torrc` (or equivalent config file):
- HiddenServiceDir /var/lib/tor/bitcoin-service/
- HiddenServicePort 8333 127.0.0.1:8334
+ HiddenServiceDir /var/lib/tor/bitcoin-service/
+ HiddenServicePort 8333 127.0.0.1:8334
The directory can be different of course, but virtual port numbers should be equal to
your bitcoind's P2P listen port (8333 by default), and target addresses and ports
should be equal to binding address and port for inbound Tor connections (127.0.0.1:8334 by default).
- -externalip=X You can tell bitcoin about its publicly reachable addresses using
- this option, and this can be an onion address. Given the above
- configuration, you can find your onion address in
- /var/lib/tor/bitcoin-service/hostname. For connections
- coming from unroutable addresses (such as 127.0.0.1, where the
- Tor proxy typically runs), onion addresses are given
- preference for your node to advertise itself with.
-
- You can set multiple local addresses with -externalip. The
- one that will be rumoured to a particular peer is the most
- compatible one and also using heuristics, e.g. the address
- with the most incoming connections, etc.
-
- -listen You'll need to enable listening for incoming connections, as this
- is off by default behind a proxy.
-
- -discover When -externalip is specified, no attempt is made to discover local
- IPv4 or IPv6 addresses. If you want to run a dual stack, reachable
- from both Tor and IPv4 (or IPv6), you'll need to either pass your
- other addresses using -externalip, or explicitly enable -discover.
- Note that both addresses of a dual-stack system may be easily
- linkable using traffic analysis.
+ -externalip=X You can tell bitcoin about its publicly reachable addresses using
+ this option, and this can be an onion address. Given the above
+ configuration, you can find your onion address in
+ /var/lib/tor/bitcoin-service/hostname. For connections
+ coming from unroutable addresses (such as 127.0.0.1, where the
+ Tor proxy typically runs), onion addresses are given
+ preference for your node to advertise itself with.
+
+ You can set multiple local addresses with -externalip. The
+ one that will be rumoured to a particular peer is the most
+ compatible one and also using heuristics, e.g. the address
+ with the most incoming connections, etc.
+
+ -listen You'll need to enable listening for incoming connections, as this
+ is off by default behind a proxy.
+
+ -discover When -externalip is specified, no attempt is made to discover local
+ IPv4 or IPv6 addresses. If you want to run a dual stack, reachable
+ from both Tor and IPv4 (or IPv6), you'll need to either pass your
+ other addresses using -externalip, or explicitly enable -discover.
+ Note that both addresses of a dual-stack system may be easily
+ linkable using traffic analysis.
In a typical situation, where you're only reachable via Tor, this should suffice:
- ./bitcoind -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
+ ./bitcoind -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
(obviously, replace the .onion address with your own). It should be noted that you still
listen on all devices and another node could establish a clearnet connection, when knowing
your address. To mitigate this, additionally bind the address of your Tor proxy:
- ./bitcoind ... -bind=127.0.0.1
+ ./bitcoind ... -bind=127.0.0.1
If you don't care too much about hiding your node, and want to be reachable on IPv4
as well, use `discover` instead:
- ./bitcoind ... -discover
+ ./bitcoind ... -discover
and open port 8333 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`).
If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
- ./bitcoind -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
+ ./bitcoind -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
## 4. Privacy recommendations
diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf
index 90a592cc63..5b7fc776a4 100644
--- a/share/examples/bitcoin.conf
+++ b/share/examples/bitcoin.conf
@@ -4,13 +4,16 @@
# Network-related settings:
-# Note that if you use testnet or regtest, particularly with the options
+# Note that if you use testnet, signet or regtest, particularly with the options
# addnode, connect, port, bind, rpcport, rpcbind or wallet, you will also
# want to read "[Sections]" further down.
-# Run on the test network instead of the real bitcoin network.
+# Run on the testnet network
#testnet=0
+# Run on a signet network
+#signet=0
+
# Run a regression test network
#regtest=0
@@ -57,7 +60,7 @@
# Listening mode, enabled by default except when 'connect' is being used
#listen=1
-# Port on which to listen for connections (default: 8333, testnet: 18333, regtest: 18444)
+# Port on which to listen for connections (default: 8333, testnet: 18333, signet: 38333, regtest: 18444)
#port=
# Maximum number of inbound+outbound connections.
@@ -155,7 +158,7 @@
#minimizetotray=1
# [Sections]
-# Most options apply to mainnet, testnet and regtest.
+# Most options apply to mainnet, testnet, signet and regtest.
# If you want to confine an option to just one network, you should add it in the
# relevant section below.
# EXCEPTIONS: The options addnode, connect, port, bind, rpcport, rpcbind and wallet
@@ -167,5 +170,8 @@
# Options only for testnet
[test]
+# Options only for signet
+[signet]
+
# Options only for regtest
[regtest]
diff --git a/share/setup.nsi.in b/share/setup.nsi.in
index 681f243d04..85ae7c57af 100644
--- a/share/setup.nsi.in
+++ b/share/setup.nsi.in
@@ -3,6 +3,7 @@ Name "@PACKAGE_NAME@ (64-bit)"
RequestExecutionLevel highest
SetCompressor /SOLID lzma
SetDateSave off
+Unicode true
# Uncomment these lines when investigating reproducibility errors
#SetCompress off
diff --git a/src/.clang-format b/src/.clang-format
index ef7a0ef5c7..a69c57f3e0 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -11,7 +11,8 @@ AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
-BinPackParameters: false
+BinPackArguments: true
+BinPackParameters: true
BreakBeforeBinaryOperators: false
BreakBeforeBraces: Custom
BraceWrapping:
diff --git a/src/Makefile.am b/src/Makefile.am
index 2616eb8638..9903c2e9b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
DIST_SUBDIRS = secp256k1 univalue
@@ -148,6 +148,7 @@ BITCOIN_CORE_H = \
fs.h \
httprpc.h \
httpserver.h \
+ i2p.h \
index/base.h \
index/blockfilterindex.h \
index/disktxpos.h \
@@ -181,7 +182,6 @@ BITCOIN_CORE_H = \
node/ui_interface.h \
node/utxo_snapshot.h \
noui.h \
- optional.h \
outputtype.h \
policy/feerate.h \
policy/fees.h \
@@ -224,29 +224,34 @@ BITCOIN_CORE_H = \
timedata.h \
torcontrol.h \
txdb.h \
- txrequest.h \
txmempool.h \
+ txorphanage.h \
+ txrequest.h \
undo.h \
util/asmap.h \
util/bip32.h \
util/bytevectorhash.h \
util/check.h \
+ util/epochguard.h \
util/error.h \
util/fees.h \
+ util/getuniquepath.h \
util/golombrice.h \
util/hasher.h \
util/macros.h \
- util/memory.h \
util/message.h \
util/moneystr.h \
util/rbf.h \
+ util/readwritefile.h \
util/ref.h \
util/settings.h \
+ util/sock.h \
util/spanparsing.h \
util/string.h \
util/system.h \
util/threadnames.h \
util/time.h \
+ util/tokenpipe.h \
util/trace.h \
util/translation.h \
util/ui_change_type.h \
@@ -263,10 +268,13 @@ BITCOIN_CORE_H = \
wallet/crypter.h \
wallet/db.h \
wallet/dump.h \
+ wallet/external_signer.h \
+ wallet/external_signer_scriptpubkeyman.h \
wallet/feebumper.h \
wallet/fees.h \
wallet/ismine.h \
wallet/load.h \
+ wallet/rpcsigner.h \
wallet/rpcwallet.h \
wallet/salvage.h \
wallet/scriptpubkeyman.h \
@@ -294,7 +302,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# Contains code accessing mempool and chain state that is meant to be separated
# from wallet and gui code (see node/README.md). Shared code should go in
# libbitcoin_common or libbitcoin_util libraries, instead.
-libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
@@ -308,6 +316,7 @@ libbitcoin_server_a_SOURCES = \
flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
+ i2p.cpp \
index/base.cpp \
index/blockfilterindex.cpp \
index/txindex.cpp \
@@ -341,8 +350,9 @@ libbitcoin_server_a_SOURCES = \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
- txrequest.cpp \
txmempool.cpp \
+ txorphanage.cpp \
+ txrequest.cpp \
validation.cpp \
validationinterface.cpp \
versionbits.cpp \
@@ -377,11 +387,14 @@ libbitcoin_wallet_a_SOURCES = \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/dump.cpp \
+ wallet/external_signer_scriptpubkeyman.cpp \
+ wallet/external_signer.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
wallet/interfaces.cpp \
wallet/load.cpp \
wallet/rpcdump.cpp \
+ wallet/rpcsigner.cpp \
wallet/rpcwallet.cpp \
wallet/scriptpubkeyman.cpp \
wallet/wallet.cpp \
@@ -539,7 +552,6 @@ libbitcoin_util_a_SOURCES = \
support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
- compat/glibc_sanity.cpp \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
@@ -556,17 +568,21 @@ libbitcoin_util_a_SOURCES = \
util/bytevectorhash.cpp \
util/error.cpp \
util/fees.cpp \
+ util/getuniquepath.cpp \
util/hasher.cpp \
+ util/sock.cpp \
util/system.cpp \
util/message.cpp \
util/moneystr.cpp \
util/rbf.cpp \
+ util/readwritefile.cpp \
util/settings.cpp \
util/threadnames.cpp \
util/spanparsing.cpp \
util/strencodings.cpp \
util/string.cpp \
util/time.cpp \
+ util/tokenpipe.cpp \
$(BITCOIN_CORE_H)
if USE_LIBEVENT
@@ -683,7 +699,7 @@ endif
bitcoin_util_SOURCES = bitcoin-util.cpp
bitcoin_util_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_util_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_util_SOURCES += bitcoin-util-res.rc
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 969f0ca411..59cfdb9839 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -164,6 +164,9 @@ BITCOIN_QT_H = \
qt/walletview.h \
qt/winshutdownmonitor.h
+RES_FONTS = \
+ qt/res/fonts/RobotoMono-Bold.ttf
+
RES_ICONS = \
qt/res/icons/add.png \
qt/res/icons/address-book.png \
@@ -290,7 +293,7 @@ qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
- $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_ANIMATION)
+ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION)
if TARGET_DARWIN
qt_libbitcoinqt_a_SOURCES += $(BITCOIN_MM)
endif
@@ -361,7 +364,7 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
@rm $(@D)/temp_$(<F)
-$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_ANIMATION)
+$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION)
@test -f $(RCC)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index e9f9b73abe..7ed0eafb14 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -2,9 +2,11 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-if ENABLE_FUZZ
+if ENABLE_FUZZ_BINARY
noinst_PROGRAMS += test/fuzz/fuzz
-else
+endif
+
+if !ENABLE_FUZZ
bin_PROGRAMS += test/test_bitcoin
endif
@@ -50,6 +52,14 @@ FUZZ_SUITE_LD_COMMON = \
$(EVENT_LIBS) \
$(EVENT_PTHREADS_LIBS)
+if USE_UPNP
+FUZZ_SUITE_LD_COMMON += $(MINIUPNPC_LIBS)
+endif
+
+if USE_NATPMP
+FUZZ_SUITE_LD_COMMON += $(NATPMP_LIBS)
+endif
+
# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
@@ -114,6 +124,7 @@ BITCOIN_TESTS =\
test/sighash_tests.cpp \
test/sigopcount_tests.cpp \
test/skiplist_tests.cpp \
+ test/sock_tests.cpp \
test/streams_tests.cpp \
test/sync_tests.cpp \
test/system_tests.cpp \
@@ -145,10 +156,16 @@ BITCOIN_TESTS += \
wallet/test/ismine_tests.cpp \
wallet/test/scriptpubkeyman_tests.cpp
+FUZZ_SUITE_LD_COMMON +=\
+ $(LIBBITCOIN_WALLET) \
+ $(SQLITE_LIBS) \
+ $(BDB_LIBS)
+
if USE_BDB
BITCOIN_TESTS += wallet/test/db_tests.cpp
endif
+
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
@@ -172,12 +189,12 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
+FUZZ_SUITE_LD_COMMON += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
-if ENABLE_FUZZ
-
FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
+if ENABLE_FUZZ_BINARY
test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -235,6 +252,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/net.cpp \
test/fuzz/net_permissions.cpp \
test/fuzz/netaddress.cpp \
+ test/fuzz/netbase_dns_lookup.cpp \
test/fuzz/node_eviction.cpp \
test/fuzz/p2p_transport_deserializer.cpp \
test/fuzz/parse_hd_keypath.cpp \
@@ -268,18 +286,21 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp \
test/fuzz/signature_checker.cpp \
test/fuzz/signet.cpp \
+ test/fuzz/socks5.cpp \
test/fuzz/span.cpp \
test/fuzz/spanparsing.cpp \
test/fuzz/string.cpp \
test/fuzz/strprintf.cpp \
test/fuzz/system.cpp \
test/fuzz/timedata.cpp \
+ test/fuzz/torcontrol.cpp \
test/fuzz/transaction.cpp \
test/fuzz/tx_in.cpp \
test/fuzz/tx_out.cpp \
- test/fuzz/txrequest.cpp
-
-endif # ENABLE_FUZZ
+ test/fuzz/txrequest.cpp \
+ test/fuzz/validation_load_mempool.cpp \
+ test/fuzz/versionbits.cpp
+endif # ENABLE_FUZZ_BINARY
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include
index 75fe68fcd1..2d772f2fca 100644
--- a/src/Makefile.test_fuzz.include
+++ b/src/Makefile.test_fuzz.include
@@ -12,7 +12,7 @@ TEST_FUZZ_H = \
test/fuzz/FuzzedDataProvider.h \
test/fuzz/util.h
-libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_fuzz_a_SOURCES = \
test/fuzz/fuzz.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 0621da8ddf..f7f393ccac 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -12,13 +12,14 @@ TEST_UTIL_H = \
test/util/logging.h \
test/util/mining.h \
test/util/net.h \
+ test/util/script.h \
test/util/setup_common.h \
test/util/str.h \
test/util/transaction_utils.h \
test/util/validation.h \
test/util/wallet.h
-libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_util_a_SOURCES = \
test/util/blockfilter.cpp \
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 8f77ed35ce..0922c1c432 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -109,15 +109,15 @@ template <typename Data>
bool DeserializeFileDB(const fs::path& path, Data& data)
{
// open input file, and associate with CAutoFile
- FILE *file = fsbridge::fopen(path, "rb");
+ FILE* file = fsbridge::fopen(path, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
- if (filein.IsNull())
- return error("%s: Failed to open file %s", __func__, path.string());
-
+ if (filein.IsNull()) {
+ LogPrintf("Missing or invalid file %s\n", path.string());
+ return false;
+ }
return DeserializeDB(filein, data);
}
-
-}
+} // namespace
CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
{
diff --git a/src/addrman.h b/src/addrman.h
index 9ac67b7af6..92a5570953 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -335,22 +335,20 @@ public:
* * nNew
* * nTried
* * number of "new" buckets XOR 2**30
- * * all nNew addrinfos in vvNew
- * * all nTried addrinfos in vvTried
- * * for each bucket:
+ * * all new addresses (total count: nNew)
+ * * all tried addresses (total count: nTried)
+ * * for each new bucket:
* * number of elements
- * * for each element: index
+ * * for each element: index in the serialized "all new addresses"
+ * * asmap checksum
*
* 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
* as incompatible. This is necessary because it did not check the version number on
* deserialization.
*
- * Notice that vvTried, mapAddr and vVector are never encoded explicitly;
+ * vvNew, vvTried, mapInfo, mapAddr and vRandom are never encoded explicitly;
* they are instead reconstructed from the other information.
*
- * vvNew is serialized, but only used if ADDRMAN_UNKNOWN_BUCKET_COUNT didn't change,
- * otherwise it is reconstructed as well.
- *
* This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
* changes to the ADDRMAN_ parameters without breaking the on-disk structure.
*
@@ -413,13 +411,13 @@ public:
}
}
}
- // Store asmap version after bucket entries so that it
+ // Store asmap checksum after bucket entries so that it
// can be ignored by older clients for backward compatibility.
- uint256 asmap_version;
+ uint256 asmap_checksum;
if (m_asmap.size() != 0) {
- asmap_version = SerializeHash(m_asmap);
+ asmap_checksum = SerializeHash(m_asmap);
}
- s << asmap_version;
+ s << asmap_checksum;
}
template <typename Stream>
@@ -500,47 +498,63 @@ public:
nTried -= nLost;
// Store positions in the new table buckets to apply later (if possible).
- std::map<int, int> entryToBucket; // Represents which entry belonged to which bucket when serializing
-
- for (int bucket = 0; bucket < nUBuckets; bucket++) {
- int nSize = 0;
- s >> nSize;
- for (int n = 0; n < nSize; n++) {
- int nIndex = 0;
- s >> nIndex;
- if (nIndex >= 0 && nIndex < nNew) {
- entryToBucket[nIndex] = bucket;
+ // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
+ // so we store all bucket-entry_index pairs to iterate through later.
+ std::vector<std::pair<int, int>> bucket_entries;
+
+ for (int bucket = 0; bucket < nUBuckets; ++bucket) {
+ int num_entries{0};
+ s >> num_entries;
+ for (int n = 0; n < num_entries; ++n) {
+ int entry_index{0};
+ s >> entry_index;
+ if (entry_index >= 0 && entry_index < nNew) {
+ bucket_entries.emplace_back(bucket, entry_index);
}
}
}
- uint256 supplied_asmap_version;
+ // If the bucket count and asmap checksum haven't changed, then attempt
+ // to restore the entries to the buckets/positions they were in before
+ // serialization.
+ uint256 supplied_asmap_checksum;
if (m_asmap.size() != 0) {
- supplied_asmap_version = SerializeHash(m_asmap);
+ supplied_asmap_checksum = SerializeHash(m_asmap);
}
- uint256 serialized_asmap_version;
+ uint256 serialized_asmap_checksum;
if (format >= Format::V2_ASMAP) {
- s >> serialized_asmap_version;
+ s >> serialized_asmap_checksum;
}
+ const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
+ serialized_asmap_checksum == supplied_asmap_checksum};
- for (int n = 0; n < nNew; n++) {
- CAddrInfo &info = mapInfo[n];
- int bucket = entryToBucket[n];
- int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
- if (format >= Format::V2_ASMAP && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
- info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
+ if (!restore_bucketing) {
+ LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
+ }
+
+ for (auto bucket_entry : bucket_entries) {
+ int bucket{bucket_entry.first};
+ const int entry_index{bucket_entry.second};
+ CAddrInfo& info = mapInfo[entry_index];
+
+ // The entry shouldn't appear in more than
+ // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
+ // this bucket_entry.
+ if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
+
+ int bucket_position = info.GetBucketPosition(nKey, true, bucket);
+ if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
// Bucketing has not changed, using existing bucket positions for the new table
- vvNew[bucket][nUBucketPos] = n;
- info.nRefCount++;
+ vvNew[bucket][bucket_position] = entry_index;
+ ++info.nRefCount;
} else {
- // In case the new table data cannot be used (format unknown, bucket count wrong or new asmap),
+ // In case the new table data cannot be used (bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
- LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
bucket = info.GetNewBucket(nKey, m_asmap);
- nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
- if (vvNew[bucket][nUBucketPos] == -1) {
- vvNew[bucket][nUBucketPos] = n;
- info.nRefCount++;
+ bucket_position = info.GetBucketPosition(nKey, true, bucket);
+ if (vvNew[bucket][bucket_position] == -1) {
+ vvNew[bucket][bucket_position] = entry_index;
+ ++info.nRefCount;
}
}
}
diff --git a/src/banman.cpp b/src/banman.cpp
index 49bf6c43dc..3fe561ad01 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -28,7 +28,7 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
m_banned.size(), GetTimeMillis() - n_start);
} else {
- LogPrintf("Invalid or missing banlist.dat; recreating\n");
+ LogPrintf("Recreating banlist.dat\n");
SetBannedSetDirty(true); // force write
DumpBanlist();
}
diff --git a/src/base58.cpp b/src/base58.cpp
index 65e373283c..fb04673c5c 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -52,7 +52,7 @@ static const int8_t mapBase58[256] = {
int size = strlen(psz) * 733 /1000 + 1; // log(58) / log(256), rounded up.
std::vector<unsigned char> b256(size);
// Process the characters.
- static_assert(sizeof(mapBase58)/sizeof(mapBase58[0]) == 256, "mapBase58.size() should be 256"); // guarantee not out of range
+ static_assert(std::size(mapBase58) == 256, "mapBase58.size() should be 256"); // guarantee not out of range
while (*psz && !IsSpace(*psz)) {
// Decode base58 character
int carry = mapBase58[(uint8_t)*psz];
diff --git a/src/bech32.cpp b/src/bech32.cpp
index 1e0471f110..288b14e023 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Pieter Wuille
+// Copyright (c) 2017, 2021 Pieter Wuille
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,15 +7,18 @@
#include <assert.h>
+namespace bech32
+{
+
namespace
{
typedef std::vector<uint8_t> data;
-/** The Bech32 character set for encoding. */
+/** The Bech32 and Bech32m character set for encoding. */
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
-/** The Bech32 character set for decoding. */
+/** The Bech32 and Bech32m character set for decoding. */
const int8_t CHARSET_REV[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -27,6 +30,12 @@ const int8_t CHARSET_REV[128] = {
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
};
+/* Determine the final constant to use for the specified encoding. */
+uint32_t EncodingConstant(Encoding encoding) {
+ assert(encoding == Encoding::BECH32 || encoding == Encoding::BECH32M);
+ return encoding == Encoding::BECH32 ? 1 : 0x2bc830a3;
+}
+
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
* bits correspond to earlier values. */
@@ -111,21 +120,24 @@ data ExpandHRP(const std::string& hrp)
}
/** Verify a checksum. */
-bool VerifyChecksum(const std::string& hrp, const data& values)
+Encoding VerifyChecksum(const std::string& hrp, const data& values)
{
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
// list of values would result in a new valid list. For that reason, Bech32 requires the
- // resulting checksum to be 1 instead.
- return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
+ // resulting checksum to be 1 instead. In Bech32m, this constant was amended.
+ const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
+ if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
+ if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
+ return Encoding::INVALID;
}
/** Create a checksum. */
-data CreateChecksum(const std::string& hrp, const data& values)
+data CreateChecksum(Encoding encoding, const std::string& hrp, const data& values)
{
data enc = Cat(ExpandHRP(hrp), values);
enc.resize(enc.size() + 6); // Append 6 zeroes
- uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
+ uint32_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes.
data ret(6);
for (size_t i = 0; i < 6; ++i) {
// Convert the 5-bit groups in mod to checksum values.
@@ -136,16 +148,13 @@ data CreateChecksum(const std::string& hrp, const data& values)
} // namespace
-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
+/** Encode a Bech32 or Bech32m string. */
+std::string Encode(Encoding encoding, const std::string& hrp, const data& values) {
+ // First ensure that the HRP is all lowercase. BIP-173 and BIP350 require an encoder
+ // to return a lowercase Bech32/Bech32m 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 checksum = CreateChecksum(encoding, hrp, values);
data combined = Cat(values, checksum);
std::string ret = hrp + '1';
ret.reserve(ret.size() + combined.size());
@@ -155,8 +164,8 @@ std::string Encode(const std::string& hrp, const data& values) {
return ret;
}
-/** Decode a Bech32 string. */
-std::pair<std::string, data> Decode(const std::string& str) {
+/** Decode a Bech32 or Bech32m string. */
+DecodeResult Decode(const std::string& str) {
bool lower = false, upper = false;
for (size_t i = 0; i < str.size(); ++i) {
unsigned char c = str[i];
@@ -183,10 +192,9 @@ std::pair<std::string, data> Decode(const std::string& str) {
for (size_t i = 0; i < pos; ++i) {
hrp += LowerCase(str[i]);
}
- if (!VerifyChecksum(hrp, values)) {
- return {};
- }
- return {hrp, data(values.begin(), values.end() - 6)};
+ Encoding result = VerifyChecksum(hrp, values);
+ if (result == Encoding::INVALID) return {};
+ return {result, std::move(hrp), data(values.begin(), values.end() - 6)};
}
} // namespace bech32
diff --git a/src/bech32.h b/src/bech32.h
index fb39cd352b..e9450ccc2b 100644
--- a/src/bech32.h
+++ b/src/bech32.h
@@ -1,13 +1,14 @@
-// Copyright (c) 2017 Pieter Wuille
+// Copyright (c) 2017, 2021 Pieter Wuille
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-// Bech32 is a string encoding format used in newer address types.
-// The output consists of a human-readable part (alphanumeric), a
-// separator character (1), and a base32 data section, the last
-// 6 characters of which are a checksum.
+// Bech32 and Bech32m are string encoding formats used in newer
+// address types. The outputs consist of a human-readable part
+// (alphanumeric), a separator character (1), and a base32 data
+// section, the last 6 characters of which are a checksum. The
+// module is namespaced under bech32 for historical reasons.
//
-// For more information, see BIP 173.
+// For more information, see BIP 173 and BIP 350.
#ifndef BITCOIN_BECH32_H
#define BITCOIN_BECH32_H
@@ -19,11 +20,29 @@
namespace bech32
{
-/** 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);
+enum class Encoding {
+ INVALID, //!< Failed decoding
-/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
-std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
+ BECH32, //!< Bech32 encoding as defined in BIP173
+ BECH32M, //!< Bech32m encoding as defined in BIP350
+};
+
+/** Encode a Bech32 or Bech32m string. If hrp contains uppercase characters, this will cause an
+ * assertion error. Encoding must be one of BECH32 or BECH32M. */
+std::string Encode(Encoding encoding, const std::string& hrp, const std::vector<uint8_t>& values);
+
+struct DecodeResult
+{
+ Encoding encoding; //!< What encoding was detected in the result; Encoding::INVALID if failed.
+ std::string hrp; //!< The human readable part
+ std::vector<uint8_t> data; //!< The payload (excluding checksum)
+
+ DecodeResult() : encoding(Encoding::INVALID) {}
+ DecodeResult(Encoding enc, std::string&& h, std::vector<uint8_t>&& d) : encoding(enc), hrp(std::move(h)), data(std::move(d)) {}
+};
+
+/** Decode a Bech32 or Bech32m string. */
+DecodeResult Decode(const std::string& str);
} // namespace bech32
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index c74d8d51b3..8e10862a37 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -19,7 +19,7 @@ static void Bech32Encode(benchmark::Bench& bench)
tmp.reserve(1 + 32 * 8 / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
bench.batch(v.size()).unit("byte").run([&] {
- bech32::Encode("bc", tmp);
+ bech32::Encode(bech32::Encoding::BECH32, "bc", tmp);
});
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index af5a82f69f..67ab02a5b3 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -16,13 +16,7 @@
static void AssembleBlock(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
const std::vector<unsigned char> op_true{OP_TRUE};
CScriptWitness witness;
@@ -38,7 +32,7 @@ static void AssembleBlock(benchmark::Bench& bench)
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
for (size_t b{0}; b < NUM_BLOCKS; ++b) {
CMutableTransaction tx;
- tx.vin.push_back(MineBlock(test_setup.m_node, SCRIPT_PUB));
+ tx.vin.push_back(MineBlock(test_setup->m_node, SCRIPT_PUB));
tx.vin.back().scriptWitness = witness;
tx.vout.emplace_back(1337, SCRIPT_PUB);
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
@@ -48,14 +42,13 @@ static void AssembleBlock(benchmark::Bench& bench)
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
for (const auto& txr : txs) {
- TxValidationState state;
- bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
- assert(ret);
+ const MempoolAcceptResult res = ::AcceptToMemoryPool(::ChainstateActive(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
+ assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
bench.run([&] {
- PrepareBlock(test_setup.m_node, SCRIPT_PUB);
+ PrepareBlock(test_setup->m_node, SCRIPT_PUB);
});
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 3abfbfd784..80acdec044 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -17,7 +17,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(1);
tx.vout[0].nValue = nValue;
- wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
+ wtxs.push_back(std::make_unique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -49,7 +49,10 @@ static void CoinSelection(benchmark::Bench& bench)
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
- const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0, false);
+ const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34,
+ /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
@@ -73,7 +76,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
+ std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
set.emplace_back();
set.back().Insert(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
wtxn.emplace_back(std::move(wtx));
diff --git a/src/bench/data.cpp b/src/bench/data.cpp
index 0ae4c7cad4..481e372105 100644
--- a/src/bench/data.cpp
+++ b/src/bench/data.cpp
@@ -8,7 +8,7 @@ namespace benchmark {
namespace data {
#include <bench/data/block413567.raw.h>
-const std::vector<uint8_t> block413567{block413567_raw, block413567_raw + sizeof(block413567_raw) / sizeof(block413567_raw[0])};
+const std::vector<uint8_t> block413567{std::begin(block413567_raw), std::end(block413567_raw)};
} // namespace data
} // namespace benchmark
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 5745e4276c..25d1a2b56c 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -14,13 +14,7 @@
static void DuplicateInputs(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
const CScript SCRIPT_PUB{CScript(OP_TRUE)};
diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp
index db9a5661fd..4f49fba7b7 100644
--- a/src/bench/mempool_eviction.cpp
+++ b/src/bench/mempool_eviction.cpp
@@ -25,13 +25,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
// unique transactions for a more meaningful performance measurement.
static void MempoolEviction(benchmark::Bench& bench)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
CMutableTransaction tx1 = CMutableTransaction();
tx1.vin.resize(1);
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index 9b862b735c..f28768efc8 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -79,7 +79,7 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
- TestingSetup test_setup;
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index 45ed9f60dc..c8886a4c23 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -12,25 +12,49 @@
#include <univalue.h>
-static void BlockToJsonVerbose(benchmark::Bench& bench)
-{
- TestingSetup test_setup{};
+namespace {
+
+struct TestBlockAndIndex {
+ const std::unique_ptr<const TestingSetup> testing_setup{MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN)};
+ CBlock block{};
+ uint256 blockHash{};
+ CBlockIndex blockindex{};
- CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ TestBlockAndIndex()
+ {
+ CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
+ char a = '\0';
+ stream.write(&a, 1); // Prevent compaction
- CBlock block;
- stream >> block;
+ stream >> block;
- CBlockIndex blockindex;
- const uint256 blockHash = block.GetHash();
- blockindex.phashBlock = &blockHash;
- blockindex.nBits = 403014710;
+ blockHash = block.GetHash();
+ blockindex.phashBlock = &blockHash;
+ blockindex.nBits = 403014710;
+ }
+};
+} // namespace
+
+static void BlockToJsonVerbose(benchmark::Bench& bench)
+{
+ TestBlockAndIndex data;
bench.run([&] {
- (void)blockToJSON(block, &blockindex, &blockindex, /*verbose*/ true);
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ ankerl::nanobench::doNotOptimizeAway(univalue);
});
}
BENCHMARK(BlockToJsonVerbose);
+
+static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
+{
+ TestBlockAndIndex data;
+ auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, /*verbose*/ true);
+ bench.run([&] {
+ auto str = univalue.write();
+ ankerl::nanobench::doNotOptimizeAway(str);
+ });
+}
+
+BENCHMARK(BlockToJsonVerboseWrite);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index b385cec085..d7cc167885 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -5,39 +5,34 @@
#include <bench/bench.h>
#include <interfaces/chain.h>
#include <node/context.h>
-#include <optional.h>
#include <test/util/mining.h>
#include <test/util/setup_common.h>
#include <test/util/wallet.h>
#include <validationinterface.h>
#include <wallet/wallet.h>
+#include <optional>
+
static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine)
{
- TestingSetup test_setup{
- CBaseChainParams::REGTEST,
- /* extra_args */ {
- "-nodebuglogfile",
- "-nodebug",
- },
- };
+ const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
- CWallet wallet{test_setup.m_node.chain.get(), "", CreateMockWalletDatabase()};
+ CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
bool first_run;
if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
}
- auto handler = test_setup.m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
+ auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
- const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : nullopt};
+ const std::optional<std::string> address_mine{add_mine ? std::optional<std::string>{getnewaddress(wallet)} : std::nullopt};
if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY);
for (int i = 0; i < 100; ++i) {
- generatetoaddress(test_setup.m_node, address_mine.value_or(ADDRESS_WATCHONLY));
- generatetoaddress(test_setup.m_node, ADDRESS_WATCHONLY);
+ generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
+ generatetoaddress(test_setup->m_node, ADDRESS_WATCHONLY);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index fa41208a31..4f3d7a4ffe 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,7 +9,6 @@
#include <chainparamsbase.h>
#include <clientversion.h>
-#include <optional.h>
#include <rpc/client.h>
#include <rpc/mining.h>
#include <rpc/protocol.h>
@@ -24,6 +23,7 @@
#include <cmath>
#include <functional>
#include <memory>
+#include <optional>
#include <stdio.h>
#include <string>
#include <tuple>
@@ -300,9 +300,12 @@ class NetinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr int8_t UNKNOWN_NETWORK{-1};
- static constexpr uint8_t m_networks_size{3};
- const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion"}};
- std::array<std::array<uint16_t, m_networks_size + 2>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total/block-relay)
+ static constexpr uint8_t m_networks_size{4};
+ static constexpr uint8_t MAX_DETAIL_LEVEL{4};
+ const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion", "i2p"}};
+ std::array<std::array<uint16_t, m_networks_size + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
+ uint8_t m_block_relay_peers_count{0};
+ uint8_t m_manual_peers_count{0};
int8_t NetworkStringToId(const std::string& str) const
{
for (uint8_t i = 0; i < m_networks_size; ++i) {
@@ -310,18 +313,18 @@ private:
}
return UNKNOWN_NETWORK;
}
- uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
- bool m_is_help_requested{false}; //!< Optional user-supplied arg to print help documentation
+ uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
bool m_is_asmap_on{false};
size_t m_max_addr_length{0};
- size_t m_max_age_length{4};
+ size_t m_max_age_length{3};
size_t m_max_id_length{2};
struct Peer {
std::string addr;
std::string sub_version;
+ std::string conn_type;
std::string network;
std::string age;
double min_ping;
@@ -333,6 +336,8 @@ private:
int id;
int mapped_as;
int version;
+ bool is_bip152_hb_from;
+ bool is_bip152_hb_to;
bool is_block_relay;
bool is_outbound;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
@@ -351,61 +356,13 @@ private:
const double milliseconds{round(1000 * seconds)};
return milliseconds > 999999 ? "-" : ToString(milliseconds);
}
- const UniValue NetinfoHelp()
+ std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
{
- return std::string{
- "-netinfo level|\"help\" \n\n"
- "Returns a network peer connections dashboard with information from the remote server.\n"
- "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
- "An optional integer argument from 0 to 4 can be passed for different peers listings.\n"
- "Pass \"help\" to see this detailed help documentation.\n"
- "If more than one argument is passed, only the first one is read and parsed.\n"
- "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
- "Arguments:\n"
- "1. level (integer 0-4, optional) Specify the info level of the peers dashboard (default 0):\n"
- " 0 - Connection counts and local addresses\n"
- " 1 - Like 0 but with a peers listing (without address or version columns)\n"
- " 2 - Like 1 but with an address column\n"
- " 3 - Like 1 but with a version column\n"
- " 4 - Like 1 but with both address and version columns\n"
- "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
- "Result:\n\n"
- "* The peers listing in levels 1-4 displays all of the peers sorted by direction and minimum ping time:\n\n"
- " Column Description\n"
- " ------ -----------\n"
- " <-> Direction\n"
- " \"in\" - inbound connections are those initiated by the peer\n"
- " \"out\" - outbound connections are those initiated by us\n"
- " type Type of peer connection\n"
- " \"full\" - full relay, the default\n"
- " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
- " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n"
- " mping Minimum observed ping time, in milliseconds (ms)\n"
- " ping Last observed ping time, in milliseconds (ms)\n"
- " send Time since last message sent to the peer, in seconds\n"
- " recv Time since last message received from the peer, in seconds\n"
- " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
- " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
- " age Duration of connection to the peer, in minutes\n"
- " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
- " peer selection (only displayed if the -asmap config option is set)\n"
- " id Peer index, in increasing order of peer connections since node startup\n"
- " address IP address and port of the peer\n"
- " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
- "* The connection counts table displays the number of peers by direction, network, and the totals\n"
- " for each, as well as a column for block relay peers.\n\n"
- "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
- "Examples:\n\n"
- "Connection counts and local addresses only\n"
- "> bitcoin-cli -netinfo\n\n"
- "Compact peers listing\n"
- "> bitcoin-cli -netinfo 1\n\n"
- "Full dashboard\n"
- "> bitcoin-cli -netinfo 4\n\n"
- "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
- "> watch --interval 1 --no-title bitcoin-cli -netinfo 4\n\n"
- "See this help\n"
- "> bitcoin-cli -netinfo help\n"};
+ if (conn_type == "outbound-full-relay") return "full";
+ if (conn_type == "block-relay-only") return "block";
+ if (conn_type == "manual" || conn_type == "feeler") return conn_type;
+ if (conn_type == "addr-fetch") return "addr";
+ return "";
}
const int64_t m_time_now{GetSystemTimeInSeconds()};
@@ -418,11 +375,9 @@ public:
if (!args.empty()) {
uint8_t n{0};
if (ParseUInt8(args.at(0), &n)) {
- m_details_level = n;
- } else if (args.at(0) == "help") {
- m_is_help_requested = true;
+ m_details_level = std::min(n, MAX_DETAIL_LEVEL);
} else {
- throw std::runtime_error(strprintf("invalid -netinfo argument: %s", args.at(0)));
+ throw std::runtime_error(strprintf("invalid -netinfo argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
}
}
UniValue result(UniValue::VARR);
@@ -433,9 +388,6 @@ public:
UniValue ProcessReply(const UniValue& batch_in) override
{
- if (m_is_help_requested) {
- return JSONRPCReplyObj(NetinfoHelp(), NullUniValue, 1);
- }
const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
@@ -452,14 +404,13 @@ public:
if (network_id == UNKNOWN_NETWORK) continue;
const bool is_outbound{!peer["inbound"].get_bool()};
const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(m_networks_size); // in/out overall
++m_counts.at(2).at(network_id); // total by network
++m_counts.at(2).at(m_networks_size); // total overall
- if (is_block_relay) {
- ++m_counts.at(is_outbound).at(m_networks_size + 1); // in/out block-relay
- ++m_counts.at(2).at(m_networks_size + 1); // total block-relay
- }
+ if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
+ if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
// Push data for this peer to the peers vector.
const int peer_id{peer["id"].get_int()};
@@ -475,7 +426,9 @@ public:
const std::string addr{peer["addr"].get_str()};
const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)};
const std::string sub_version{peer["subver"].get_str()};
- m_peers.push_back({addr, sub_version, network, age, min_ping, ping, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_block_relay, is_outbound});
+ const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
+ const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_age_length = std::max(age.length(), m_max_age_length);
m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
@@ -489,15 +442,15 @@ public:
// Report detailed peer connections list sorted by direction and minimum ping time.
if (DetailsRequested() && !m_peers.empty()) {
std::sort(m_peers.begin(), m_peers.end());
- result += strprintf("<-> relay net mping ping send recv txn blk %*s ", m_max_age_length, "age");
+ result += strprintf("<-> type net mping ping send recv txn blk hb %*s ", m_max_age_length, "age");
if (m_is_asmap_on) result += " asmap ";
result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
for (const Peer& peer : m_peers) {
std::string version{ToString(peer.version) + peer.sub_version};
result += strprintf(
- "%3s %5s %5s%7s%7s%5s%5s%5s%5s %*s%*i %*s %-*s%s\n",
+ "%3s %6s %5s%7s%7s%5s%5s%5s%5s %2s %*s%*i %*s %-*s%s\n",
peer.is_outbound ? "out" : "in",
- peer.is_block_relay ? "block" : "full",
+ ConnectionTypeForNetinfo(peer.conn_type),
peer.network,
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
@@ -505,6 +458,7 @@ public:
peer.last_recv == 0 ? "" : ToString(m_time_now - peer.last_recv),
peer.last_trxn == 0 ? "" : ToString((m_time_now - peer.last_trxn) / 60),
peer.last_blck == 0 ? "" : ToString((m_time_now - peer.last_blck) / 60),
+ strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_age_length, // variable spacing
peer.age,
m_is_asmap_on ? 7 : 0, // variable spacing
@@ -515,18 +469,28 @@ public:
IsAddressSelected() ? peer.addr : "",
IsVersionSelected() && version != "0" ? version : "");
}
- result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
+ result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
}
// Report peer connection totals by type.
- result += " ipv4 ipv6 onion total block-relay\n";
+ result += " ipv4 ipv6 onion";
+ const bool any_i2p_peers = m_counts.at(2).at(3); // false if total i2p peers count is 0, otherwise true
+ if (any_i2p_peers) result += " i2p";
+ result += " total block";
+ if (m_manual_peers_count) result += " manual";
const std::array<std::string, 3> rows{{"in", "out", "total"}};
- for (uint8_t i = 0; i < m_networks_size; ++i) {
- result += strprintf("%-5s %5i %5i %5i %5i %5i\n", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2), m_counts.at(i).at(m_networks_size), m_counts.at(i).at(m_networks_size + 1));
+ for (uint8_t i = 0; i < 3; ++i) {
+ result += strprintf("\n%-5s %5i %5i %5i", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2)); // ipv4/ipv6/onion peers counts
+ if (any_i2p_peers) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
+ result += strprintf(" %5i", m_counts.at(i).at(m_networks_size)); // total peers count
+ if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
+ result += strprintf(" %5i", m_block_relay_peers_count);
+ if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count);
+ }
}
// Report local addresses, ports, and scores.
- result += "\nLocal addresses";
+ result += "\n\nLocal addresses";
const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
if (local_addrs.empty()) {
result += ": n/a\n";
@@ -542,6 +506,67 @@ public:
return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
}
+
+ const std::string m_help_doc{
+ "-netinfo level|\"help\" \n\n"
+ "Returns a network peer connections dashboard with information from the remote server.\n"
+ "This human-readable interface will change regularly and is not intended to be a stable API.\n"
+ "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
+ + strprintf("An optional integer argument from 0 to %d can be passed for different peers listings; %d to 255 are parsed as %d.\n", MAX_DETAIL_LEVEL, MAX_DETAIL_LEVEL, MAX_DETAIL_LEVEL) +
+ "Pass \"help\" to see this detailed help documentation.\n"
+ "If more than one argument is passed, only the first one is read and parsed.\n"
+ "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
+ "Arguments:\n"
+ + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", MAX_DETAIL_LEVEL) +
+ " 0 - Connection counts and local addresses\n"
+ " 1 - Like 0 but with a peers listing (without address or version columns)\n"
+ " 2 - Like 1 but with an address column\n"
+ " 3 - Like 1 but with a version column\n"
+ " 4 - Like 1 but with both address and version columns\n"
+ "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
+ "Result:\n\n"
+ + strprintf("* The peers listing in levels 1-%d displays all of the peers sorted by direction and minimum ping time:\n\n", MAX_DETAIL_LEVEL) +
+ " Column Description\n"
+ " ------ -----------\n"
+ " <-> Direction\n"
+ " \"in\" - inbound connections are those initiated by the peer\n"
+ " \"out\" - outbound connections are those initiated by us\n"
+ " type Type of peer connection\n"
+ " \"full\" - full relay, the default\n"
+ " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
+ " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
+ " \"feeler\" - short-lived connection for testing addresses\n"
+ " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
+ " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n"
+ " mping Minimum observed ping time, in milliseconds (ms)\n"
+ " ping Last observed ping time, in milliseconds (ms)\n"
+ " send Time since last message sent to the peer, in seconds\n"
+ " recv Time since last message received from the peer, in seconds\n"
+ " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
+ " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
+ " hb High-bandwidth BIP152 compact block relay\n"
+ " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
+ " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
+ " age Duration of connection to the peer, in minutes\n"
+ " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
+ " peer selection (only displayed if the -asmap config option is set)\n"
+ " id Peer index, in increasing order of peer connections since node startup\n"
+ " address IP address and port of the peer\n"
+ " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
+ "* The connection counts table displays the number of peers by direction, network, and the totals\n"
+ " for each, as well as two special outbound columns for block relay peers and manual peers.\n\n"
+ "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
+ "Examples:\n\n"
+ "Connection counts and local addresses only\n"
+ "> bitcoin-cli -netinfo\n\n"
+ "Compact peers listing\n"
+ "> bitcoin-cli -netinfo 1\n\n"
+ "Full dashboard\n"
+ + strprintf("> bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
+ "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
+ + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
+ "See this help\n"
+ "> bitcoin-cli -netinfo help\n"};
};
/** Process RPC generatetoaddress request. */
@@ -586,16 +611,16 @@ public:
}
};
-static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
+static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
{
std::string host;
// In preference order, we choose the following for the port:
// 1. -rpcport
// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
// 3. default port for chain
- int port = BaseParams().RPCPort();
+ uint16_t port{BaseParams().RPCPort()};
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
- port = gArgs.GetArg("-rpcport", port);
+ port = static_cast<uint16_t>(gArgs.GetArg("-rpcport", port));
// Obtain event base
raii_event_base base = obtain_event_base();
@@ -683,6 +708,8 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
} else {
throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
}
+ } else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
+ throw std::runtime_error(strprintf("Server response: %s", response.body));
} else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
else if (response.body.empty())
@@ -708,7 +735,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
* @returns the RPC response as a UniValue object.
* @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup.
*/
-static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
+static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
{
UniValue response(UniValue::VOBJ);
// Execute and handle connection failures with -rpcwait.
@@ -792,7 +819,7 @@ static void GetWalletBalances(UniValue& result)
*/
static UniValue GetNewAddress()
{
- Optional<std::string> wallet_name{};
+ std::optional<std::string> wallet_name{};
if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
DefaultRequestHandler rh;
return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
@@ -873,6 +900,10 @@ static int CommandLineRPC(int argc, char *argv[])
if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-netinfo", false)) {
+ if (!args.empty() && args.at(0) == "help") {
+ tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
+ return 0;
+ }
rh.reset(new NetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-generate", false)) {
const UniValue getnewaddress{GetNewAddress()};
@@ -893,7 +924,7 @@ static int CommandLineRPC(int argc, char *argv[])
}
if (nRet == 0) {
// Perform RPC call
- Optional<std::string> wallet_name{};
+ std::optional<std::string> wallet_name{};
if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 3e8e5fc7bc..b84d909b07 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -33,51 +33,52 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("dump", "Print out all of the wallet key-value records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("createfromdump", "Create new wallet file from dumped records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ argsman.AddCommand("info", "Get wallet info", OptionsCategory::COMMANDS);
+ argsman.AddCommand("create", "Create new wallet file", OptionsCategory::COMMANDS);
+ argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", OptionsCategory::COMMANDS);
+ argsman.AddCommand("dump", "Print out all of the wallet key-value records", OptionsCategory::COMMANDS);
+ argsman.AddCommand("createfromdump", "Create new wallet file from dumped records", OptionsCategory::COMMANDS);
}
-static bool WalletAppInit(int argc, char* argv[])
+static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
{
- SetupWalletToolArgs(gArgs);
+ SetupWalletToolArgs(args);
std::string error_message;
- if (!gArgs.ParseParameters(argc, argv, error_message)) {
+ if (!args.ParseParameters(argc, argv, error_message)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message);
return false;
}
- if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (argc < 2 || HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n";
- if (!gArgs.IsArgSet("-version")) {
- strUsage += "\n"
- "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n"
- "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n"
- "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n"
- "Usage:\n"
- " bitcoin-wallet [options] <command>\n";
- strUsage += "\n" + gArgs.GetHelpMessage();
- }
+ if (!args.IsArgSet("-version")) {
+ strUsage += "\n"
+ "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n"
+ "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n"
+ "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n"
+ "Usage:\n"
+ " bitcoin-wallet [options] <command>\n";
+ strUsage += "\n" + args.GetHelpMessage();
+ }
tfm::format(std::cout, "%s", strUsage);
return false;
}
// check for printtoconsole, allow -debug
- LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
+ LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", args.GetBoolArg("-debug", false));
if (!CheckDataDirOption()) {
- tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""));
return false;
}
// Check for chain settings (Params() calls are only valid after this clause)
- SelectParams(gArgs.GetChainName());
+ SelectParams(args.GetChainName());
return true;
}
int main(int argc, char* argv[])
{
+ ArgsManager& args = gArgs;
#ifdef WIN32
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
@@ -85,7 +86,7 @@ int main(int argc, char* argv[])
SetupEnvironment();
RandomInit();
try {
- if (!WalletAppInit(argc, argv)) return EXIT_FAILURE;
+ if (!WalletAppInit(args, argc, argv)) return EXIT_FAILURE;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "WalletAppInit()");
return EXIT_FAILURE;
@@ -94,33 +95,19 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
- std::string method {};
- for(int i = 1; i < argc; ++i) {
- if (!IsSwitchChar(argv[i][0])) {
- if (!method.empty()) {
- tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method, argv[i]);
- return EXIT_FAILURE;
- }
- method = argv[i];
- }
- }
-
- if (method.empty()) {
+ const auto command = args.GetCommand();
+ if (!command) {
tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
return EXIT_FAILURE;
}
-
- // A name must be provided when creating a file
- if (method == "create" && !gArgs.IsArgSet("-wallet")) {
- tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
+ if (command->args.size() != 0) {
+ tfm::format(std::cerr, "Error: Additional arguments provided (%s). Methods do not take arguments. Please refer to `-help`.\n", Join(command->args, ", "));
return EXIT_FAILURE;
}
- std::string name = gArgs.GetArg("-wallet", "");
-
ECCVerifyHandle globalVerifyHandle;
ECC_Start();
- if (!WalletTool::ExecuteWalletToolFunc(gArgs, method, name)) {
+ if (!WalletTool::ExecuteWalletToolFunc(args, command->command)) {
return EXIT_FAILURE;
}
ECC_Stop();
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b7bcb534ef..1b4ca3e9a8 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -20,14 +20,89 @@
#include <util/strencodings.h>
#include <util/system.h>
#include <util/threadnames.h>
+#include <util/tokenpipe.h>
#include <util/translation.h>
#include <util/url.h>
#include <functional>
+#include <optional>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
UrlDecodeFn* const URL_DECODE = urlDecode;
+#if HAVE_DECL_FORK
+
+/** Custom implementation of daemon(). This implements the same order of operations as glibc.
+ * Opens a pipe to the child process to be able to wait for an event to occur.
+ *
+ * @returns 0 if successful, and in child process.
+ * >0 if successful, and in parent process.
+ * -1 in case of error (in parent process).
+ *
+ * In case of success, endpoint will be one end of a pipe from the child to parent process,
+ * which can be used with TokenWrite (in the child) or TokenRead (in the parent).
+ */
+int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
+{
+ // communication pipe with child process
+ std::optional<TokenPipe> umbilical = TokenPipe::Make();
+ if (!umbilical) {
+ return -1; // pipe or pipe2 failed.
+ }
+
+ int pid = fork();
+ if (pid < 0) {
+ return -1; // fork failed.
+ }
+ if (pid != 0) {
+ // Parent process gets read end, closes write end.
+ endpoint = umbilical->TakeReadEnd();
+ umbilical->TakeWriteEnd().Close();
+
+ int status = endpoint.TokenRead();
+ if (status != 0) { // Something went wrong while setting up child process.
+ endpoint.Close();
+ return -1;
+ }
+
+ return pid;
+ }
+ // Child process gets write end, closes read end.
+ endpoint = umbilical->TakeWriteEnd();
+ umbilical->TakeReadEnd().Close();
+
+#if HAVE_DECL_SETSID
+ if (setsid() < 0) {
+ exit(1); // setsid failed.
+ }
+#endif
+
+ if (!nochdir) {
+ if (chdir("/") != 0) {
+ exit(1); // chdir failed.
+ }
+ }
+ if (!noclose) {
+ // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
+ // from terminal.
+ int fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0;
+ // Don't close if fd<=2 to try to handle the case where the program was invoked without any file descriptors open.
+ if (fd > 2) close(fd);
+ if (err) {
+ exit(1); // dup2 failed.
+ }
+ } else {
+ exit(1); // open /dev/null failed.
+ }
+ }
+ endpoint.TokenWrite(0); // Success
+ return 0;
+}
+
+#endif
+
static bool AppInit(int argc, char* argv[])
{
NodeContext node;
@@ -59,6 +134,14 @@ static bool AppInit(int argc, char* argv[])
return true;
}
+#if HAVE_DECL_FORK
+ // Communication with parent after daemonizing. This is used for signalling in the following ways:
+ // - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
+ // that the parent process can quit, and whether it was successful/unsuccessful.
+ // - an unexpected shutdown of the child process creates an unexpected end of stream at the parent
+ // end, which is interpreted as failure to start.
+ TokenPipeEnd daemon_ep;
+#endif
util::Ref context{node};
try
{
@@ -105,24 +188,34 @@ static bool AppInit(int argc, char* argv[])
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (args.GetBoolArg("-daemon", false)) {
-#if HAVE_DECL_DAEMON
-#if defined(MAC_OSX)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
+ if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
+#if HAVE_DECL_FORK
tfm::format(std::cout, PACKAGE_NAME " starting\n");
// Daemonize
- if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
- return InitError(Untranslated(strprintf("daemon() failed: %s\n", strerror(errno))));
+ switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0)
+ case 0: // Child: continue.
+ // If -daemonwait is not enabled, immediately send a success token the parent.
+ if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
+ daemon_ep.TokenWrite(1);
+ daemon_ep.Close();
+ }
+ break;
+ case -1: // Error happened.
+ return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", strerror(errno))));
+ default: { // Parent: wait and exit.
+ int token = daemon_ep.TokenRead();
+ if (token) { // Success
+ exit(EXIT_SUCCESS);
+ } else { // fRet = false or token read error (premature exit).
+ tfm::format(std::cerr, "Error during initializaton - check debug.log for details\n");
+ exit(EXIT_FAILURE);
+ }
+ }
}
-#if defined(MAC_OSX)
-#pragma GCC diagnostic pop
-#endif
#else
return InitError(Untranslated("-daemon is not supported on this operating system\n"));
-#endif // HAVE_DECL_DAEMON
+#endif // HAVE_DECL_FORK
}
// Lock data directory after daemonization
if (!AppInitLockDataDirectory())
@@ -138,6 +231,13 @@ static bool AppInit(int argc, char* argv[])
PrintExceptionContinue(nullptr, "AppInit()");
}
+#if HAVE_DECL_FORK
+ if (daemon_ep.IsOpen()) {
+ // Signal initialization status to parent, then close pipe.
+ daemon_ep.TokenWrite(fRet);
+ daemon_ep.Close();
+ }
+#endif
if (fRet) {
WaitForShutdown();
}
diff --git a/src/chain.h b/src/chain.h
index 43e8a39f36..04a5db5a17 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -163,14 +163,27 @@ public:
//! Number of transactions in this block.
//! Note: in a potential headers-first mode, this number cannot be relied upon
+ //! Note: this value is faked during UTXO snapshot load to ensure that
+ //! LoadBlockIndex() will load index entries for blocks that we lack data for.
+ //! @sa ActivateSnapshot
unsigned int nTx{0};
//! (memory only) Number of transactions in the chain up to and including this block.
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
//! Change to 64-bit type when necessary; won't happen before 2030
+ //!
+ //! Note: this value is faked during use of a UTXO snapshot because we don't
+ //! have the underlying block data available during snapshot load.
+ //! @sa AssumeutxoData
+ //! @sa ActivateSnapshot
unsigned int nChainTx{0};
//! Verification status of this block. See enum BlockStatus
+ //!
+ //! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot
+ //! load to avoid the block index being spuriously rewound.
+ //! @sa RewindBlockIndex
+ //! @sa ActivateSnapshot
uint32_t nStatus{0};
//! block header
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 88cf5ef0a8..16efffa6f0 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -8,9 +8,7 @@
#include <chainparamsseeds.h>
#include <consensus/merkle.h>
#include <hash.h> // for signet block challenge hash
-#include <tinyformat.h>
#include <util/system.h>
-#include <util/strencodings.h>
#include <versionbitsinfo.h>
#include <assert.h>
@@ -136,7 +134,7 @@ public:
bech32_hrp = "bc";
- vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
+ vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_main), std::end(pnSeed6_main));
fDefaultConsistencyChecks = false;
fRequireStandard = true;
@@ -161,6 +159,10 @@ public:
}
};
+ m_assumeutxo_data = MapAssumeutxo{
+ // TODO to be specified in a future patch.
+ };
+
chainTxData = ChainTxData{
// Data from RPC: getchaintxstats 4096 0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72
/* nTime */ 1603995752,
@@ -237,7 +239,7 @@ public:
bech32_hrp = "tb";
- vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
+ vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_test), std::end(pnSeed6_test));
fDefaultConsistencyChecks = false;
fRequireStandard = false;
@@ -250,6 +252,10 @@ public:
}
};
+ m_assumeutxo_data = MapAssumeutxo{
+ // TODO to be specified in a future patch.
+ };
+
chainTxData = ChainTxData{
// Data from RPC: getchaintxstats 4096 000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0
/* nTime */ 1603359686,
@@ -406,7 +412,7 @@ public:
pchMessageStart[2] = 0xb5;
pchMessageStart[3] = 0xda;
nDefaultPort = 18444;
- nPruneAfterHeight = 1000;
+ nPruneAfterHeight = gArgs.GetBoolArg("-fastprune", false) ? 100 : 1000;
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
@@ -431,6 +437,17 @@ public:
}
};
+ m_assumeutxo_data = MapAssumeutxo{
+ {
+ 110,
+ {uint256S("0x76fd7334ac7c1baf57ddc0c626f073a655a35d98a4258cd1382c8cc2b8392e10"), 110},
+ },
+ {
+ 210,
+ {uint256S("0x9c5ed99ef98544b34f8920b6d1802f72ac28ae6e2bd2bd4c316ff10c230df3f2"), 210},
+ },
+ };
+
chainTxData = ChainTxData{
0,
0,
@@ -526,3 +543,9 @@ void SelectParams(const std::string& network)
SelectBaseParams(network);
globalChainParams = CreateChainParams(gArgs, network);
}
+
+std::ostream& operator<<(std::ostream& o, const AssumeutxoData& aud)
+{
+ o << strprintf("AssumeutxoData(%s, %s)", aud.hash_serialized.ToString(), aud.nChainTx);
+ return o;
+}
diff --git a/src/chainparams.h b/src/chainparams.h
index d8b25c7220..013f075be6 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -31,6 +31,26 @@ struct CCheckpointData {
};
/**
+ * Holds configuration for use during UTXO snapshot load and validation. The contents
+ * here are security critical, since they dictate which UTXO snapshots are recognized
+ * as valid.
+ */
+struct AssumeutxoData {
+ //! The expected hash of the deserialized UTXO set.
+ const uint256 hash_serialized;
+
+ //! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex().
+ //!
+ //! We need to hardcode the value here because this is computed cumulatively using block data,
+ //! which we do not necessarily have at the time of snapshot load.
+ const unsigned int nChainTx;
+};
+
+std::ostream& operator<<(std::ostream& o, const AssumeutxoData& aud);
+
+using MapAssumeutxo = std::map<int, const AssumeutxoData>;
+
+/**
* Holds various statistics on transactions within a chain. Used to estimate
* verification progress during chain sync.
*
@@ -64,7 +84,7 @@ public:
const Consensus::Params& GetConsensus() const { return consensus; }
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
- int GetDefaultPort() const { return nDefaultPort; }
+ uint16_t GetDefaultPort() const { return nDefaultPort; }
const CBlock& GenesisBlock() const { return genesis; }
/** Default value for -checkmempool and -checkblockindex argument */
@@ -90,13 +110,18 @@ public:
const std::string& Bech32HRP() const { return bech32_hrp; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
+
+ //! Get allowed assumeutxo configuration.
+ //! @see ChainstateManager
+ const MapAssumeutxo& Assumeutxo() const { return m_assumeutxo_data; }
+
const ChainTxData& TxData() const { return chainTxData; }
protected:
CChainParams() {}
Consensus::Params consensus;
CMessageHeader::MessageStartChars pchMessageStart;
- int nDefaultPort;
+ uint16_t nDefaultPort;
uint64_t nPruneAfterHeight;
uint64_t m_assumed_blockchain_size;
uint64_t m_assumed_chain_state_size;
@@ -111,6 +136,7 @@ protected:
bool m_is_test_chain;
bool m_is_mockable_chain;
CCheckpointData checkpointData;
+ MapAssumeutxo m_assumeutxo_data;
ChainTxData chainTxData;
};
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 2c517b58f8..1631176477 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -7,7 +7,6 @@
#include <tinyformat.h>
#include <util/system.h>
-#include <util/memory.h>
#include <assert.h>
@@ -44,13 +43,13 @@ const CBaseChainParams& BaseParams()
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN) {
- return MakeUnique<CBaseChainParams>("", 8332, 8334);
+ return std::make_unique<CBaseChainParams>("", 8332, 8334);
} else if (chain == CBaseChainParams::TESTNET) {
- return MakeUnique<CBaseChainParams>("testnet3", 18332, 18334);
+ return std::make_unique<CBaseChainParams>("testnet3", 18332, 18334);
} else if (chain == CBaseChainParams::SIGNET) {
- return MakeUnique<CBaseChainParams>("signet", 38332, 38334);
+ return std::make_unique<CBaseChainParams>("signet", 38332, 38334);
} else if (chain == CBaseChainParams::REGTEST) {
- return MakeUnique<CBaseChainParams>("regtest", 18443, 18445);
+ return std::make_unique<CBaseChainParams>("regtest", 18443, 18445);
}
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
diff --git a/src/coins.cpp b/src/coins.cpp
index dd84e720e7..d52851cadd 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -97,6 +97,14 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
}
+void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
+ cachedCoinsUsage += coin.DynamicMemoryUsage();
+ cacheCoins.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(std::move(outpoint)),
+ std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
+}
+
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
bool fCoinbase = tx.IsCoinBase();
const uint256& txid = tx.GetHash();
diff --git a/src/coins.h b/src/coins.h
index d2eb42d8cf..feb441fd6a 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -20,6 +20,8 @@
#include <functional>
#include <unordered_map>
+class ChainstateManager;
+
/**
* A UTXO entry.
*
@@ -125,6 +127,7 @@ struct CCoinsCacheEntry
CCoinsCacheEntry() : flags(0) {}
explicit CCoinsCacheEntry(Coin&& coin_) : coin(std::move(coin_)), flags(0) {}
+ CCoinsCacheEntry(Coin&& coin_, unsigned char flag) : coin(std::move(coin_)), flags(flag) {}
};
typedef std::unordered_map<COutPoint, CCoinsCacheEntry, SaltedOutpointHasher> CCoinsMap;
@@ -263,6 +266,15 @@ public:
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
/**
+ * Emplace a coin into cacheCoins without performing any checks, marking
+ * the emplaced coin as dirty.
+ *
+ * NOT FOR GENERAL USE. Used only when loading coins from a UTXO snapshot.
+ * @sa ChainstateManager::PopulateAndValidateSnapshot()
+ */
+ void EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin);
+
+ /**
* Spend a coin. Pass moveto in order to get the deleted data.
* If no unspent output exists for the passed outpoint, this call
* has no effect.
diff --git a/src/compat.h b/src/compat.h
index dad14748a2..3449bc2661 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -44,6 +44,7 @@ typedef unsigned int SOCKET;
#define WSAEINVAL EINVAL
#define WSAEALREADY EALREADY
#define WSAEWOULDBLOCK EWOULDBLOCK
+#define WSAEAGAIN EAGAIN
#define WSAEMSGSIZE EMSGSIZE
#define WSAEINTR EINTR
#define WSAEINPROGRESS EINPROGRESS
@@ -51,6 +52,14 @@ typedef unsigned int SOCKET;
#define WSAENOTSOCK EBADF
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR -1
+#else
+#ifndef WSAEAGAIN
+#ifdef EAGAIN
+#define WSAEAGAIN EAGAIN
+#else
+#define WSAEAGAIN WSAEWOULDBLOCK
+#endif
+#endif
#endif
#ifdef WIN32
@@ -96,4 +105,14 @@ bool static inline IsSelectableSocket(const SOCKET& s) {
#endif
}
+// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+#endif
+
+// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
+#if !defined(MSG_DONTWAIT)
+#define MSG_DONTWAIT 0
+#endif
+
#endif // BITCOIN_COMPAT_H
diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h
index 301c2d914c..5f50cde3ff 100644
--- a/src/compat/assumptions.h
+++ b/src/compat/assumptions.h
@@ -17,16 +17,12 @@
# error "Bitcoin cannot be compiled without assertions."
#endif
-// Assumption: We assume a C++11 (ISO/IEC 14882:2011) compiler (minimum requirement).
-// Example(s): We assume the presence of C++11 features everywhere :-)
-// Note: MSVC does not report the expected __cplusplus value due to legacy
-// reasons.
-#if !defined(_MSC_VER)
-// ISO Standard C++11 [cpp.predefined]p1:
-// "The name __cplusplus is defined to the value 201103L when compiling a C++
+// Assumption: We assume a C++17 (ISO/IEC 14882:2017) compiler (minimum requirement).
+// Example(s): We assume the presence of C++17 features everywhere :-)
+// ISO Standard C++17 [cpp.predefined]p1:
+// "The name __cplusplus is defined to the value 201703L when compiling a C++
// translation unit."
-static_assert(__cplusplus >= 201103L, "C++11 standard assumed");
-#endif
+static_assert(__cplusplus >= 201703L, "C++17 standard assumed");
// Assumption: We assume the floating-point types to fulfill the requirements of
// IEC 559 (IEEE 754) standard.
diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp
index 8a51f310f7..ff581d4a9e 100644
--- a/src/compat/glibc_compat.cpp
+++ b/src/compat/glibc_compat.cpp
@@ -9,13 +9,6 @@
#include <cstddef>
#include <cstdint>
-// Prior to GLIBC_2.14, memcpy was aliased to memmove.
-extern "C" void* memmove(void* a, const void* b, size_t c);
-extern "C" void* memcpy(void* a, const void* b, size_t c)
-{
- return memmove(a, b, c);
-}
-
#if defined(__i386__) || defined(__arm__)
extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp
deleted file mode 100644
index 06d0dd6fba..0000000000
--- a/src/compat/glibc_sanity.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2009-2020 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 <cstddef>
-
-extern "C" void* memcpy(void* a, const void* b, size_t c);
-void* memcpy_int(void* a, const void* b, size_t c)
-{
- return memcpy(a, b, c);
-}
-
-namespace
-{
-// trigger: Use the memcpy_int wrapper which calls our internal memcpy.
-// A direct call to memcpy may be optimized away by the compiler.
-// test: Fill an array with a sequence of integers. memcpy to a new empty array.
-// Verify that the arrays are equal. Use an odd size to decrease the odds of
-// the call being optimized away.
-template <unsigned int T>
-bool sanity_test_memcpy()
-{
- unsigned int memcpy_test[T];
- unsigned int memcpy_verify[T] = {};
- for (unsigned int i = 0; i != T; ++i)
- memcpy_test[i] = i;
-
- memcpy_int(memcpy_verify, memcpy_test, sizeof(memcpy_test));
-
- for (unsigned int i = 0; i != T; ++i) {
- if (memcpy_verify[i] != i)
- return false;
- }
- return true;
-}
-} // namespace
-
-bool glibc_sanity_test()
-{
- return sanity_test_memcpy<1025>();
-}
diff --git a/src/compat/sanity.h b/src/compat/sanity.h
index 909c4f6da8..8efa416102 100644
--- a/src/compat/sanity.h
+++ b/src/compat/sanity.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_COMPAT_SANITY_H
#define BITCOIN_COMPAT_SANITY_H
-bool glibc_sanity_test();
bool glibcxx_sanity_test();
#endif // BITCOIN_COMPAT_SANITY_H
diff --git a/src/core_io.h b/src/core_io.h
index 5469a760ee..01340ae2ee 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -40,7 +40,7 @@ std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strN
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
-UniValue ValueFromAmount(const CAmount& amount);
+UniValue ValueFromAmount(const CAmount amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type);
diff --git a/src/core_write.cpp b/src/core_write.cpp
index a3902863d6..d3034ae25d 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -14,17 +14,20 @@
#include <undo.h>
#include <univalue.h>
#include <util/check.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
-UniValue ValueFromAmount(const CAmount& amount)
+UniValue ValueFromAmount(const CAmount amount)
{
- bool sign = amount < 0;
- int64_t n_abs = (sign ? -amount : amount);
- int64_t quotient = n_abs / COIN;
- int64_t remainder = n_abs % COIN;
+ static_assert(COIN > 1);
+ int64_t quotient = amount / COIN;
+ int64_t remainder = amount % COIN;
+ if (amount < 0) {
+ quotient = -quotient;
+ remainder = -remainder;
+ }
return UniValue(UniValue::VNUM,
- strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
+ strprintf("%s%d.%08d", amount < 0 ? "-" : "", quotient, remainder));
}
std::string FormatScript(const CScript& script)
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
index fbd14f9325..e5a0d4cb9c 100644
--- a/src/crypto/muhash.cpp
+++ b/src/crypto/muhash.cpp
@@ -17,7 +17,6 @@ namespace {
using limb_t = Num3072::limb_t;
using double_limb_t = Num3072::double_limb_t;
constexpr int LIMB_SIZE = Num3072::LIMB_SIZE;
-constexpr int LIMBS = Num3072::LIMBS;
/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */
constexpr limb_t MAX_PRIME_DIFF = 1103717;
@@ -123,7 +122,7 @@ inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul)
} // namespace
-/** Indicates wether d is larger than the modulus. */
+/** Indicates whether d is larger than the modulus. */
bool Num3072::IsOverflow() const
{
if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false;
@@ -276,18 +275,33 @@ void Num3072::Divide(const Num3072& a)
if (this->IsOverflow()) this->FullReduce();
}
-Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
- Num3072 out{};
- uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
- unsigned char tmp[BYTE_SIZE];
- ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, BYTE_SIZE);
+Num3072::Num3072(const unsigned char (&data)[BYTE_SIZE]) {
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ this->limbs[i] = ReadLE32(data + 4 * i);
+ } else if (sizeof(limb_t) == 8) {
+ this->limbs[i] = ReadLE64(data + 8 * i);
+ }
+ }
+}
+
+void Num3072::ToBytes(unsigned char (&out)[BYTE_SIZE]) {
for (int i = 0; i < LIMBS; ++i) {
if (sizeof(limb_t) == 4) {
- out.limbs[i] = ReadLE32(tmp + 4 * i);
+ WriteLE32(out + i * 4, this->limbs[i]);
} else if (sizeof(limb_t) == 8) {
- out.limbs[i] = ReadLE64(tmp + 8 * i);
+ WriteLE64(out + i * 8, this->limbs[i]);
}
}
+}
+
+Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
+ unsigned char tmp[Num3072::BYTE_SIZE];
+
+ uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
+ ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, Num3072::BYTE_SIZE);
+ Num3072 out{tmp};
+
return out;
}
@@ -301,14 +315,8 @@ void MuHash3072::Finalize(uint256& out) noexcept
m_numerator.Divide(m_denominator);
m_denominator.SetToOne(); // Needed to keep the MuHash object valid
- unsigned char data[384];
- for (int i = 0; i < LIMBS; ++i) {
- if (sizeof(limb_t) == 4) {
- WriteLE32(data + i * 4, m_numerator.limbs[i]);
- } else if (sizeof(limb_t) == 8) {
- WriteLE64(data + i * 8, m_numerator.limbs[i]);
- }
- }
+ unsigned char data[Num3072::BYTE_SIZE];
+ m_numerator.ToBytes(data);
out = (CHashWriter(SER_DISK, 0) << data).GetSHA256();
}
diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h
index 0c710007c4..c023a8b9d3 100644
--- a/src/crypto/muhash.h
+++ b/src/crypto/muhash.h
@@ -22,6 +22,7 @@ private:
Num3072 GetInverse() const;
public:
+ static constexpr size_t BYTE_SIZE = 384;
#ifdef HAVE___INT128
typedef unsigned __int128 double_limb_t;
@@ -48,8 +49,10 @@ public:
void Divide(const Num3072& a);
void SetToOne();
void Square();
+ void ToBytes(unsigned char (&out)[BYTE_SIZE]);
Num3072() { this->SetToOne(); };
+ Num3072(const unsigned char (&data)[BYTE_SIZE]);
SERIALIZE_METHODS(Num3072, obj)
{
@@ -78,7 +81,7 @@ public:
* arbitrary subset of the update operations, allowing them to be
* efficiently combined later.
*
- * Muhash does not support checking if an element is already part of the
+ * MuHash does not support checking if an element is already part of the
* set. That is why this class does not enforce the use of a set as the
* data it represents because there is no efficient way to do so.
* It is possible to add elements more than once and also to remove
@@ -91,8 +94,6 @@ public:
class MuHash3072
{
private:
- static constexpr size_t BYTE_SIZE = 384;
-
Num3072 m_numerator;
Num3072 m_denominator;
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 4543f098a1..bb06c95e7d 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -38,6 +38,7 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-paytxfee=<amt>",
"-rescan",
"-salvagewallet",
+ "-signer=<cmd>",
"-spendzeroconfchange",
"-txconfirmtarget=<n>",
"-wallet=<path>",
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
index 11cf357f3d..151f1a38f1 100644
--- a/src/flatfile.cpp
+++ b/src/flatfile.cpp
@@ -66,7 +66,7 @@ size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_
if (CheckDiskSpace(m_dir, inc_size)) {
FILE *file = Open(pos);
if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
+ LogPrint(BCLog::VALIDATION, "Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
AllocateFileRange(file, pos.nPos, inc_size);
fclose(file);
return inc_size;
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index cb8b220895..867ddb090e 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -301,7 +301,7 @@ bool StartHTTPRPC(const util::Ref& context)
}
struct event_base* eventBase = EventBase();
assert(eventBase);
- httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase);
+ httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase);
RPCSetTimerInterface(httpRPCTimerInterface.get());
return true;
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 0a8e58ab67..12395f5b24 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -262,7 +262,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
item.release(); /* if true, queue took ownership */
else {
LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
- item->req->WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Work queue depth exceeded");
+ item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded");
}
} else {
hreq->WriteReply(HTTP_NOT_FOUND);
@@ -290,8 +290,8 @@ static bool ThreadHTTP(struct event_base* base)
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http)
{
- int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
- std::vector<std::pair<std::string, uint16_t> > endpoints;
+ uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))};
+ std::vector<std::pair<std::string, uint16_t>> endpoints;
// Determine what addresses to bind to
if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
@@ -305,7 +305,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
}
} else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
- int port = http_port;
+ uint16_t port{http_port};
std::string host;
SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port));
diff --git a/src/i2p.cpp b/src/i2p.cpp
new file mode 100644
index 0000000000..d16c620d88
--- /dev/null
+++ b/src/i2p.cpp
@@ -0,0 +1,407 @@
+// Copyright (c) 2020-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <compat.h>
+#include <compat/endian.h>
+#include <crypto/sha256.h>
+#include <fs.h>
+#include <i2p.h>
+#include <logging.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <random.h>
+#include <util/strencodings.h>
+#include <tinyformat.h>
+#include <util/readwritefile.h>
+#include <util/sock.h>
+#include <util/spanparsing.h>
+#include <util/system.h>
+
+#include <chrono>
+#include <stdexcept>
+#include <string>
+
+namespace i2p {
+
+/**
+ * Swap Standard Base64 <-> I2P Base64.
+ * Standard Base64 uses `+` and `/` as last two characters of its alphabet.
+ * I2P Base64 uses `-` and `~` respectively.
+ * So it is easy to detect in which one is the input and convert to the other.
+ * @param[in] from Input to convert.
+ * @return converted `from`
+ */
+static std::string SwapBase64(const std::string& from)
+{
+ std::string to;
+ to.resize(from.size());
+ for (size_t i = 0; i < from.size(); ++i) {
+ switch (from[i]) {
+ case '-':
+ to[i] = '+';
+ break;
+ case '~':
+ to[i] = '/';
+ break;
+ case '+':
+ to[i] = '-';
+ break;
+ case '/':
+ to[i] = '~';
+ break;
+ default:
+ to[i] = from[i];
+ break;
+ }
+ }
+ return to;
+}
+
+/**
+ * Decode an I2P-style Base64 string.
+ * @param[in] i2p_b64 I2P-style Base64 string.
+ * @return decoded `i2p_b64`
+ * @throw std::runtime_error if decoding fails
+ */
+static Binary DecodeI2PBase64(const std::string& i2p_b64)
+{
+ const std::string& std_b64 = SwapBase64(i2p_b64);
+ bool invalid;
+ Binary decoded = DecodeBase64(std_b64.c_str(), &invalid);
+ if (invalid) {
+ throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
+ }
+ return decoded;
+}
+
+/**
+ * Derive the .b32.i2p address of an I2P destination (binary).
+ * @param[in] dest I2P destination.
+ * @return the address that corresponds to `dest`
+ * @throw std::runtime_error if conversion fails
+ */
+static CNetAddr DestBinToAddr(const Binary& dest)
+{
+ CSHA256 hasher;
+ hasher.Write(dest.data(), dest.size());
+ unsigned char hash[CSHA256::OUTPUT_SIZE];
+ hasher.Finalize(hash);
+
+ CNetAddr addr;
+ const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
+ if (!addr.SetSpecial(addr_str)) {
+ throw std::runtime_error(strprintf("Cannot parse I2P address: \"%s\"", addr_str));
+ }
+
+ return addr;
+}
+
+/**
+ * Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
+ * @param[in] dest I2P destination.
+ * @return the address that corresponds to `dest`
+ * @throw std::runtime_error if conversion fails
+ */
+static CNetAddr DestB64ToAddr(const std::string& dest)
+{
+ const Binary& decoded = DecodeI2PBase64(dest);
+ return DestBinToAddr(decoded);
+}
+
+namespace sam {
+
+Session::Session(const fs::path& private_key_file,
+ const CService& control_host,
+ CThreadInterrupt* interrupt)
+ : m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt)
+{
+}
+
+Session::~Session()
+{
+ LOCK(m_mutex);
+ Disconnect();
+}
+
+bool Session::Listen(Connection& conn)
+{
+ try {
+ LOCK(m_mutex);
+ CreateIfNotCreatedAlready();
+ conn.me = m_my_addr;
+ conn.sock = StreamAccept();
+ return true;
+ } catch (const std::runtime_error& e) {
+ Log("Error listening: %s", e.what());
+ CheckControlSock();
+ }
+ return false;
+}
+
+bool Session::Accept(Connection& conn)
+{
+ try {
+ while (!*m_interrupt) {
+ Sock::Event occurred;
+ conn.sock.Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred);
+
+ if ((occurred & Sock::RECV) == 0) {
+ // Timeout, no incoming connections within MAX_WAIT_FOR_IO.
+ continue;
+ }
+
+ const std::string& peer_dest =
+ conn.sock.RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
+
+ conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
+
+ return true;
+ }
+ } catch (const std::runtime_error& e) {
+ Log("Error accepting: %s", e.what());
+ CheckControlSock();
+ }
+ return false;
+}
+
+bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
+{
+ proxy_error = true;
+
+ std::string session_id;
+ Sock sock;
+ conn.peer = to;
+
+ try {
+ {
+ LOCK(m_mutex);
+ CreateIfNotCreatedAlready();
+ session_id = m_session_id;
+ conn.me = m_my_addr;
+ sock = Hello();
+ }
+
+ const Reply& lookup_reply =
+ SendRequestAndGetReply(sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
+
+ const std::string& dest = lookup_reply.Get("VALUE");
+
+ const Reply& connect_reply = SendRequestAndGetReply(
+ sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
+ false);
+
+ const std::string& result = connect_reply.Get("RESULT");
+
+ if (result == "OK") {
+ conn.sock = std::move(sock);
+ return true;
+ }
+
+ if (result == "INVALID_ID") {
+ LOCK(m_mutex);
+ Disconnect();
+ throw std::runtime_error("Invalid session id");
+ }
+
+ if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
+ proxy_error = false;
+ }
+
+ throw std::runtime_error(strprintf("\"%s\"", connect_reply.full));
+ } catch (const std::runtime_error& e) {
+ Log("Error connecting to %s: %s", to.ToString(), e.what());
+ CheckControlSock();
+ return false;
+ }
+}
+
+// Private methods
+
+std::string Session::Reply::Get(const std::string& key) const
+{
+ const auto& pos = keys.find(key);
+ if (pos == keys.end() || !pos->second.has_value()) {
+ throw std::runtime_error(
+ strprintf("Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
+ }
+ return pos->second.value();
+}
+
+template <typename... Args>
+void Session::Log(const std::string& fmt, const Args&... args) const
+{
+ LogPrint(BCLog::I2P, "I2P: %s\n", tfm::format(fmt, args...));
+}
+
+Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
+ const std::string& request,
+ bool check_result_ok) const
+{
+ sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);
+
+ Reply reply;
+
+ // Don't log the full "SESSION CREATE ..." because it contains our private key.
+ reply.request = request.substr(0, 14) == "SESSION CREATE" ? "SESSION CREATE ..." : request;
+
+ // It could take a few minutes for the I2P router to reply as it is querying the I2P network
+ // (when doing name lookup, for example). Notice: `RecvUntilTerminator()` is checking
+ // `m_interrupt` more often, so we would not be stuck here for long if `m_interrupt` is
+ // signaled.
+ static constexpr auto recv_timeout = 3min;
+
+ reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE);
+
+ for (const auto& kv : spanparsing::Split(reply.full, ' ')) {
+ const auto& pos = std::find(kv.begin(), kv.end(), '=');
+ if (pos != kv.end()) {
+ reply.keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
+ } else {
+ reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
+ }
+ }
+
+ if (check_result_ok && reply.Get("RESULT") != "OK") {
+ throw std::runtime_error(
+ strprintf("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
+ }
+
+ return reply;
+}
+
+Sock Session::Hello() const
+{
+ auto sock = CreateSock(m_control_host);
+
+ if (!sock) {
+ throw std::runtime_error("Cannot create socket");
+ }
+
+ if (!ConnectSocketDirectly(m_control_host, sock->Get(), nConnectTimeout, true)) {
+ throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
+ }
+
+ SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
+
+ return std::move(*sock);
+}
+
+void Session::CheckControlSock()
+{
+ LOCK(m_mutex);
+
+ std::string errmsg;
+ if (!m_control_sock.IsConnected(errmsg)) {
+ Log("Control socket error: %s", errmsg);
+ Disconnect();
+ }
+}
+
+void Session::DestGenerate(const Sock& sock)
+{
+ // https://geti2p.net/spec/common-structures#key-certificates
+ // "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and Destinations".
+ // Use "7" because i2pd <2.24.0 does not recognize the textual form.
+ const Reply& reply = SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
+
+ m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
+}
+
+void Session::GenerateAndSavePrivateKey(const Sock& sock)
+{
+ DestGenerate(sock);
+
+ // umask is set to 077 in init.cpp, which is ok (unless -sysperms is given)
+ if (!WriteBinaryFile(m_private_key_file,
+ std::string(m_private_key.begin(), m_private_key.end()))) {
+ throw std::runtime_error(
+ strprintf("Cannot save I2P private key to %s", m_private_key_file));
+ }
+}
+
+Binary Session::MyDestination() const
+{
+ // From https://geti2p.net/spec/common-structures#destination:
+ // "They are 387 bytes plus the certificate length specified at bytes 385-386, which may be
+ // non-zero"
+ static constexpr size_t DEST_LEN_BASE = 387;
+ static constexpr size_t CERT_LEN_POS = 385;
+
+ uint16_t cert_len;
+ memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
+ cert_len = be16toh(cert_len);
+
+ const size_t dest_len = DEST_LEN_BASE + cert_len;
+
+ return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
+}
+
+void Session::CreateIfNotCreatedAlready()
+{
+ std::string errmsg;
+ if (m_control_sock.IsConnected(errmsg)) {
+ return;
+ }
+
+ Log("Creating SAM session with %s", m_control_host.ToString());
+
+ Sock sock = Hello();
+
+ const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
+ if (read_ok) {
+ m_private_key.assign(data.begin(), data.end());
+ } else {
+ GenerateAndSavePrivateKey(sock);
+ }
+
+ const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
+ const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
+
+ SendRequestAndGetReply(sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
+ session_id, private_key_b64));
+
+ m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
+ m_session_id = session_id;
+ m_control_sock = std::move(sock);
+
+ LogPrintf("I2P: SAM session created: session id=%s, my address=%s\n", m_session_id,
+ m_my_addr.ToString());
+}
+
+Sock Session::StreamAccept()
+{
+ Sock sock = Hello();
+
+ const Reply& reply = SendRequestAndGetReply(
+ sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
+
+ const std::string& result = reply.Get("RESULT");
+
+ if (result == "OK") {
+ return sock;
+ }
+
+ if (result == "INVALID_ID") {
+ // If our session id is invalid, then force session re-creation on next usage.
+ Disconnect();
+ }
+
+ throw std::runtime_error(strprintf("\"%s\"", reply.full));
+}
+
+void Session::Disconnect()
+{
+ if (m_control_sock.Get() != INVALID_SOCKET) {
+ if (m_session_id.empty()) {
+ Log("Destroying incomplete session");
+ } else {
+ Log("Destroying session %s", m_session_id);
+ }
+ }
+ m_control_sock.Reset();
+ m_session_id.clear();
+}
+} // namespace sam
+} // namespace i2p
diff --git a/src/i2p.h b/src/i2p.h
new file mode 100644
index 0000000000..1ebe7d0329
--- /dev/null
+++ b/src/i2p.h
@@ -0,0 +1,268 @@
+// Copyright (c) 2020-2020 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_I2P_H
+#define BITCOIN_I2P_H
+
+#include <compat.h>
+#include <fs.h>
+#include <netaddress.h>
+#include <sync.h>
+#include <threadinterrupt.h>
+#include <util/sock.h>
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace i2p {
+
+/**
+ * Binary data.
+ */
+using Binary = std::vector<uint8_t>;
+
+/**
+ * An established connection with another peer.
+ */
+struct Connection {
+ /** Connected socket. */
+ Sock sock;
+
+ /** Our I2P address. */
+ CService me;
+
+ /** The peer's I2P address. */
+ CService peer;
+};
+
+namespace sam {
+
+/**
+ * The maximum size of an incoming message from the I2P SAM proxy (in bytes).
+ * Used to avoid a runaway proxy from sending us an "unlimited" amount of data without a terminator.
+ * The longest known message is ~1400 bytes, so this is high enough not to be triggered during
+ * normal operation, yet low enough to avoid a malicious proxy from filling our memory.
+ */
+static constexpr size_t MAX_MSG_SIZE{65536};
+
+/**
+ * I2P SAM session.
+ */
+class Session
+{
+public:
+ /**
+ * Construct a session. This will not initiate any IO, the session will be lazily created
+ * later when first used.
+ * @param[in] private_key_file Path to a private key file. If the file does not exist then the
+ * private key will be generated and saved into the file.
+ * @param[in] control_host Location of the SAM proxy.
+ * @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
+ * possible and executing methods throw an exception. Notice: only a pointer to the
+ * `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
+ * `Session` object.
+ */
+ Session(const fs::path& private_key_file,
+ const CService& control_host,
+ CThreadInterrupt* interrupt);
+
+ /**
+ * Destroy the session, closing the internally used sockets. The sockets that have been
+ * returned by `Accept()` or `Connect()` will not be closed, but they will be closed by
+ * the SAM proxy because the session is destroyed. So they will return an error next time
+ * we try to read or write to them.
+ */
+ ~Session();
+
+ /**
+ * Start listening for an incoming connection.
+ * @param[out] conn Upon successful completion the `sock` and `me` members will be set
+ * to the listening socket and address.
+ * @return true on success
+ */
+ bool Listen(Connection& conn);
+
+ /**
+ * Wait for and accept a new incoming connection.
+ * @param[in,out] conn The `sock` member is used for waiting and accepting. Upon successful
+ * completion the `peer` member will be set to the address of the incoming peer.
+ * @return true on success
+ */
+ bool Accept(Connection& conn);
+
+ /**
+ * Connect to an I2P peer.
+ * @param[in] to Peer to connect to.
+ * @param[out] conn Established connection. Only set if `true` is returned.
+ * @param[out] proxy_error If an error occurs due to proxy or general network failure, then
+ * this is set to `true`. If an error occurs due to unreachable peer (likely peer is down), then
+ * it is set to `false`. Only set if `false` is returned.
+ * @return true on success
+ */
+ bool Connect(const CService& to, Connection& conn, bool& proxy_error);
+
+private:
+ /**
+ * A reply from the SAM proxy.
+ */
+ struct Reply {
+ /**
+ * Full, unparsed reply.
+ */
+ std::string full;
+
+ /**
+ * Request, used for detailed error reporting.
+ */
+ std::string request;
+
+ /**
+ * A map of keywords from the parsed reply.
+ * For example, if the reply is "A=X B C=YZ", then the map will be
+ * keys["A"] == "X"
+ * keys["B"] == (empty std::optional)
+ * keys["C"] == "YZ"
+ */
+ std::unordered_map<std::string, std::optional<std::string>> keys;
+
+ /**
+ * Get the value of a given key.
+ * For example if the reply is "A=X B" then:
+ * Value("A") -> "X"
+ * Value("B") -> throws
+ * Value("C") -> throws
+ * @param[in] key Key whose value to retrieve
+ * @returns the key's value
+ * @throws std::runtime_error if the key is not present or if it has no value
+ */
+ std::string Get(const std::string& key) const;
+ };
+
+ /**
+ * Log a message in the `BCLog::I2P` category.
+ * @param[in] fmt printf(3)-like format string.
+ * @param[in] args printf(3)-like arguments that correspond to `fmt`.
+ */
+ template <typename... Args>
+ void Log(const std::string& fmt, const Args&... args) const;
+
+ /**
+ * Send request and get a reply from the SAM proxy.
+ * @param[in] sock A socket that is connected to the SAM proxy.
+ * @param[in] request Raw request to send, a newline terminator is appended to it.
+ * @param[in] check_result_ok If true then after receiving the reply a check is made
+ * whether it contains "RESULT=OK" and an exception is thrown if it does not.
+ * @throws std::runtime_error if an error occurs
+ */
+ Reply SendRequestAndGetReply(const Sock& sock,
+ const std::string& request,
+ bool check_result_ok = true) const;
+
+ /**
+ * Open a new connection to the SAM proxy.
+ * @return a connected socket
+ * @throws std::runtime_error if an error occurs
+ */
+ Sock Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Check the control socket for errors and possibly disconnect.
+ */
+ void CheckControlSock();
+
+ /**
+ * Generate a new destination with the SAM proxy and set `m_private_key` to it.
+ * @param[in] sock Socket to use for talking to the SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Generate a new destination with the SAM proxy, set `m_private_key` to it and save
+ * it on disk to `m_private_key_file`.
+ * @param[in] sock Socket to use for talking to the SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Derive own destination from `m_private_key`.
+ * @see https://geti2p.net/spec/common-structures#destination
+ * @return an I2P destination
+ */
+ Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Create the session if not already created. Reads the private key file and connects to the
+ * SAM proxy.
+ * @throws std::runtime_error if an error occurs
+ */
+ void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
+ * session id. Return the idle socket that is waiting for a peer to connect to us.
+ * @throws std::runtime_error if an error occurs
+ */
+ Sock StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * Destroy the session, closing the internally used sockets.
+ */
+ void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
+
+ /**
+ * The name of the file where this peer's private key is stored (in binary).
+ */
+ const fs::path m_private_key_file;
+
+ /**
+ * The host and port of the SAM control service.
+ */
+ const CService m_control_host;
+
+ /**
+ * Cease network activity when this is signaled.
+ */
+ CThreadInterrupt* const m_interrupt;
+
+ /**
+ * Mutex protecting the members that can be concurrently accessed.
+ */
+ mutable Mutex m_mutex;
+
+ /**
+ * The private key of this peer.
+ * @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3
+ */
+ Binary m_private_key GUARDED_BY(m_mutex);
+
+ /**
+ * SAM control socket.
+ * Used to connect to the I2P SAM service and create a session
+ * ("SESSION CREATE"). With the established session id we later open
+ * other connections to the SAM service to accept incoming I2P
+ * connections and make outgoing ones.
+ * See https://geti2p.net/en/docs/api/samv3
+ */
+ Sock m_control_sock GUARDED_BY(m_mutex);
+
+ /**
+ * Our .b32.i2p address.
+ * Derived from `m_private_key`.
+ */
+ CService m_my_addr GUARDED_BY(m_mutex);
+
+ /**
+ * SAM session id.
+ */
+ std::string m_session_id GUARDED_BY(m_mutex);
+};
+
+} // namespace sam
+} // namespace i2p
+
+#endif /* BITCOIN_I2P_H */
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3d3dda95b1..25644c3b41 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -65,6 +65,43 @@ bool BaseIndex::Init()
m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
}
m_synced = m_best_block_index.load() == ::ChainActive().Tip();
+ if (!m_synced) {
+ bool prune_violation = false;
+ if (!m_best_block_index) {
+ // index is not built yet
+ // make sure we have all block data back to the genesis
+ const CBlockIndex* block = ::ChainActive().Tip();
+ while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ block = block->pprev;
+ }
+ prune_violation = block != ::ChainActive().Genesis();
+ }
+ // in case the index has a best block set and is not fully synced
+ // check if we have the required blocks to continue building the index
+ else {
+ const CBlockIndex* block_to_test = m_best_block_index.load();
+ if (!ChainActive().Contains(block_to_test)) {
+ // if the bestblock is not part of the mainchain, find the fork
+ // and make sure we have all data down to the fork
+ block_to_test = ::ChainActive().FindFork(block_to_test);
+ }
+ const CBlockIndex* block = ::ChainActive().Tip();
+ prune_violation = true;
+ // check backwards from the tip if we have all block data until we reach the indexes bestblock
+ while (block_to_test && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ if (block_to_test == block) {
+ prune_violation = false;
+ break;
+ }
+ block = block->pprev;
+ }
+ }
+ if (prune_violation) {
+ // throw error and graceful shutdown if we can't build the index
+ FatalError("%s: %s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)", __func__, GetName());
+ return false;
+ }
+ }
return true;
}
@@ -177,6 +214,10 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti
assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
// In the case of a reorg, ensure persisted block locator is not stale.
+ // Pruning has a minimum of 288 blocks-to-keep and getting the index
+ // out of sync may be possible but a users fault.
+ // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
+ // throw and lead to a graceful shutdown
m_best_block_index = new_tip;
if (!Commit()) {
// If commit fails, revert the best block index to avoid corruption.
@@ -325,6 +366,6 @@ IndexSummary BaseIndex::GetSummary() const
IndexSummary summary{};
summary.name = GetName();
summary.synced = m_synced;
- summary.best_block_height = m_best_block_index.load()->nHeight;
+ summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0;
return summary;
}
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index 4f61bbeabd..32271fb7ab 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -102,8 +102,8 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
fs::create_directories(path);
m_name = filter_name + " block filter index";
- m_db = MakeUnique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
- m_filter_fileseq = MakeUnique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
+ m_db = std::make_unique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
+ m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
}
bool BlockFilterIndex::Init()
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 6398b7edc8..f41985c344 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -192,7 +192,7 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
}
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
- : m_db(MakeUnique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
+ : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
{}
TxIndex::~TxIndex() {}
diff --git a/src/init.cpp b/src/init.cpp
index 5d5c9b65b0..7d5420e3be 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -52,6 +52,7 @@
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
+#include <txorphanage.h>
#include <util/asmap.h>
#include <util/check.h>
#include <util/moneystr.h>
@@ -386,6 +387,7 @@ void SetupServerArgs(NodeContext& node)
#endif
argsman.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, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-fastprune", "Use smaller block files and lower minimum prune height for testing purposes", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
#if HAVE_SYSTEM
argsman.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
@@ -434,8 +436,9 @@ void SetupServerArgs(NodeContext& node)
argsman.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);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -445,7 +448,9 @@ void SetupServerArgs(NodeContext& node)
argsman.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);
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.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. Warning: if it is used with ipv4 or ipv6 but not onion and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with non-onion networks and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -454,8 +459,8 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.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);
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
- argsman.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.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);
+ argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
@@ -531,6 +536,7 @@ void SetupServerArgs(NodeContext& node)
#else
hidden_args.emplace_back("-logthreadnames");
#endif
+ argsman.AddArg("-logsourcelocations", strprintf("Prepend debug output with name of the originating source location (source file, line number and function name) (default: %u)", DEFAULT_LOGSOURCELOCATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.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);
@@ -574,10 +580,12 @@ void SetupServerArgs(NodeContext& node)
argsman.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);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
-#if HAVE_DECL_DAEMON
- argsman.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#if HAVE_DECL_FORK
+ argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
+ hidden_args.emplace_back("-daemonwait");
#endif
// Add the hidden options
@@ -714,7 +722,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
- LoadGenesisBlock(chainparams);
+ ::ChainstateActive().LoadGenesisBlock(chainparams);
}
// -loadblock=
@@ -765,13 +773,17 @@ static bool InitSanityCheck()
return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
}
- if (!glibc_sanity_test() || !glibcxx_sanity_test())
+ if (!glibcxx_sanity_test())
return false;
if (!Random_SanityCheck()) {
return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
}
+ if (!ChronoSanityCheck()) {
+ return InitError(Untranslated("Clock epoch mismatch. Aborting."));
+ }
+
return true;
}
@@ -840,6 +852,9 @@ void InitParameterInteraction(ArgsManager& args)
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (args.SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
+ if (args.SoftSetBoolArg("-i2pacceptincoming", false)) {
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -i2pacceptincoming=0\n", __func__);
+ }
}
if (args.IsArgSet("-externalip")) {
@@ -877,6 +892,7 @@ void InitLogging(const ArgsManager& args)
#ifdef HAVE_THREAD_LOCAL
LogInstance().m_log_threadnames = args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
#endif
+ LogInstance().m_log_sourcelocations = args.GetBoolArg("-logsourcelocations", DEFAULT_LOGSOURCELOCATIONS);
fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
@@ -1023,9 +1039,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
if (args.GetArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
- if (!g_enabled_filter_types.empty()) {
- return InitError(_("Prune mode is incompatible with -blockfilterindex."));
- }
}
// -bind and -whitebind can't be set when not listening
@@ -1338,7 +1351,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
assert(!node.scheduler);
- node.scheduler = MakeUnique<CScheduler>();
+ node.scheduler = std::make_unique<CScheduler>();
// Start the lightweight task scheduler thread
node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); });
@@ -1390,9 +1403,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)};
assert(!node.banman);
- node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
- node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true));
+ node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true));
assert(!node.fee_estimator);
// Don't initialize fee estimation with old data if we don't relay transactions,
@@ -1625,7 +1638,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1736,7 +1749,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// work when we allow VerifyDB to be parameterized by chainstate.
if (&::ChainstateActive() == chainstate &&
!CVerifyDB().VerifyDB(
- chainparams, &chainstate->CoinsDB(),
+ chainparams, *chainstate, &chainstate->CoinsDB(),
args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
@@ -1788,7 +1801,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
- g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex);
+ g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
g_txindex->Start();
}
@@ -1985,6 +1998,21 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
connOptions.m_specified_outgoing = connect;
}
}
+
+ const std::string& i2psam_arg = args.GetArg("-i2psam", "");
+ if (!i2psam_arg.empty()) {
+ CService addr;
+ if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
+ return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
+ }
+ SetReachable(NET_I2P, true);
+ SetProxy(NET_I2P, proxyType{addr});
+ } else {
+ SetReachable(NET_I2P, false);
+ }
+
+ connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", true);
+
if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
diff --git a/src/init.h b/src/init.h
index c04d966d06..34bca09dd1 100644
--- a/src/init.h
+++ b/src/init.h
@@ -9,6 +9,11 @@
#include <memory>
#include <string>
+//! Default value for -daemon option
+static constexpr bool DEFAULT_DAEMON = false;
+//! Default value for -daemonwait option
+static constexpr bool DEFAULT_DAEMONWAIT = false;
+
class ArgsManager;
struct NodeContext;
namespace interfaces {
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 1a49518d69..3395741b1b 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -5,12 +5,12 @@
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
-#include <optional.h> // For Optional and nullopt
#include <primitives/transaction.h> // For CTransactionRef
#include <util/settings.h> // For util::SettingsValue
#include <functional>
#include <memory>
+#include <optional>
#include <stddef.h>
#include <stdint.h>
#include <string>
@@ -94,7 +94,7 @@ public:
//! Get current chain height, not including genesis block (returns 0 if
//! chain only contains genesis block, nullopt if chain does not contain
//! any blocks)
- virtual Optional<int> getHeight() = 0;
+ virtual std::optional<int> getHeight() = 0;
//! Get block hash. Height must be valid or this function will abort.
virtual uint256 getBlockHash(int height) = 0;
@@ -109,7 +109,7 @@ public:
//! Return height of the highest block on chain in common with the locator,
//! which will either be the original block used to create the locator,
//! or one of its ancestors.
- virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
+ virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
//! Check if transaction will be final given chain height current time.
virtual bool checkFinalTx(const CTransaction& tx) = 0;
@@ -154,11 +154,14 @@ public:
//! Return true if data is available for all blocks in the specified range
//! of blocks. This checks all blocks that are ancestors of block_hash in
//! the height range from min_height to max_height, inclusive.
- virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, Optional<int> max_height = {}) = 0;
+ virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, std::optional<int> max_height = {}) = 0;
//! Check if transaction is RBF opt in.
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
+ //! Check if transaction is in mempool.
+ virtual bool isInMempool(const uint256& txid) = 0;
+
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp
index 4134a4527f..c6bbbed445 100644
--- a/src/interfaces/handler.cpp
+++ b/src/interfaces/handler.cpp
@@ -4,7 +4,6 @@
#include <interfaces/handler.h>
-#include <util/memory.h>
#include <boost/signals2/connection.hpp>
#include <utility>
@@ -35,12 +34,12 @@ public:
std::unique_ptr<Handler> MakeHandler(boost::signals2::connection connection)
{
- return MakeUnique<HandlerImpl>(std::move(connection));
+ return std::make_unique<HandlerImpl>(std::move(connection));
}
std::unique_ptr<Handler> MakeHandler(std::function<void()> cleanup)
{
- return MakeUnique<CleanupHandler>(std::move(cleanup));
+ return std::make_unique<CleanupHandler>(std::move(cleanup));
}
} // namespace interfaces
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 15f7ef6256..1dd1e92e2f 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -6,9 +6,10 @@
#define BITCOIN_INTERFACES_NODE_H
#include <amount.h> // For CAmount
-#include <net.h> // For CConnman::NumConnections
+#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
+#include <netbase.h> // For ConnectionDirection
#include <support/allocators/secure.h> // For SecureString
#include <util/translation.h>
@@ -88,7 +89,7 @@ public:
virtual bool getProxy(Network net, proxyType& proxy_info) = 0;
//! Get number of connections.
- virtual size_t getNodeCount(CConnman::NumConnections flags) = 0;
+ virtual size_t getNodeCount(ConnectionDirection flags) = 0;
//! Get stats for connected nodes.
using NodesStats = std::vector<std::tuple<CNodeStats, bool, CNodeStateStats>>;
diff --git a/src/key_io.cpp b/src/key_io.cpp
index e27673fd16..dbcbfa1f29 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -43,7 +43,7 @@ public:
std::vector<unsigned char> data = {0};
data.reserve(33);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
- return bech32::Encode(m_params.Bech32HRP(), data);
+ return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
}
std::string operator()(const WitnessV0ScriptHash& id) const
@@ -51,7 +51,7 @@ public:
std::vector<unsigned char> data = {0};
data.reserve(53);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
- return bech32::Encode(m_params.Bech32HRP(), data);
+ return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
}
std::string operator()(const WitnessUnknown& id) const
@@ -62,7 +62,7 @@ public:
std::vector<unsigned char> data = {(unsigned char)id.version};
data.reserve(1 + (id.length * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
- return bech32::Encode(m_params.Bech32HRP(), data);
+ return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
}
std::string operator()(const CNoDestination& no) const { return {}; }
@@ -95,20 +95,26 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
error_str = "Invalid prefix for Base58-encoded address";
}
data.clear();
- auto bech = bech32::Decode(str);
- if (bech.second.size() > 0) {
+ const auto dec = bech32::Decode(str);
+ if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) {
+ // Bech32 decoding
error_str = "";
-
- if (bech.first != params.Bech32HRP()) {
+ if (dec.hrp != params.Bech32HRP()) {
error_str = "Invalid prefix for Bech32 address";
return CNoDestination();
}
-
- // Bech32 decoding
- int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
+ int version = dec.data[0]; // The first 5 bit symbol is the witness version (0-16)
+ if (version == 0 && dec.encoding != bech32::Encoding::BECH32) {
+ error_str = "Version 0 witness address must use Bech32 checksum";
+ return CNoDestination();
+ }
+ if (version != 0 && dec.encoding != bech32::Encoding::BECH32M) {
+ error_str = "Version 1+ witness address must use Bech32m checksum";
+ return CNoDestination();
+ }
// The rest of the symbols are converted witness program bytes.
- data.reserve(((bech.second.size() - 1) * 5) / 8);
- if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) {
+ data.reserve(((dec.data.size() - 1) * 5) / 8);
+ if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin() + 1, dec.data.end())) {
if (version == 0) {
{
WitnessV0KeyHash keyid;
diff --git a/src/logging.cpp b/src/logging.cpp
index 4ddcf1d930..866213786e 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -5,6 +5,7 @@
#include <logging.h>
#include <util/threadnames.h>
+#include <util/string.h>
#include <util/time.h>
#include <mutex>
@@ -155,6 +156,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::QT, "qt"},
{BCLog::LEVELDB, "leveldb"},
{BCLog::VALIDATION, "validation"},
+ {BCLog::I2P, "i2p"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
@@ -203,9 +205,9 @@ std::string BCLog::Logger::LogTimestampStr(const std::string& str)
strStamped.pop_back();
strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
}
- int64_t mocktime = GetMockTime();
- if (mocktime) {
- strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
+ std::chrono::seconds mocktime = GetMockTime();
+ if (mocktime > 0s) {
+ strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
}
strStamped += ' ' + str;
} else
@@ -236,11 +238,15 @@ namespace BCLog {
}
}
-void BCLog::Logger::LogPrintStr(const std::string& str)
+void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line)
{
StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str);
+ if (m_log_sourcelocations && m_started_new_line) {
+ str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
+ }
+
if (m_log_threadnames && m_started_new_line) {
str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
}
diff --git a/src/logging.h b/src/logging.h
index 9efecc7c12..436f0cd12e 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -22,6 +22,7 @@ static const bool DEFAULT_LOGTIMEMICROS = false;
static const bool DEFAULT_LOGIPS = false;
static const bool DEFAULT_LOGTIMESTAMPS = true;
static const bool DEFAULT_LOGTHREADNAMES = false;
+static const bool DEFAULT_LOGSOURCELOCATIONS = false;
extern const char * const DEFAULT_DEBUGLOGFILE;
extern bool fLogIPs;
@@ -56,6 +57,7 @@ namespace BCLog {
QT = (1 << 19),
LEVELDB = (1 << 20),
VALIDATION = (1 << 21),
+ I2P = (1 << 22),
ALL = ~(uint32_t)0,
};
@@ -90,12 +92,13 @@ namespace BCLog {
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
+ bool m_log_sourcelocations = DEFAULT_LOGSOURCELOCATIONS;
fs::path m_file_path;
std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */
- void LogPrintStr(const std::string& str);
+ void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line);
/** Returns whether logs will be written to any output */
bool Enabled() const
@@ -163,7 +166,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
// peer can fill up a user's disk with debug.log entries.
template <typename... Args>
-static inline void LogPrintf(const char* fmt, const Args&... args)
+static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args)
{
if (LogInstance().Enabled()) {
std::string log_msg;
@@ -173,10 +176,12 @@ static inline void LogPrintf(const char* fmt, const Args&... args)
/* Original format string will have newline so don't add one here */
log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
}
- LogInstance().LogPrintStr(log_msg);
+ LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line);
}
}
+#define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__)
+
// Use a macro instead of a function for conditional logging to prevent
// evaluating arguments when logging for the category is not enabled.
#define LogPrint(category, ...) \
diff --git a/src/miner.cpp b/src/miner.cpp
index 076d43c951..fe7a54c052 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -39,13 +39,13 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
return nNewTime - nOldTime;
}
-void RegenerateCommitments(CBlock& block)
+void RegenerateCommitments(CBlock& block, BlockManager& blockman)
{
CMutableTransaction tx{*block.vtx.at(0)};
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
+ GenerateCoinbaseCommitment(block, WITH_LOCK(::cs_main, assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); return blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
}
@@ -96,10 +96,7 @@ void BlockAssembler::resetBlock()
nFees = 0;
}
-Optional<int64_t> BlockAssembler::m_last_block_num_txs{nullopt};
-Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt};
-
-std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
+std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn)
{
int64_t nTimeStart = GetTimeMicros();
@@ -117,7 +114,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- CBlockIndex* pindexPrev = ::ChainActive().Tip();
+ assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*chainstate.m_chain.Tip()));
+ CBlockIndex* pindexPrev = chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -176,7 +174,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+ if (!TestBlockValidity(state, chainparams, chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
int64_t nTime2 = GetTimeMicros();
diff --git a/src/miner.h b/src/miner.h
index 9a2b7063f4..023635814c 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -6,12 +6,12 @@
#ifndef BITCOIN_MINER_H
#define BITCOIN_MINER_H
-#include <optional.h>
#include <primitives/block.h>
#include <txmempool.h>
#include <validation.h>
#include <memory>
+#include <optional>
#include <stdint.h>
#include <boost/multi_index_container.hpp>
@@ -158,10 +158,10 @@ public:
explicit BlockAssembler(const CTxMemPool& mempool, const CChainParams& params, const Options& options);
/** Construct a new block template with coinbase to scriptPubKeyIn */
- std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
+ std::unique_ptr<CBlockTemplate> CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn);
- static Optional<int64_t> m_last_block_num_txs;
- static Optional<int64_t> m_last_block_weight;
+ inline static std::optional<int64_t> m_last_block_num_txs{};
+ inline static std::optional<int64_t> m_last_block_weight{};
private:
// utility functions
@@ -202,6 +202,6 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
-void RegenerateCommitments(CBlock& block);
+void RegenerateCommitments(CBlock& block, BlockManager& blockman);
#endif // BITCOIN_MINER_H
diff --git a/src/net.cpp b/src/net.cpp
index d004aace88..3b1ebede98 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -11,15 +11,17 @@
#include <banman.h>
#include <clientversion.h>
+#include <compat.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
+#include <i2p.h>
#include <net_permissions.h>
#include <netbase.h>
#include <node/ui_interface.h>
-#include <optional.h>
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
+#include <util/sock.h>
#include <util/strencodings.h>
#include <util/translation.h>
@@ -36,6 +38,7 @@
#include <algorithm>
#include <cstdint>
#include <functional>
+#include <optional>
#include <unordered_map>
#include <math.h>
@@ -71,16 +74,6 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
-// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif
-
-// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_DONTWAIT)
-#define MSG_DONTWAIT 0
-#endif
-
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
@@ -120,7 +113,7 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort()
{
- return (uint16_t)(gArgs.GetArg("-port", Params().GetDefaultPort()));
+ return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
@@ -200,31 +193,29 @@ bool IsPeerAddrLocalGood(CNode *pnode)
IsReachable(addrLocal.GetNetwork());
}
-// pushes our own address to a peer
-void AdvertiseLocal(CNode *pnode)
+std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode)
{
- if (fListen && pnode->fSuccessfullyConnected)
+ CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
+ if (gArgs.GetBoolArg("-addrmantest", false)) {
+ // use IPv4 loopback during addrmantest
+ addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
+ }
+ // If discovery is enabled, sometimes give our peer the address it
+ // tells us that it sees us as in case it has a better idea of our
+ // address than we do.
+ FastRandomContext rng;
+ if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
+ rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
{
- CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
- if (gArgs.GetBoolArg("-addrmantest", false)) {
- // use IPv4 loopback during addrmantest
- addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
- }
- // If discovery is enabled, sometimes give our peer the address it
- // tells us that it sees us as in case it has a better idea of our
- // address than we do.
- FastRandomContext rng;
- if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
- rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
- {
- addrLocal.SetIP(pnode->GetAddrLocal());
- }
- if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
- {
- LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
- pnode->PushAddress(addrLocal, rng);
- }
+ addrLocal.SetIP(pnode->GetAddrLocal());
}
+ if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
+ {
+ LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), pnode->GetId());
+ return addrLocal;
+ }
+ // Address is unroutable. Don't advertise.
+ return std::nullopt;
}
// learn a new local address
@@ -403,7 +394,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Resolve
- const int default_port = Params().GetDefaultPort();
+ const uint16_t default_port{Params().GetDefaultPort()};
if (pszDest) {
std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
@@ -429,24 +420,36 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Connect
bool connected = false;
- SOCKET hSocket = INVALID_SOCKET;
+ std::unique_ptr<Sock> sock;
proxyType proxy;
+ CAddress addr_bind;
+ assert(!addr_bind.IsValid());
+
if (addrConnect.IsValid()) {
bool proxyConnectionFailed = false;
- if (GetProxy(addrConnect.GetNetwork(), proxy)) {
- hSocket = CreateSocket(proxy.proxy);
- if (hSocket == INVALID_SOCKET) {
+ if (addrConnect.GetNetwork() == NET_I2P && m_i2p_sam_session.get() != nullptr) {
+ i2p::Connection conn;
+ if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
+ connected = true;
+ sock = std::make_unique<Sock>(std::move(conn.sock));
+ addr_bind = CAddress{conn.me, NODE_NONE};
+ }
+ } else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
+ sock = CreateSock(proxy.proxy);
+ if (!sock) {
return nullptr;
}
- connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, proxyConnectionFailed);
+ connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(),
+ *sock, nConnectTimeout, proxyConnectionFailed);
} else {
// no proxy needed (none set for target network)
- hSocket = CreateSocket(addrConnect);
- if (hSocket == INVALID_SOCKET) {
+ sock = CreateSock(addrConnect);
+ if (!sock) {
return nullptr;
}
- connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, conn_type == ConnectionType::MANUAL);
+ connected = ConnectSocketDirectly(addrConnect, sock->Get(), nConnectTimeout,
+ conn_type == ConnectionType::MANUAL);
}
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
@@ -454,26 +457,28 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addrman.Attempt(addrConnect, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
- hSocket = CreateSocket(proxy.proxy);
- if (hSocket == INVALID_SOCKET) {
+ sock = CreateSock(proxy.proxy);
+ if (!sock) {
return nullptr;
}
std::string host;
- int port = default_port;
+ uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
- connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, proxyConnectionFailed);
+ connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
+ proxyConnectionFailed);
}
if (!connected) {
- CloseSocket(hSocket);
return nullptr;
}
// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type);
+ if (!addr_bind.IsValid()) {
+ addr_bind = GetBindAddress(sock->Get());
+ }
+ CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
pnode->AddRef();
// We're making a new connection, harvest entropy from the time (and our peer count)
@@ -598,21 +603,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
stats.minFeeFilter = 0;
}
- // It is common for nodes with good ping times to suddenly become lagged,
- // due to a new block arriving or other large transfer.
- // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
- // since pingtime does not update until the ping is complete, which might take a while.
- // So, if a ping is taking an unusually long time in flight,
- // the caller can immediately detect that this is happening.
- std::chrono::microseconds ping_wait{0};
- if ((0 != nPingNonceSent) && (0 != m_ping_start.load().count())) {
- ping_wait = GetTime<std::chrono::microseconds>() - m_ping_start.load();
- }
-
- // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
- stats.m_ping_usec = nPingUsecTime;
- stats.m_min_ping_usec = nMinPingUsecTime;
- stats.m_ping_wait_usec = count_microseconds(ping_wait);
+ X(m_last_ping_time);
+ X(m_min_ping_time);
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -640,7 +632,7 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
uint32_t out_err_raw_size{0};
- Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
+ std::optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
if (!result) {
// Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
@@ -731,10 +723,10 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
+std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
{
// decompose a single CNetMessage from the TransportDeserializer
- Optional<CNetMessage> msg(std::move(vRecv));
+ std::optional<CNetMessage> msg(std::move(vRecv));
// store command string, time, and sizes
msg->m_command = hdr.GetCommand();
@@ -755,12 +747,12 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::mic
HexStr(hdr.pchChecksum),
m_node_id);
out_err_raw_size = msg->m_raw_message_size;
- msg = nullopt;
+ msg = std::nullopt;
} else if (!hdr.IsCommandValid()) {
LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
hdr.GetCommand(), msg->m_message_size, m_node_id);
out_err_raw_size = msg->m_raw_message_size;
- msg = nullopt;
+ msg.reset();
}
// Always reset the network deserializer (prepare for the next message)
@@ -834,7 +826,7 @@ size_t CConnman::SocketSendData(CNode& node) const
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
{
- return a.nMinPingUsecTime > b.nMinPingUsecTime;
+ return a.m_min_ping_time > b.m_min_ping_time;
}
static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -887,7 +879,7 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
elements.erase(elements.end() - eraseSize, elements.end());
}
-[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
+[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
{
// Protect connections with certain characteristics
@@ -926,7 +918,7 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
total_protect_size -= initial_size - vEvictionCandidates.size();
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
- if (vEvictionCandidates.empty()) return nullopt;
+ if (vEvictionCandidates.empty()) return std::nullopt;
// If any remaining peers are preferred for eviction consider only them.
// This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks)
@@ -989,7 +981,7 @@ bool CConnman::AttemptToEvictConnection()
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,
+ NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->m_min_ping_time,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
@@ -997,7 +989,7 @@ bool CConnman::AttemptToEvictConnection()
vEvictionCandidates.push_back(candidate);
}
}
- const Optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
+ const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
if (!node_id_to_evict) {
return false;
}
@@ -1017,17 +1009,35 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
- int nInbound = 0;
- int nMaxInbound = nMaxConnections - m_max_outbound;
- if (hSocket != INVALID_SOCKET) {
- if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ if (hSocket == INVALID_SOCKET) {
+ const int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK) {
+ LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
}
+ return;
}
+ if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
+ LogPrintf("Warning: Unknown socket family\n");
+ }
+
+ const CAddress addr_bind = GetBindAddress(hSocket);
+
NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
+
+ CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr);
+}
+
+void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ NetPermissionFlags permissionFlags,
+ const CAddress& addr_bind,
+ const CAddress& addr)
+{
+ int nInbound = 0;
+ int nMaxInbound = nMaxConnections - m_max_outbound;
+
AddWhitelistPermissionFlags(permissionFlags, addr);
if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
@@ -1044,14 +1054,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- if (hSocket == INVALID_SOCKET)
- {
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK)
- LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
- return;
- }
-
if (!fNetworkActive) {
LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString());
CloseSocket(hSocket);
@@ -1099,7 +1101,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
ServiceFlags nodeServices = nLocalServices;
if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
@@ -1218,18 +1219,17 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
+bool CConnman::RunInactivityChecks(const CNode& node) const
+{
+ return GetSystemTimeInSeconds() > node.nTimeConnected + m_peer_connect_timeout;
+}
+
bool CConnman::InactivityCheck(const CNode& node) const
{
// Use non-mockable system time (otherwise these timers will pop when we
// use setmocktime in the tests).
int64_t now = GetSystemTimeInSeconds();
- if (now <= node.nTimeConnected + m_peer_connect_timeout) {
- // Only run inactivity checks if the peer has been connected longer
- // than m_peer_connect_timeout.
- return false;
- }
-
if (node.nLastRecv == 0 || node.nLastSend == 0) {
LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d peer=%d\n", m_peer_connect_timeout, node.nLastRecv != 0, node.nLastSend != 0, node.GetId());
return true;
@@ -1245,14 +1245,6 @@ bool CConnman::InactivityCheck(const CNode& node) const
return true;
}
- if (node.nPingNonceSent && node.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>()) {
- // We use mockable time for ping timeouts. This means that setmocktime
- // may cause pings to time out for peers that have been connected for
- // longer than m_peer_connect_timeout.
- LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(GetTime<std::chrono::microseconds>() - node.m_ping_start.load()), node.GetId());
- return true;
- }
-
if (!node.fSuccessfullyConnected) {
LogPrint(BCLog::NET, "version handshake timeout peer=%d\n", node.GetId());
return true;
@@ -1534,7 +1526,7 @@ void CConnman::SocketHandler()
if (bytes_sent) RecordBytesSent(bytes_sent);
}
- if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
+ if (RunInactivityChecks(*pnode) && InactivityCheck(*pnode)) pnode->fDisconnect = true;
}
{
LOCK(cs_vNodes);
@@ -1769,11 +1761,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Initiate network connections
- int64_t nStart = GetTime();
+ auto start = GetTime<std::chrono::microseconds>();
// Minimum time before next feeler connection (in microseconds).
- int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
- int64_t nNextExtraBlockRelay = PoissonNextSend(nStart*1000*1000, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ auto next_feeler = PoissonNextSend(start, FEELER_INTERVAL);
+ auto next_extra_block_relay = PoissonNextSend(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
+ bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
+
+ if (!add_fixed_seeds) {
+ LogPrintf("Fixed seeds are disabled\n");
+ }
+
while (!interruptNet)
{
ProcessAddrFetch();
@@ -1785,18 +1784,32 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (interruptNet)
return;
- // Add seed nodes if DNS seeds are all down (an infrastructure attack?).
- // Note that we only do this if we started with an empty peers.dat,
- // (in which case we will query DNS seeds immediately) *and* the DNS
- // seeds have not returned any results.
- if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
- static bool done = false;
- if (!done) {
- LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
+ if (add_fixed_seeds && addrman.size() == 0) {
+ // When the node starts with an empty peers.dat, there are a few other sources of peers before
+ // we fallback on to fixed seeds: -dnsseed, -seednode, -addnode
+ // If none of those are available, we fallback on to fixed seeds immediately, else we allow
+ // 60 seconds for any of those sources to populate addrman.
+ bool add_fixed_seeds_now = false;
+ // It is cheapest to check if enough time has passed first.
+ if (GetTime<std::chrono::seconds>() > start + std::chrono::minutes{1}) {
+ add_fixed_seeds_now = true;
+ LogPrintf("Adding fixed seeds as 60 seconds have passed and addrman is empty\n");
+ }
+
+ // Checking !dnsseed is cheaper before locking 2 mutexes.
+ if (!add_fixed_seeds_now && !dnsseed) {
+ LOCK2(m_addr_fetches_mutex, cs_vAddedNodes);
+ if (m_addr_fetches.empty() && vAddedNodes.empty()) {
+ add_fixed_seeds_now = true;
+ LogPrintf("Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n");
+ }
+ }
+
+ if (add_fixed_seeds_now) {
CNetAddr local;
local.SetInternal("fixedseeds");
addrman.Add(convertSeed6(Params().FixedSeeds()), local);
- done = true;
+ add_fixed_seeds = false;
}
}
@@ -1835,7 +1848,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
- int64_t nTime = GetTimeMicros();
+ auto now = GetTime<std::chrono::microseconds>();
bool anchor = false;
bool fFeeler = false;
@@ -1847,7 +1860,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
// try opening an additional OUTBOUND_FULL_RELAY connection. If none of
// these conditions are met, check to see if it's time to try an extra
- // block-relay-only peer (to confirm our tip is current, see below) or the nNextFeeler
+ // block-relay-only peer (to confirm our tip is current, see below) or the next_feeler
// timer to decide if we should open a FEELER.
if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) {
@@ -1859,7 +1872,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
conn_type = ConnectionType::BLOCK_RELAY;
} else if (GetTryNewOutboundPeer()) {
// OUTBOUND_FULL_RELAY
- } else if (nTime > nNextExtraBlockRelay && m_start_extra_block_relay_peers) {
+ } else if (now > next_extra_block_relay && m_start_extra_block_relay_peers) {
// Periodically connect to a peer (using regular outbound selection
// methodology from addrman) and stay connected long enough to sync
// headers, but not much else.
@@ -1881,10 +1894,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Because we can promote these connections to block-relay-only
// connections, they do not get their own ConnectionType enum
// (similar to how we deal with extra outbound peers).
- nNextExtraBlockRelay = PoissonNextSend(nTime, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ next_extra_block_relay = PoissonNextSend(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
conn_type = ConnectionType::BLOCK_RELAY;
- } else if (nTime > nNextFeeler) {
- nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ } else if (now > next_feeler) {
+ next_feeler = PoissonNextSend(now, FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
fFeeler = true;
} else {
@@ -2174,6 +2187,45 @@ void CConnman::ThreadMessageHandler()
}
}
+void CConnman::ThreadI2PAcceptIncoming()
+{
+ static constexpr auto err_wait_begin = 1s;
+ static constexpr auto err_wait_cap = 5min;
+ auto err_wait = err_wait_begin;
+
+ bool advertising_listen_addr = false;
+ i2p::Connection conn;
+
+ while (!interruptNet) {
+
+ if (!m_i2p_sam_session->Listen(conn)) {
+ if (advertising_listen_addr && conn.me.IsValid()) {
+ RemoveLocal(conn.me);
+ advertising_listen_addr = false;
+ }
+
+ interruptNet.sleep_for(err_wait);
+ if (err_wait < err_wait_cap) {
+ err_wait *= 2;
+ }
+
+ continue;
+ }
+
+ if (!advertising_listen_addr) {
+ AddLocal(conn.me, LOCAL_BIND);
+ advertising_listen_addr = true;
+ }
+
+ if (!m_i2p_sam_session->Accept(conn)) {
+ continue;
+ }
+
+ CreateNodeFromAcceptedSocket(conn.sock.Release(), NetPermissionFlags::PF_NONE,
+ CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
+ }
+}
+
bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions)
{
int nOne = 1;
@@ -2188,9 +2240,8 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
return false;
}
- SOCKET hListenSocket = CreateSocket(addrBind);
- if (hListenSocket == INVALID_SOCKET)
- {
+ std::unique_ptr<Sock> sock = CreateSock(addrBind);
+ if (!sock) {
strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError.original);
return false;
@@ -2198,21 +2249,21 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
- setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
+ setsockopt(sock->Get(), SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
+ setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
+ setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
#endif
}
- if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
+ if (::bind(sock->Get(), (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
@@ -2220,21 +2271,19 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
LogPrintf("%s\n", strError.original);
- CloseSocket(hListenSocket);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
// Listen for incoming connections
- if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
+ if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR)
{
strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError.original);
- CloseSocket(hListenSocket);
return false;
}
- vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
+ vhListenSocket.push_back(ListenSocket(sock->Release(), permissions));
return true;
}
@@ -2376,6 +2425,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
return false;
}
+ proxyType i2p_sam;
+ if (GetProxy(NET_I2P, i2p_sam)) {
+ m_i2p_sam_session = std::make_unique<i2p::sam::Session>(GetDataDir() / "i2p_private_key",
+ i2p_sam.proxy, &interruptNet);
+ }
+
for (const auto& strDest : connOptions.vSeedNodes) {
AddAddrFetch(strDest);
}
@@ -2391,7 +2446,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart);
else {
addrman.Clear(); // Addrman can be in an inconsistent state after failure, reset it
- LogPrintf("Invalid or missing peers.dat; recreating\n");
+ LogPrintf("Recreating peers.dat\n");
DumpAddresses();
}
}
@@ -2411,11 +2466,11 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (semOutbound == nullptr) {
// initialize semaphore
- semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
+ semOutbound = std::make_unique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
}
if (semAddnode == nullptr) {
// initialize semaphore
- semAddnode = MakeUnique<CSemaphore>(nMaxAddnode);
+ semAddnode = std::make_unique<CSemaphore>(nMaxAddnode);
}
//
@@ -2434,7 +2489,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Send and receive from sockets, accept connections
threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this)));
- if (!gArgs.GetBoolArg("-dnsseed", true))
+ if (!gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED))
LogPrintf("DNS seeding disabled\n");
else
threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()> >, "dnsseed", std::function<void()>(std::bind(&CConnman::ThreadDNSAddressSeed, this)));
@@ -2456,6 +2511,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Process messages
threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
+ if (connOptions.m_i2p_accept_incoming && m_i2p_sam_session.get() != nullptr) {
+ threadI2PAcceptIncoming =
+ std::thread(&TraceThread<std::function<void()>>, "i2paccept",
+ std::function<void()>(std::bind(&CConnman::ThreadI2PAcceptIncoming, this)));
+ }
+
// Dump network addresses
scheduler.scheduleEvery([this] { DumpAddresses(); }, DUMP_PEERS_INTERVAL);
@@ -2503,6 +2564,9 @@ void CConnman::Interrupt()
void CConnman::StopThreads()
{
+ if (threadI2PAcceptIncoming.joinable()) {
+ threadI2PAcceptIncoming.join();
+ }
if (threadMessageHandler.joinable())
threadMessageHandler.join();
if (threadOpenConnections.joinable())
@@ -2599,9 +2663,7 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
- SOCKET socket;
- WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
- auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
+ auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
.Write(requestor.addr.GetNetwork())
.Write(local_socket_bytes.data(), local_socket_bytes.size())
@@ -2663,15 +2725,15 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
return false;
}
-size_t CConnman::GetNodeCount(NumConnections flags)
+size_t CConnman::GetNodeCount(ConnectionDirection flags)
{
LOCK(cs_vNodes);
- if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total
+ if (flags == ConnectionDirection::Both) // Shortcut if we want total
return vNodes.size();
int nNum = 0;
for (const auto& pnode : vNodes) {
- if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
+ if (flags & (pnode->IsInboundConn() ? ConnectionDirection::In : ConnectionDirection::Out)) {
nNum++;
}
}
@@ -2833,22 +2895,22 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
: nTimeConnected(GetSystemTimeInSeconds()),
addr(addrIn),
addrBind(addrBindIn),
+ m_inbound_onion(inbound_onion),
nKeyedNetGroup(nKeyedNetGroupIn),
id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
m_conn_type(conn_type_in),
- nLocalServices(nLocalServicesIn),
- m_inbound_onion(inbound_onion)
+ nLocalServices(nLocalServicesIn)
{
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
- m_tx_relay = MakeUnique<TxRelay>();
+ m_tx_relay = std::make_unique<TxRelay>();
}
if (RelayAddrsWithConn()) {
- m_addr_known = MakeUnique<CRollingBloomFilter>(5000, 0.001);
+ m_addr_known = std::make_unique<CRollingBloomFilter>(5000, 0.001);
}
for (const std::string &msg : getAllNetMessageTypes())
@@ -2861,8 +2923,8 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
- m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer());
+ m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
CNode::~CNode()
@@ -2920,20 +2982,21 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
+std::chrono::microseconds CConnman::PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- if (m_next_send_inv_to_incoming < now) {
+ if (m_next_send_inv_to_incoming.load() < now) {
// If this function were called from multiple threads simultaneously
// it would possible that both update the next send variable, and return a different result to their caller.
// This is not possible in practice as only the net processing thread invokes this function.
- m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
+ m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval);
}
return m_next_send_inv_to_incoming;
}
-int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
+std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+ double unscaled = -log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
+ return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
}
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
diff --git a/src/net.h b/src/net.h
index 2c47d5e557..5228c4fbd3 100644
--- a/src/net.h
+++ b/src/net.h
@@ -14,9 +14,10 @@
#include <compat.h>
#include <crypto/siphash.h>
#include <hash.h>
+#include <i2p.h>
#include <net_permissions.h>
#include <netaddress.h>
-#include <optional.h>
+#include <netbase.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -33,6 +34,7 @@
#include <deque>
#include <map>
#include <memory>
+#include <optional>
#include <thread>
#include <vector>
@@ -48,10 +50,10 @@ static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
static const int TIMEOUT_INTERVAL = 20 * 60;
-/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
-static const int FEELER_INTERVAL = 120;
+/** Run the feeler connection loop once every 2 minutes. **/
+static constexpr auto FEELER_INTERVAL = 2min;
/** Run the extra block-relay-only connection loop once every 5 minutes. **/
-static const int EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 300;
+static constexpr auto EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 5min;
/** The maximum number of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
@@ -80,6 +82,8 @@ static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
static const int NUM_FDS_MESSAGE_CAPTURE = 1;
static const bool DEFAULT_FORCEDNSSEED = false;
+static const bool DEFAULT_DNSSEED = true;
+static const bool DEFAULT_FIXEDSEEDS = true;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
@@ -195,7 +199,8 @@ enum
};
bool IsPeerAddrLocalGood(CNode *pnode);
-void AdvertiseLocal(CNode *pnode);
+/** Returns a local address that we should advertise to this peer */
+std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode);
/**
* Mark a network as reachable or unreachable (no automatic connects to it)
@@ -224,7 +229,7 @@ extern std::string strSubVersion;
struct LocalServiceInfo {
int nScore;
- int nPort;
+ uint16_t nPort;
};
extern RecursiveMutex cs_mapLocalHost;
@@ -257,9 +262,8 @@ public:
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
NetPermissionFlags m_permissionFlags;
- int64_t m_ping_usec;
- int64_t m_ping_wait_usec;
- int64_t m_min_ping_usec;
+ std::chrono::microseconds m_last_ping_time;
+ std::chrono::microseconds m_min_ping_time;
CAmount minFeeFilter;
// Our address, as reported by the peer
std::string addrLocal;
@@ -307,7 +311,7 @@ public:
/** read and deserialize data, advances msg_bytes data pointer */
virtual int Read(Span<const uint8_t>& msg_bytes) = 0;
// decomposes a message from the context
- virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
+ virtual std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
virtual ~TransportDeserializer() {}
};
@@ -371,7 +375,7 @@ public:
}
return ret;
}
- Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
+ std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -427,6 +431,8 @@ public:
const CAddress addr;
// Bind address of our side of the connection
const CAddress addrBind;
+ //! Whether this peer is an inbound onion, i.e. connected via our Tor onion service.
+ const bool m_inbound_onion;
std::atomic<int> nVersion{0};
RecursiveMutex cs_SubVer;
/**
@@ -445,6 +451,7 @@ public:
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
*/
std::atomic_bool m_wants_addrv2{false};
+ /** fSuccessfullyConnected is set to true on receiving VERACK from the peer. */
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
@@ -567,7 +574,7 @@ public:
/** Minimum fee rate with which to filter inv's to this node */
std::atomic<CAmount> minFeeFilter{0};
CAmount lastSentFeeFilter{0};
- int64_t nextSendTimeFeeFilter{0};
+ std::chrono::microseconds m_next_send_feefilter{0};
};
// m_tx_relay == nullptr if we're not relaying transactions with this peer
@@ -586,19 +593,14 @@ public:
* in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
- // Ping time measurement:
- // The pong reply we're expecting, or 0 if no pong expected.
- std::atomic<uint64_t> nPingNonceSent{0};
- /** When the last ping was sent, or 0 if no ping was ever sent */
- std::atomic<std::chrono::microseconds> m_ping_start{0us};
- // Last measured round-trip time.
- std::atomic<int64_t> nPingUsecTime{0};
- // Best measured round-trip time.
- std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
- // Whether a ping is requested.
- std::atomic<bool> fPingQueued{false};
-
- CNode(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
+ /** Last measured round-trip time. Used only for RPC/GUI stats/debugging.*/
+ std::atomic<std::chrono::microseconds> m_last_ping_time{0us};
+
+ /** Lowest measured round-trip time. Used as an inbound peer eviction
+ * criterium in CConnman::AttemptToEvictConnection. */
+ std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};
+
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -716,8 +718,11 @@ public:
std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); }
- /** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */
- bool IsInboundOnion() const { return m_inbound_onion; }
+ /** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */
+ void PongReceived(std::chrono::microseconds ping_time) {
+ m_last_ping_time = ping_time;
+ m_min_ping_time = std::min(m_min_ping_time.load(), ping_time);
+ }
private:
const NodeId id;
@@ -751,9 +756,6 @@ private:
CService addrLocal GUARDED_BY(cs_addrLocal);
mutable RecursiveMutex cs_addrLocal;
- //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service.
- const bool m_inbound_onion{false};
-
mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend);
mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
};
@@ -800,13 +802,6 @@ class CConnman
{
public:
- enum NumConnections {
- CONNECTIONS_NONE = 0,
- CONNECTIONS_IN = (1U << 0),
- CONNECTIONS_OUT = (1U << 1),
- CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
- };
-
struct Options
{
ServiceFlags nLocalServices = NODE_NONE;
@@ -831,6 +826,7 @@ public:
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
std::vector<bool> m_asmap;
+ bool m_i2p_accept_incoming;
};
void Init(const Options& connOptions) {
@@ -974,7 +970,7 @@ public:
*/
bool AddConnection(const std::string& address, ConnectionType conn_type);
- size_t GetNodeCount(NumConnections num);
+ size_t GetNodeCount(ConnectionDirection);
void GetNodeStats(std::vector<CNodeStats>& vstats);
bool DisconnectNode(const std::string& node);
bool DisconnectNode(const CSubNet& subnet);
@@ -1019,10 +1015,13 @@ public:
Works assuming that a single interval is used.
Variable intervals will result in privacy decrease.
*/
- int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
+ std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
+ /** Return true if the peer has been connected for long enough to do inactivity checks. */
+ bool RunInactivityChecks(const CNode& node) const;
+
private:
struct ListenSocket {
public:
@@ -1045,7 +1044,22 @@ private:
void ProcessAddrFetch();
void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
+ void ThreadI2PAcceptIncoming();
void AcceptConnection(const ListenSocket& hListenSocket);
+
+ /**
+ * Create a `CNode` object from a socket that has just been accepted and add the node to
+ * the `vNodes` member.
+ * @param[in] hSocket Connected socket to communicate with the peer.
+ * @param[in] permissionFlags The peer's permissions.
+ * @param[in] addr_bind The address and port at our side of the connection.
+ * @param[in] addr The address and port at the peer's side of the connection.
+ */
+ void CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ NetPermissionFlags permissionFlags,
+ const CAddress& addr_bind,
+ const CAddress& addr);
+
void DisconnectNodes();
void NotifyNumConnectionsChanged();
/** Return true if the peer is inactive and should be disconnected. */
@@ -1204,13 +1218,26 @@ private:
Mutex mutexMsgProc;
std::atomic<bool> flagInterruptMsgProc{false};
+ /**
+ * This is signaled when network activity should cease.
+ * A pointer to it is saved in `m_i2p_sam_session`, so make sure that
+ * the lifetime of `interruptNet` is not shorter than
+ * the lifetime of `m_i2p_sam_session`.
+ */
CThreadInterrupt interruptNet;
+ /**
+ * I2P SAM session.
+ * Used to accept incoming and make outgoing I2P connections.
+ */
+ std::unique_ptr<i2p::sam::Session> m_i2p_sam_session;
+
std::thread threadDNSAddressSeed;
std::thread threadSocketHandler;
std::thread threadOpenAddedConnections;
std::thread threadOpenConnections;
std::thread threadMessageHandler;
+ std::thread threadI2PAcceptIncoming;
/** flag for deciding to connect to an extra outbound peer,
* in excess of m_max_outbound_full_relay
@@ -1223,7 +1250,7 @@ private:
*/
std::atomic_bool m_start_extra_block_relay_peers{false};
- std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+ std::atomic<std::chrono::microseconds> m_next_send_inv_to_incoming{0us};
/**
* A vector of -bind=<address>:<port>=onion arguments each of which is
@@ -1236,13 +1263,7 @@ private:
};
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
-int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
-
-/** Wrapper to return mockable type */
-inline std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
-{
- return std::chrono::microseconds{PoissonNextSend(now.count(), average_interval.count())};
-}
+std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval);
/** Dump binary message to file, with timestamp */
void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming);
@@ -1251,7 +1272,7 @@ struct NodeEvictionCandidate
{
NodeId id;
int64_t nTimeConnected;
- int64_t nMinPingUsecTime;
+ std::chrono::microseconds m_min_ping_time;
int64_t nLastBlockTime;
int64_t nLastTXTime;
bool fRelevantServices;
@@ -1262,6 +1283,6 @@ struct NodeEvictionCandidate
bool m_is_local;
};
-[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates);
+[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates);
#endif // BITCOIN_NET_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 2dffbd7620..e561f02c4a 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -26,6 +26,7 @@
#include <streams.h>
#include <tinyformat.h>
#include <txmempool.h>
+#include <txorphanage.h>
#include <txrequest.h>
#include <util/check.h> // For NDEBUG compile time check
#include <util/strencodings.h>
@@ -33,20 +34,17 @@
#include <validation.h>
#include <memory>
+#include <optional>
#include <typeinfo>
-/** Expiration time for orphan transactions in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** How long to cache transactions in mapRelay for normal relay */
-static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME = std::chrono::minutes{15};
+static constexpr auto RELAY_TX_CACHE_TIME = 15min;
/** How long a transaction has to be in the mempool before it can unconditionally be relayed (even when not in mapRelay). */
-static constexpr std::chrono::seconds UNCONDITIONAL_RELAY_DELAY = std::chrono::minutes{2};
-/** Headers download timeout expressed in microseconds
+static constexpr auto UNCONDITIONAL_RELAY_DELAY = 2min;
+/** Headers download timeout.
* Timeout = base + per_header * (expected number of headers) */
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
+static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1ms;
/** Protect at least this many outbound peers from disconnection due to slow/
* behind headers chain.
*/
@@ -93,8 +91,8 @@ static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seco
static const unsigned int MAX_GETDATA_SZ = 1000;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
-/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
-static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
+/** Time during which a peer must stall block download progress before being disconnected. */
+static constexpr auto BLOCK_STALLING_TIMEOUT = 2s;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
static const unsigned int MAX_HEADERS_RESULTS = 2000;
@@ -108,10 +106,10 @@ static const int MAX_BLOCKTXN_DEPTH = 10;
* degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
* want to make this a per-peer adaptive value at some point. */
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
-/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
-static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
+/** Block download timeout base, expressed in multiples of the block interval (i.e. 10 min) */
+static constexpr double BLOCK_DOWNLOAD_TIMEOUT_BASE = 1;
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
-static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000;
+static constexpr double BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 0.5;
/** Maximum number of headers to announce when relaying blocks with headers message.*/
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
/** Maximum number of unconnecting headers announcements before DoS score */
@@ -119,17 +117,21 @@ static const int MAX_UNCONNECTING_HEADERS = 10;
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
/** Average delay between local address broadcasts */
-static constexpr std::chrono::hours AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24};
+static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h;
/** Average delay between peer address broadcasts */
-static constexpr std::chrono::seconds AVG_ADDRESS_BROADCAST_INTERVAL{30};
-/** Average delay between trickled inventory transmissions in seconds.
- * Blocks and peers with noban permission bypass this, outbound peers get half this delay. */
-static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
+static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL = 30s;
+/** Average delay between trickled inventory transmissions for inbound peers.
+ * Blocks and peers with noban permission bypass this. */
+static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL = 5s;
+/** Average delay between trickled inventory transmissions for outbound peers.
+ * Use a smaller delay as there is less privacy concern for them.
+ * Blocks and peers with noban permission bypass this. */
+static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL = 2s;
/** Maximum rate of inventory items to send per second.
* Limits the impact of low-fee transaction floods. */
static constexpr unsigned int INVENTORY_BROADCAST_PER_SECOND = 7;
/** Maximum number of inventory items to send per transmission. */
-static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * INVENTORY_BROADCAST_INTERVAL;
+static constexpr unsigned int INVENTORY_BROADCAST_MAX = INVENTORY_BROADCAST_PER_SECOND * count_seconds(INBOUND_INVENTORY_BROADCAST_INTERVAL);
/** The number of most recently announced transactions a peer can request. */
static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
/** Verify that INVENTORY_MAX_RECENT_RELAY is enough to cache everything typically
@@ -138,9 +140,9 @@ static constexpr unsigned int INVENTORY_MAX_RECENT_RELAY = 3500;
* peers, and random variations in the broadcast mechanism. */
static_assert(INVENTORY_MAX_RECENT_RELAY >= INVENTORY_BROADCAST_PER_SECOND * UNCONDITIONAL_RELAY_DELAY / std::chrono::seconds{1}, "INVENTORY_RELAY_MAX too low");
/** Average delay between feefilter broadcasts in seconds. */
-static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
+static constexpr auto AVG_FEEFILTER_BROADCAST_INTERVAL = 10min;
/** Maximum feefilter broadcast delay after significant change. */
-static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
+static constexpr auto MAX_FEEFILTER_CHANGE_DELAY = 5min;
/** Maximum number of compact filters that may be requested with one getcfilters. See BIP 157. */
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
@@ -148,27 +150,16 @@ static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
/** the maximum percentage of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;
-struct COrphanTx {
- // When modifying, adapt the copy of this definition in tests/DoS_tests.
- CTransactionRef tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
- size_t list_pos;
-};
-
-/** Guards orphan transactions and extra txs for compact blocks */
-RecursiveMutex g_cs_orphans;
-/** Map from txid to orphan transaction record. Limited by
- * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
-std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-/** Index from wtxid into the mapOrphanTransactions to lookup orphan
- * transactions using their witness ids. */
-std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans);
-
-void EraseOrphansFor(NodeId peer);
-
// Internal stuff
namespace {
+/** Blocks that are in flight, and that are in the queue to be downloaded. */
+struct QueuedBlock {
+ uint256 hash;
+ const CBlockIndex* pindex; //!< Optional.
+ bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+};
+
/**
* Data structure for an individual peer. This struct is not protected by
* cs_main since it does not contain validation-critical data.
@@ -211,6 +202,13 @@ struct Peer {
/** This peer's reported block height when we connected */
std::atomic<int> m_starting_height{-1};
+ /** The pong reply we're expecting, or 0 if no pong expected. */
+ std::atomic<uint64_t> m_ping_nonce_sent{0};
+ /** When the last ping was sent, or 0 if no ping was ever sent */
+ std::atomic<std::chrono::microseconds> m_ping_start{0us};
+ /** Whether a ping has been requested by the user */
+ std::atomic<bool> m_ping_queued{false};
+
/** Set of txids to reconsider once their parent transactions have been accepted **/
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
@@ -248,6 +246,8 @@ public:
void CheckForStaleTipAndEvictPeers() override;
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
+ void SendPings() override;
+ void RelayTransaction(const uint256& txid, const uint256& wtxid) override;
void SetBestHeight(int height) override { m_best_height = height; };
void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override;
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
@@ -261,7 +261,7 @@ private:
void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */
- void ReattemptInitialBroadcast(CScheduler& scheduler) const;
+ void ReattemptInitialBroadcast(CScheduler& scheduler);
/** Get a shared pointer to the Peer object.
* May return an empty shared_ptr if the Peer object can't be found. */
@@ -294,9 +294,10 @@ private:
/** Maybe disconnect a peer and discourage future connections from its address.
*
* @param[in] pnode The node to check.
+ * @param[in] peer The peer object to check.
* @return True if the peer was marked for disconnection in this function
*/
- bool MaybeDiscourageAndDisconnect(CNode& pnode);
+ bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
/** Process a single headers message from a peer. */
@@ -315,6 +316,10 @@ private:
/** Send a version message to a peer */
void PushNodeVersion(CNode& pnode, int64_t nTime);
+ /** Send a ping message every PING_INTERVAL or if requested via RPC. May
+ * mark the peer to be disconnected if a ping has timed out. */
+ void MaybeSendPing(CNode& node_to, Peer& peer);
+
const CChainParams& m_chainparams;
CConnman& m_connman;
/** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
@@ -345,10 +350,7 @@ private:
* their own locks.
*/
std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
-};
-} // namespace
-namespace {
/** Number of nodes with fSyncStarted. */
int nSyncStarted GUARDED_BY(cs_main) = 0;
@@ -360,6 +362,14 @@ namespace {
*/
std::map<uint256, std::pair<NodeId, bool>> mapBlockSource GUARDED_BY(cs_main);
+ /** Number of peers with wtxid relay. */
+ int m_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
+
+ /** Number of outbound peers with m_chain_sync.m_protect. */
+ int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
+
+ bool AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/**
* Filter for transactions that were recently rejected by
* AcceptToMemoryPool. These are not rerequested until the chain tip
@@ -402,63 +412,89 @@ namespace {
* We use this to avoid requesting transactions that have already been
* confirnmed.
*/
- Mutex g_cs_recent_confirmed_transactions;
- std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions GUARDED_BY(g_cs_recent_confirmed_transactions);
-
- /** Blocks that are in flight, and that are in the queue to be downloaded. */
- struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
- };
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
+ Mutex m_recent_confirmed_transactions_mutex;
+ std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /** Stack of nodes which we have set to announce using compact blocks */
- std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
+ /* Returns a bool indicating whether we requested this block.
+ * Also used if a block was /not/ received and timed out or started with another peer
+ */
+ bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of preferable block download peers. */
- int nPreferredDownload GUARDED_BY(cs_main) = 0;
+ /* Mark a block as in flight
+ * Returns false, still setting pit, if the block was already in flight from the same peer
+ * pit will only be valid as long as the same cs_main lock is being held
+ */
+ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of peers with wtxid relay. */
- int g_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
+ /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
+ * at most count entries.
+ */
+ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of outbound peers with m_chain_sync.m_protect. */
- int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
+ std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
/** When our tip was last updated. */
- std::atomic<int64_t> g_last_tip_update(0);
+ std::atomic<int64_t> m_last_tip_update{0};
+
+ /** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
+ CTransactionRef FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main);
+
+ void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex);
/** Relay map (txid or wtxid -> CTransactionRef) */
typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay GUARDED_BY(cs_main);
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
- std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
+ std::deque<std::pair<std::chrono::microseconds, MapRelay::iterator>> g_relay_expiration GUARDED_BY(cs_main);
- struct IteratorComparator
- {
- template<typename I>
- bool operator()(const I& a, const I& b) const
- {
- return &(*a) < &(*b);
- }
- };
+ /**
+ * When a peer sends us a valid block, instruct it to announce blocks to us
+ * using CMPCTBLOCK if possible by adding its nodeid to the end of
+ * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
+ * removing the first element if necessary.
+ */
+ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Stack of nodes which we have set to announce using compact blocks */
+ std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
+
+ /** Number of peers from which we're downloading blocks. */
+ int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+
+ /** Storage for orphan information */
+ TxOrphanage m_orphanage;
- /** Index from the parents' COutPoint into the mapOrphanTransactions. Used
- * to remove orphan transactions from the mapOrphanTransactions */
- std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
- /** Orphan transactions in vector for quick random eviction */
- std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans);
+ void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
* these are kept in a ring buffer */
- static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
+ std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
/** Offset into vExtraTxnForCompact to insert the next tx */
- static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+
+ void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void UpdateBlockAvailability(NodeId nodeid, const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool CanDirectFetch() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequestAllowed(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv);
+ bool PrepareBlockFilterRequest(CNode& peer,
+ BlockFilterType filter_type, uint32_t start_height,
+ const uint256& stop_hash, uint32_t max_height_diff,
+ const CBlockIndex*& stop_index,
+ BlockFilterIndex*& filter_index);
+ void ProcessGetCFilters(CNode& peer, CDataStream& vRecv);
+ void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv);
+ void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv);
+};
+} // namespace
+
+namespace {
+ /** Number of preferable block download peers. */
+ int nPreferredDownload GUARDED_BY(cs_main) = 0;
} // namespace
namespace {
@@ -469,50 +505,48 @@ namespace {
* and we're no longer holding the node's locks.
*/
struct CNodeState {
- //! The peer's address
- const CService address;
//! The best known block we know this peer has announced.
- const CBlockIndex *pindexBestKnownBlock;
+ const CBlockIndex* pindexBestKnownBlock{nullptr};
//! The hash of the last unknown block this peer has announced.
- uint256 hashLastUnknownBlock;
+ uint256 hashLastUnknownBlock{};
//! The last full block we both have.
- const CBlockIndex *pindexLastCommonBlock;
+ const CBlockIndex* pindexLastCommonBlock{nullptr};
//! The best header we have sent our peer.
- const CBlockIndex *pindexBestHeaderSent;
+ const CBlockIndex* pindexBestHeaderSent{nullptr};
//! Length of current-streak of unconnecting headers announcements
- int nUnconnectingHeaders;
+ int nUnconnectingHeaders{0};
//! Whether we've started headers synchronization with this peer.
- bool fSyncStarted;
+ bool fSyncStarted{false};
//! When to potentially disconnect peer for stalling headers download
- int64_t nHeadersSyncTimeout;
+ std::chrono::microseconds m_headers_sync_timeout{0us};
//! Since when we're stalling block download progress (in microseconds), or 0.
- int64_t nStallingSince;
+ std::chrono::microseconds m_stalling_since{0us};
std::list<QueuedBlock> vBlocksInFlight;
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
- int64_t nDownloadingSince;
- int nBlocksInFlight;
- int nBlocksInFlightValidHeaders;
+ std::chrono::microseconds m_downloading_since{0us};
+ int nBlocksInFlight{0};
+ int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
- bool fPreferredDownload;
+ bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
- bool fPreferHeaders;
+ bool fPreferHeaders{false};
//! Whether this peer wants invs or cmpctblocks (when possible) for block announcements.
- bool fPreferHeaderAndIDs;
+ bool fPreferHeaderAndIDs{false};
/**
* Whether this peer will send us cmpctblocks if we request them.
* This is not used to gate request logic, as we really only care about fSupportsDesiredCmpctVersion,
* but is used as a flag to "lock in" the version of compact blocks (fWantsCmpctWitness) we send.
*/
- bool fProvidesHeaderAndIDs;
+ bool fProvidesHeaderAndIDs{false};
//! Whether this peer can give us witnesses
- bool fHaveWitness;
+ bool fHaveWitness{false};
//! Whether this peer wants witnesses in cmpctblocks/blocktxns
- bool fWantsCmpctWitness;
+ bool fWantsCmpctWitness{false};
/**
* If we've announced NODE_WITNESS to this peer: whether the peer sends witnesses in cmpctblocks/blocktxns,
* otherwise: whether this peer sends non-witnesses in cmpctblocks/blocktxns.
*/
- bool fSupportsDesiredCmpctVersion;
+ bool fSupportsDesiredCmpctVersion{false};
/** State used to enforce CHAIN_SYNC_TIMEOUT and EXTRA_PEER_CHECK_INTERVAL logic.
*
@@ -522,7 +556,7 @@ struct CNodeState {
* - its connection type is IsBlockOnlyConn() == false
* - it gave us a valid connecting header
* - we haven't reached MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT yet
- * - it has a better chain than we have
+ * - its chain tip has at least as much work as ours
*
* CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip,
* set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
@@ -540,22 +574,22 @@ struct CNodeState {
*/
struct ChainSyncTimeoutState {
//! A timeout used for checking whether our peer has sufficiently synced
- int64_t m_timeout;
+ int64_t m_timeout{0};
//! A header with the work we require on our peer's chain
- const CBlockIndex * m_work_header;
+ const CBlockIndex* m_work_header{nullptr};
//! After timeout is reached, set to true after sending getheaders
- bool m_sent_getheaders;
+ bool m_sent_getheaders{false};
//! Whether this peer is protected from disconnection due to a bad/slow chain
- bool m_protect;
+ bool m_protect{false};
};
ChainSyncTimeoutState m_chain_sync;
//! Time of last new block announcement
- int64_t m_last_block_announcement;
+ int64_t m_last_block_announcement{0};
//! Whether this peer is an inbound connection
- bool m_is_inbound;
+ const bool m_is_inbound;
//! A rolling bloom filter of all announced tx CInvs to this peer.
CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
@@ -563,31 +597,7 @@ struct CNodeState {
//! Whether this peer relays txs via wtxid
bool m_wtxid_relay{false};
- CNodeState(CAddress addrIn, bool is_inbound)
- : address(addrIn), m_is_inbound(is_inbound)
- {
- pindexBestKnownBlock = nullptr;
- hashLastUnknownBlock.SetNull();
- pindexLastCommonBlock = nullptr;
- pindexBestHeaderSent = nullptr;
- nUnconnectingHeaders = 0;
- fSyncStarted = false;
- nHeadersSyncTimeout = 0;
- nStallingSince = 0;
- nDownloadingSince = 0;
- nBlocksInFlight = 0;
- nBlocksInFlightValidHeaders = 0;
- fPreferredDownload = false;
- fPreferHeaders = false;
- fPreferHeaderAndIDs = false;
- fProvidesHeaderAndIDs = false;
- fHaveWitness = false;
- fWantsCmpctWitness = false;
- fSupportsDesiredCmpctVersion = false;
- m_chain_sync = { 0, nullptr, false, false };
- m_last_block_announcement = 0;
- m_recently_announced_invs.reset();
- }
+ CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {}
};
/** Map maintaining per-node state. */
@@ -610,9 +620,8 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-// Returns a bool indicating whether we requested this block.
-// Also used if a block was /not/ received and timed out or started with another peer
-static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
+{
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
@@ -624,20 +633,19 @@ static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs
}
if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
// First block on the queue was received, update the start download time for the next one
- state->nDownloadingSince = std::max(state->nDownloadingSince, count_microseconds(GetTime<std::chrono::microseconds>()));
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
}
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
- state->nStallingSince = 0;
+ state->m_stalling_since = 0us;
mapBlocksInFlight.erase(itInFlight);
return true;
}
return false;
}
-// returns false, still setting pit, if the block was already in flight from the same peer
-// pit will only be valid as long as the same cs_main lock is being held
-static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit)
+{
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -654,12 +662,12 @@ static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint25
MarkBlockAsReceived(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : nullptr)});
+ {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
- state->nDownloadingSince = GetTime<std::chrono::microseconds>().count();
+ state->m_downloading_since = GetTime<std::chrono::microseconds>();
}
if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
nPeersWithValidatedDownloads++;
@@ -670,48 +678,7 @@ static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint25
return true;
}
-/** Check whether the last unknown block a peer advertised is not yet known. */
-static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- CNodeState *state = State(nodeid);
- assert(state != nullptr);
-
- if (!state->hashLastUnknownBlock.IsNull()) {
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
- if (pindex && pindex->nChainWork > 0) {
- if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
- state->pindexBestKnownBlock = pindex;
- }
- state->hashLastUnknownBlock.SetNull();
- }
- }
-}
-
-/** Update tracking information about which blocks a peer is assumed to have. */
-static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
- CNodeState *state = State(nodeid);
- assert(state != nullptr);
-
- ProcessBlockAvailability(nodeid);
-
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- if (pindex && pindex->nChainWork > 0) {
- // An actually better block was announced.
- if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
- state->pindexBestKnownBlock = pindex;
- }
- } else {
- // An unknown block was announced; just assume that the latest one is the best one.
- state->hashLastUnknownBlock = hash;
- }
-}
-
-/**
- * When a peer sends us a valid block, instruct it to announce blocks to us
- * using CMPCTBLOCK if possible by adding its nodeid to the end of
- * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
- * removing the first element if necessary.
- */
-static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
{
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
@@ -727,21 +694,21 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
return;
}
}
- connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
- connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
- connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
+ m_connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [this, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ m_connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
// save BIP152 bandwidth state: we select peer to be low-bandwidth
pnodeStop->m_bip152_highbandwidth_to = false;
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
// save BIP152 bandwidth state: we select peer to be high-bandwidth
pfrom->m_bip152_highbandwidth_to = true;
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
@@ -750,18 +717,19 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
}
}
-static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::TipMayBeStale()
{
AssertLockHeld(cs_main);
- if (g_last_tip_update == 0) {
- g_last_tip_update = GetTime();
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
+ if (m_last_tip_update == 0) {
+ m_last_tip_update = GetTime();
}
- return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+ return m_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
}
-static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::CanDirectFetch()
{
- return ::ChainActive().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+ return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - m_chainparams.GetConsensus().nPowTargetSpacing * 20;
}
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -773,9 +741,42 @@ static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIV
return false;
}
-/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
- * at most count entries. */
-static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+/** Check whether the last unknown block a peer advertised is not yet known. */
+void PeerManagerImpl::ProcessBlockAvailability(NodeId nodeid) {
+ CNodeState *state = State(nodeid);
+ assert(state != nullptr);
+
+ if (!state->hashLastUnknownBlock.IsNull()) {
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
+ if (pindex && pindex->nChainWork > 0) {
+ if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
+ state->pindexBestKnownBlock = pindex;
+ }
+ state->hashLastUnknownBlock.SetNull();
+ }
+ }
+}
+
+/** Update tracking information about which blocks a peer is assumed to have. */
+void PeerManagerImpl::UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
+ CNodeState *state = State(nodeid);
+ assert(state != nullptr);
+
+ ProcessBlockAvailability(nodeid);
+
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
+ if (pindex && pindex->nChainWork > 0) {
+ // An actually better block was announced.
+ if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
+ state->pindexBestKnownBlock = pindex;
+ }
+ } else {
+ // An unknown block was announced; just assume that the latest one is the best one.
+ state->hashLastUnknownBlock = hash;
+ }
+}
+
+void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller)
{
if (count == 0)
return;
@@ -787,7 +788,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);
- if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < ::ChainActive().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
+ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
// This peer has nothing interesting.
return;
}
@@ -795,7 +796,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
if (state->pindexLastCommonBlock == nullptr) {
// Bootstrap quickly by guessing a parent of our best tip is the forking point.
// Guessing wrong in either direction is not a problem.
- state->pindexLastCommonBlock = ::ChainActive()[std::min(state->pindexBestKnownBlock->nHeight, ::ChainActive().Height())];
+ state->pindexLastCommonBlock = m_chainman.ActiveChain()[std::min(state->pindexBestKnownBlock->nHeight, m_chainman.ActiveChain().Height())];
}
// If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
@@ -804,6 +805,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
return;
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
@@ -837,7 +839,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
// We wouldn't download this block or its descendants from this peer.
return;
}
- if (pindex->nStatus & BLOCK_HAVE_DATA || ::ChainActive().Contains(pindex)) {
+ if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
} else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
@@ -911,7 +913,7 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
auto delay = std::chrono::microseconds{0};
const bool preferred = state->fPreferredDownload;
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
- if (!gtxid.IsWtxid() && g_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
+ if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
const bool overloaded = !node.HasPermission(PF_RELAY) &&
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
@@ -929,12 +931,10 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
void PeerManagerImpl::InitializeNode(CNode *pnode)
{
- CAddress addr = pnode->addr;
- std::string addrName = pnode->GetAddrName();
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn()));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(pnode->IsInboundConn()));
assert(m_txrequest.Count(nodeid) == 0);
}
{
@@ -947,7 +947,7 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
}
}
-void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const
+void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
{
std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
@@ -956,7 +956,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const
if (tx != nullptr) {
LOCK(cs_main);
- RelayTransaction(txid, tx->GetWitnessHash(), m_connman);
+ RelayTransaction(txid, tx->GetWitnessHash());
} else {
m_mempool.RemoveUnbroadcastTx(txid, true);
}
@@ -999,15 +999,15 @@ void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTim
for (const QueuedBlock& entry : state->vBlocksInFlight) {
mapBlocksInFlight.erase(entry.hash);
}
- EraseOrphansFor(nodeid);
+ WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
assert(nPeersWithValidatedDownloads >= 0);
- g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
- assert(g_outbound_peers_with_protect_from_disconnect >= 0);
- g_wtxid_relay_peers -= state->m_wtxid_relay;
- assert(g_wtxid_relay_peers >= 0);
+ m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
+ assert(m_outbound_peers_with_protect_from_disconnect >= 0);
+ m_wtxid_relay_peers -= state->m_wtxid_relay;
+ assert(m_wtxid_relay_peers >= 0);
mapNodeState.erase(nodeid);
@@ -1016,8 +1016,8 @@ void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTim
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
assert(nPeersWithValidatedDownloads == 0);
- assert(g_outbound_peers_with_protect_from_disconnect == 0);
- assert(g_wtxid_relay_peers == 0);
+ assert(m_outbound_peers_with_protect_from_disconnect == 0);
+ assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
}
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
@@ -1060,16 +1060,23 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats)
PeerRef peer = GetPeerRef(nodeid);
if (peer == nullptr) return false;
stats.m_starting_height = peer->m_starting_height;
+ // It is common for nodes with good ping times to suddenly become lagged,
+ // due to a new block arriving or other large transfer.
+ // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
+ // since pingtime does not update until the ping is complete, which might take a while.
+ // So, if a ping is taking an unusually long time in flight,
+ // the caller can immediately detect that this is happening.
+ std::chrono::microseconds ping_wait{0};
+ if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) {
+ ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
+ }
+
+ stats.m_ping_wait = ping_wait;
return true;
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapOrphanTransactions
-//
-
-static void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
@@ -1080,126 +1087,6 @@ static void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_L
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn;
}
-bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
-{
- const uint256& hash = tx->GetHash();
- if (mapOrphanTransactions.count(hash))
- return false;
-
- // Ignore big transactions, to avoid a
- // send-big-orphans memory exhaustion attack. If a peer has a legitimate
- // large transaction with a missing parent then we assume
- // it will rebroadcast it later, after the parent transaction(s)
- // have been mined or received.
- // 100 orphans, each of which is at most 100,000 bytes big is
- // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
- unsigned int sz = GetTransactionWeight(*tx);
- if (sz > MAX_STANDARD_TX_WEIGHT)
- {
- LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
- return false;
- }
-
- auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, g_orphan_list.size()});
- assert(ret.second);
- g_orphan_list.push_back(ret.first);
- // Allow for lookups in the orphan pool by wtxid, as well as txid
- g_orphans_by_wtxid.emplace(tx->GetWitnessHash(), ret.first);
- for (const CTxIn& txin : tx->vin) {
- mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
- }
-
- AddToCompactExtraTransactions(tx);
-
- LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
- mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
- return true;
-}
-
-int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
-{
- std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
- if (it == mapOrphanTransactions.end())
- return 0;
- for (const CTxIn& txin : it->second.tx->vin)
- {
- auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itPrev == mapOrphanTransactionsByPrev.end())
- continue;
- itPrev->second.erase(it);
- if (itPrev->second.empty())
- mapOrphanTransactionsByPrev.erase(itPrev);
- }
-
- size_t old_pos = it->second.list_pos;
- assert(g_orphan_list[old_pos] == it);
- if (old_pos + 1 != g_orphan_list.size()) {
- // Unless we're deleting the last entry in g_orphan_list, move the last
- // entry to the position we're deleting.
- auto it_last = g_orphan_list.back();
- g_orphan_list[old_pos] = it_last;
- it_last->second.list_pos = old_pos;
- }
- g_orphan_list.pop_back();
- g_orphans_by_wtxid.erase(it->second.tx->GetWitnessHash());
-
- mapOrphanTransactions.erase(it);
- return 1;
-}
-
-void EraseOrphansFor(NodeId peer)
-{
- LOCK(g_cs_orphans);
- int nErased = 0;
- std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- std::map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- nErased += EraseOrphanTx(maybeErase->second.tx->GetHash());
- }
- }
- if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
-}
-
-
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
-{
- LOCK(g_cs_orphans);
-
- unsigned int nEvicted = 0;
- static int64_t nNextSweep;
- int64_t nNow = GetTime();
- if (nNextSweep <= nNow) {
- // Sweep out expired orphan pool entries:
- int nErased = 0;
- int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
- std::map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- std::map<uint256, COrphanTx>::iterator maybeErase = iter++;
- if (maybeErase->second.nTimeExpire <= nNow) {
- nErased += EraseOrphanTx(maybeErase->second.tx->GetHash());
- } else {
- nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
- }
- }
- // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
- nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
- if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
- }
- FastRandomContext rng;
- while (mapOrphanTransactions.size() > nMaxOrphans)
- {
- // Evict a random orphan:
- size_t randompos = rng.randrange(g_orphan_list.size());
- EraseOrphanTx(g_orphan_list[randompos]->first);
- ++nEvicted;
- }
- return nEvicted;
-}
-
void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
{
assert(howmuch > 0);
@@ -1305,13 +1192,13 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
// active chain if they are no more than a month older (both in time, and in
// best equivalent proof of work) than the best header chain we know about and
// we fully-validated them at some point.
-static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
{
AssertLockHeld(cs_main);
- if (::ChainActive().Contains(pindex)) return true;
+ if (m_chainman.ActiveChain().Contains(pindex)) return true;
return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
- (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
+ (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
}
std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
@@ -1332,6 +1219,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
+ assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -1344,7 +1232,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
// The false positive rate of 1/1M should come out to less than 1
// transaction per day that would be inadvertently ignored (which is the
// same probability that we have in the reject filter).
- g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
+ m_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
// Stale tip checking and peer eviction are on two different timers, but we
// don't want them to get out of sync due to drift in the scheduler, so we
@@ -1359,49 +1247,21 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
}
/**
- * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
+ * Evict orphan txn pool entries based on a newly connected
* block, remember the recently confirmed transactions, and delete tracked
* announcements for them. Also save the time of the last tip update.
*/
void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
- {
- LOCK(g_cs_orphans);
-
- std::vector<uint256> vOrphanErase;
-
- for (const CTransactionRef& ptx : pblock->vtx) {
- const CTransaction& tx = *ptx;
+ m_orphanage.EraseForBlock(*pblock);
+ m_last_tip_update = GetTime();
- // Which orphan pool entries must we evict?
- for (const auto& txin : tx.vin) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(txin.prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = *(*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
- }
- }
- }
-
- // Erase orphan transactions included or precluded by this block
- if (vOrphanErase.size()) {
- int nErased = 0;
- for (const uint256& orphanHash : vOrphanErase) {
- nErased += EraseOrphanTx(orphanHash);
- }
- LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
- }
-
- g_last_tip_update = GetTime();
- }
{
- LOCK(g_cs_recent_confirmed_transactions);
+ LOCK(m_recent_confirmed_transactions_mutex);
for (const auto& ptx : pblock->vtx) {
- g_recent_confirmed_transactions->insert(ptx->GetHash());
+ m_recent_confirmed_transactions->insert(ptx->GetHash());
if (ptx->GetHash() != ptx->GetWitnessHash()) {
- g_recent_confirmed_transactions->insert(ptx->GetWitnessHash());
+ m_recent_confirmed_transactions->insert(ptx->GetWitnessHash());
}
}
}
@@ -1424,8 +1284,8 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
// block's worth of transactions in it, but that should be fine, since
// presumably the most common case of relaying a confirmed transaction
// should be just after a new block containing it is found.
- LOCK(g_cs_recent_confirmed_transactions);
- g_recent_confirmed_transactions->reset();
+ LOCK(m_recent_confirmed_transactions_mutex);
+ m_recent_confirmed_transactions->reset();
}
// All of the following cache a recent block, and are protected by cs_most_recent_block
@@ -1485,7 +1345,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
/**
* Update our best height and announce any block hashes which weren't previously
- * in ::ChainActive() to our peers.
+ * in m_chainman.ActiveChain() to our peers.
*/
void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
{
@@ -1547,10 +1407,10 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
// the tip yet so we have no way to check this directly here. Instead we
// just check that there are currently no other blocks in flight.
else if (state.IsValid() &&
- !::ChainstateActive().IsInitialBlockDownload() &&
+ !m_chainman.ActiveChainstate().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first);
}
}
if (it != mapBlockSource.end())
@@ -1563,45 +1423,44 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
//
-bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid)
{
assert(recentRejects);
- if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
+ if (m_chainman.ActiveChain().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
// If the chain tip has changed previously rejected transactions
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
// or a double-spend. Reset the rejects filter and give those
// txs a second chance.
- hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
+ hashRecentRejectsChainTip = m_chainman.ActiveChain().Tip()->GetBlockHash();
recentRejects->reset();
}
const uint256& hash = gtxid.GetHash();
- {
- LOCK(g_cs_orphans);
- if (!gtxid.IsWtxid() && mapOrphanTransactions.count(hash)) {
- return true;
- } else if (gtxid.IsWtxid() && g_orphans_by_wtxid.count(hash)) {
- return true;
- }
- }
+ if (m_orphanage.HaveTx(gtxid)) return true;
{
- LOCK(g_cs_recent_confirmed_transactions);
- if (g_recent_confirmed_transactions->contains(hash)) return true;
+ LOCK(m_recent_confirmed_transactions_mutex);
+ if (m_recent_confirmed_transactions->contains(hash)) return true;
}
- return recentRejects->contains(hash) || mempool.exists(gtxid);
+ return recentRejects->contains(hash) || m_mempool.exists(gtxid);
+}
+
+bool PeerManagerImpl::AlreadyHaveBlock(const uint256& block_hash)
+{
+ return m_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;
}
-bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManagerImpl::SendPings()
{
- return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;
+ LOCK(m_peer_mutex);
+ for(auto& it : m_peer_map) it.second->m_ping_queued = true;
}
-void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
+void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid)
{
- connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ m_connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
CNodeState* state = State(pnode->GetId());
@@ -1669,13 +1528,11 @@ static void RelayAddress(const CNode& originator,
connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
}
-void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman)
+void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv)
{
- bool send = false;
std::shared_ptr<const CBlock> a_recent_block;
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
bool fWitnessesPresentInARecentCompactBlock;
- const Consensus::Params& consensusParams = chainparams.GetConsensus();
{
LOCK(cs_most_recent_block);
a_recent_block = most_recent_block;
@@ -1686,7 +1543,7 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
bool need_activate_chain = false;
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
if (pindex) {
if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
pindex->IsValid(BLOCK_VALID_TREE)) {
@@ -1701,134 +1558,131 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
- if (pindex) {
- send = BlockRequestAllowed(pindex, consensusParams);
- if (!send) {
- LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
- }
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
+ if (!pindex) {
+ return;
+ }
+ if (!BlockRequestAllowed(pindex)) {
+ LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
+ return;
}
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
- if (send &&
- connman.OutboundTargetReached(true) &&
+ if (m_connman.OutboundTargetReached(true) &&
(((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
-
- //disconnect node
pfrom.fDisconnect = true;
- send = false;
+ return;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- 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 */) )
+ if (!pfrom.HasPermission(PF_NOBAN) && (
+ (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().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());
-
+ LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId());
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
pfrom.fDisconnect = true;
- send = false;
+ return;
}
// Pruned nodes may have deleted the block, so check whether
// it's available before trying to send.
- if (send && (pindex->nStatus & BLOCK_HAVE_DATA))
- {
- std::shared_ptr<const CBlock> pblock;
- if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
- pblock = a_recent_block;
- } else if (inv.IsMsgWitnessBlk()) {
- // Fast-path: in this case it is possible to serve the block directly from disk,
- // as the network format matches the format on disk
- std::vector<uint8_t> block_data;
- if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) {
- assert(!"cannot load block from disk");
- }
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
- // Don't set pblock as we've sent the block
- } else {
- // Send block from disk
- std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
- if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams))
- assert(!"cannot load block from disk");
- pblock = pblockRead;
+ if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
+ return;
+ }
+ std::shared_ptr<const CBlock> pblock;
+ if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
+ pblock = a_recent_block;
+ } else if (inv.IsMsgWitnessBlk()) {
+ // Fast-path: in this case it is possible to serve the block directly from disk,
+ // as the network format matches the format on disk
+ std::vector<uint8_t> block_data;
+ if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) {
+ assert(!"cannot load block from disk");
+ }
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data)));
+ // Don't set pblock as we've sent the block
+ } else {
+ // Send block from disk
+ std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
+ if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus())) {
+ assert(!"cannot load block from disk");
}
- if (pblock) {
- if (inv.IsMsgBlk()) {
- connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
- } else if (inv.IsMsgWitnessBlk()) {
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
- } else if (inv.IsMsgFilteredBlk()) {
- bool sendMerkleBlock = false;
- CMerkleBlock merkleBlock;
- 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.m_tx_relay->pfilter);
- }
- }
- if (sendMerkleBlock) {
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
- // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
- // This avoids hurting performance by pointlessly requiring a round-trip
- // Note that there is currently no way for a node to request any single transactions we didn't send here -
- // they must either disconnect and retry or request the full block.
- // Thus, the protocol spec specified allows for us to provide duplicate txn here,
- // however we MUST always provide at least what the remote peer needs
- typedef std::pair<unsigned int, uint256> PairType;
- for (PairType& pair : merkleBlock.vMatchedTxn)
- connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ pblock = pblockRead;
+ }
+ if (pblock) {
+ if (inv.IsMsgBlk()) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
+ } else if (inv.IsMsgWitnessBlk()) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
+ } else if (inv.IsMsgFilteredBlk()) {
+ bool sendMerkleBlock = false;
+ CMerkleBlock merkleBlock;
+ 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.m_tx_relay->pfilter);
}
- // else
- // no response
- } else if (inv.IsMsgCmpctBlk()) {
- // If a peer is asking for old blocks, we're almost guaranteed
- // they won't have a useful mempool to match against a compact block,
- // and we don't feel like constructing the object for them, so
- // instead we respond with the full, non-compact block.
- bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
- int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- if (CanDirectFetch(consensusParams) && pindex->nHeight >= ::ChainActive().Height() - MAX_CMPCTBLOCK_DEPTH) {
- if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
- } else {
- CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
- }
+ }
+ if (sendMerkleBlock) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
+ // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
+ // This avoids hurting performance by pointlessly requiring a round-trip
+ // Note that there is currently no way for a node to request any single transactions we didn't send here -
+ // they must either disconnect and retry or request the full block.
+ // Thus, the protocol spec specified allows for us to provide duplicate txn here,
+ // however we MUST always provide at least what the remote peer needs
+ typedef std::pair<unsigned int, uint256> PairType;
+ for (PairType& pair : merkleBlock.vMatchedTxn)
+ m_connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first]));
+ }
+ // else
+ // no response
+ } else if (inv.IsMsgCmpctBlk()) {
+ // If a peer is asking for old blocks, we're almost guaranteed
+ // they won't have a useful mempool to match against a compact block,
+ // and we don't feel like constructing the object for them, so
+ // instead we respond with the full, non-compact block.
+ bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness;
+ int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
+ if (CanDirectFetch() && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
+ if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
+ CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness);
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
+ } else {
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock));
}
}
+ }
- {
- LOCK(peer.m_block_inv_mutex);
- // Trigger the peer node to send a getblocks request for the next batch of inventory
- if (inv.hash == peer.m_continuation_block) {
- // Send immediately. This must send even if redundant,
- // and we want it right after the last block so they don't
- // wait for other stuff first.
- std::vector<CInv> vInv;
- vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash()));
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
- peer.m_continuation_block.SetNull();
- }
+ {
+ LOCK(peer.m_block_inv_mutex);
+ // Trigger the peer node to send a getblocks request for the next batch of inventory
+ if (inv.hash == peer.m_continuation_block) {
+ // Send immediately. This must send even if redundant,
+ // and we want it right after the last block so they don't
+ // wait for other stuff first.
+ std::vector<CInv> vInv;
+ vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
+ peer.m_continuation_block.SetNull();
}
}
}
-//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+CTransactionRef PeerManagerImpl::FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now)
{
- auto txinfo = mempool.info(gtxid);
+ auto txinfo = m_mempool.info(gtxid);
if (txinfo.tx) {
// If a TX could have been INVed in reply to a MEMPOOL request,
// or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
@@ -1853,7 +1707,7 @@ static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode&
return {};
}
-void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex)
+void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc)
{
AssertLockNotHeld(cs_main);
@@ -1882,17 +1736,17 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
continue;
}
- CTransactionRef tx = FindTxForGetData(mempool, pfrom, ToGenTxid(inv), mempool_req, now);
+ CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now);
if (tx) {
// WTX and WITNESS_TX imply we serialize with witness
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
- mempool.RemoveUnbroadcastTx(tx->GetHash());
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
+ m_mempool.RemoveUnbroadcastTx(tx->GetHash());
// As we're going to send tx, make sure its unconfirmed parents are made requestable.
std::vector<uint256> parent_ids_to_add;
{
- LOCK(mempool.cs);
- auto txiter = mempool.GetIter(tx->GetHash());
+ LOCK(m_mempool.cs);
+ auto txiter = m_mempool.GetIter(tx->GetHash());
if (txiter) {
const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst();
parent_ids_to_add.reserve(parents.size());
@@ -1920,7 +1774,7 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.IsGenBlkMsg()) {
- ProcessGetBlockData(pfrom, peer, chainparams, inv, connman);
+ ProcessGetBlockData(pfrom, peer, inv);
}
// else: If the first item on the queue is an unknown type, we erase it
// and continue processing the queue on the next call.
@@ -1943,7 +1797,7 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
// In normal operation, we often send NOTFOUND messages for parents of
// transactions that we relay; if a peer is missing a parent, they may
// assume we have them and request the parents from us.
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
@@ -1997,9 +1851,9 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// don't connect before giving DoS points
// - Once a headers message is received that is valid and does connect,
// nUnconnectingHeaders gets reset back to 0.
- if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -2027,7 +1881,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// If we don't have the last header, then they'll have given us
// something new (if these headers are valid).
- if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
received_new_header = true;
}
}
@@ -2055,27 +1909,26 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
- if (received_new_header && pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) {
+ if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
if (nCount == MAX_HEADERS_RESULTS) {
// Headers message had its maximum size; the peer may have more headers.
- // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
+ // TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
}
- bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
- if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) {
+ if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) {
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = pindexLast;
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
- while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
@@ -2088,7 +1941,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// very large reorg at a time we think we're close to caught up to
// the main chain -- this shouldn't really happen. Bail out on the
// direct fetch and rely on parallel download instead.
- if (!::ChainActive().Contains(pindexWalk)) {
+ if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
pindexLast->GetBlockHash().ToString(),
pindexLast->nHeight);
@@ -2102,7 +1955,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2121,7 +1974,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
// If we're in IBD, we want outbound peers that will serve us a useful
// chain. Disconnect peers that are on chains with insufficient work.
- if (::ChainstateActive().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
@@ -2129,7 +1982,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// us sync -- disconnect if it is an outbound disconnection
// candidate.
// Note: We compare their tip to nMinimumChainWork (rather than
- // ::ChainActive().Tip()) because we won't start block download
+ // m_chainman.ActiveChain().Tip()) because we won't start block download
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
@@ -2146,10 +1999,10 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// thus always subject to eviction under the bad/lagging chain logic.
// See ChainSyncTimeoutState.
if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
- 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) {
+ if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
- ++g_outbound_peers_with_protect_from_disconnect;
+ ++m_outbound_peers_with_protect_from_disconnect;
}
}
}
@@ -2174,26 +2027,18 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
const uint256 orphanHash = *orphan_work_set.begin();
orphan_work_set.erase(orphan_work_set.begin());
- auto orphan_it = mapOrphanTransactions.find(orphanHash);
- if (orphan_it == mapOrphanTransactions.end()) continue;
+ const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
+ if (porphanTx == nullptr) continue;
- const CTransactionRef porphanTx = orphan_it->second.tx;
- TxValidationState state;
- std::list<CTransactionRef> removed_txn;
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */);
+ const TxValidationState& state = result.m_state;
- if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
+ if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
- for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
- auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
- if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
- for (const auto& elem : it_by_prev->second) {
- orphan_work_set.insert(elem->first);
- }
- }
- }
- EraseOrphanTx(orphanHash);
- for (const CTransactionRef& removedTx : removed_txn) {
+ RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
+ m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set);
+ m_orphanage.EraseTx(orphanHash);
+ for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
AddToCompactExtraTransactions(removedTx);
}
break;
@@ -2201,10 +2046,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
orphanHash.ToString(),
- orphan_it->second.fromPeer,
+ from_peer,
state.ToString());
// Maybe punish peer that gave us an invalid orphan tx
- MaybePunishNodeForTx(orphan_it->second.fromPeer, state);
+ MaybePunishNodeForTx(from_peer, state);
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
@@ -2239,11 +2084,11 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
recentRejects->insert(porphanTx->GetHash());
}
}
- EraseOrphanTx(orphanHash);
+ m_orphanage.EraseTx(orphanHash);
break;
}
}
- m_mempool.check(&::ChainstateActive().CoinsTip());
+ m_mempool.check(m_chainman.ActiveChainstate());
}
/**
@@ -2252,7 +2097,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
* May disconnect from the peer in the case of a bad request.
*
* @param[in] peer The peer that we received the request from
- * @param[in] chain_params Chain parameters
* @param[in] filter_type The filter type the request is for. Must be basic filters.
* @param[in] start_height The start height for the request
* @param[in] stop_hash The stop_hash for the request
@@ -2261,11 +2105,11 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
- BlockFilterType filter_type, uint32_t start_height,
- const uint256& stop_hash, uint32_t max_height_diff,
- const CBlockIndex*& stop_index,
- BlockFilterIndex*& filter_index)
+bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer,
+ BlockFilterType filter_type, uint32_t start_height,
+ const uint256& stop_hash, uint32_t max_height_diff,
+ const CBlockIndex*& stop_index,
+ BlockFilterIndex*& filter_index)
{
const bool supported_filter_type =
(filter_type == BlockFilterType::BASIC &&
@@ -2279,10 +2123,10 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par
{
LOCK(cs_main);
- stop_index = g_chainman.m_blockman.LookupBlockIndex(stop_hash);
+ stop_index = m_chainman.m_blockman.LookupBlockIndex(stop_hash);
// Check that the stop block exists and the peer would be allowed to fetch it.
- if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
+ if (!stop_index || !BlockRequestAllowed(stop_index)) {
LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n",
peer.GetId(), stop_hash.ToString());
peer.fDisconnect = true;
@@ -2321,11 +2165,8 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par
*
* @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
- * @param[in] chain_params Chain parameters
- * @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman& connman)
+void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv)
{
uint8_t filter_type_ser;
uint32_t start_height;
@@ -2337,7 +2178,7 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, filter_type, start_height, stop_hash,
MAX_GETCFILTERS_SIZE, stop_index, filter_index)) {
return;
}
@@ -2352,7 +2193,7 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara
for (const auto& filter : filters) {
CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion())
.Make(NetMsgType::CFILTER, filter);
- connman.PushMessage(&peer, std::move(msg));
+ m_connman.PushMessage(&peer, std::move(msg));
}
}
@@ -2363,11 +2204,8 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara
*
* @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
- * @param[in] chain_params Chain parameters
- * @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman& connman)
+void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv)
{
uint8_t filter_type_ser;
uint32_t start_height;
@@ -2379,7 +2217,7 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, filter_type, start_height, stop_hash,
MAX_GETCFHEADERS_SIZE, stop_index, filter_index)) {
return;
}
@@ -2408,7 +2246,7 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar
stop_index->GetBlockHash(),
prev_header,
filter_hashes);
- connman.PushMessage(&peer, std::move(msg));
+ m_connman.PushMessage(&peer, std::move(msg));
}
/**
@@ -2418,11 +2256,8 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar
*
* @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
- * @param[in] chain_params Chain parameters
- * @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
- CConnman& connman)
+void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv)
{
uint8_t filter_type_ser;
uint256 stop_hash;
@@ -2433,7 +2268,7 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, /*start_height=*/0, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, filter_type, /*start_height=*/0, stop_hash,
/*max_height_diff=*/std::numeric_limits<uint32_t>::max(),
stop_index, filter_index)) {
return;
@@ -2459,7 +2294,7 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar
filter_type_ser,
stop_index->GetBlockHash(),
headers);
- connman.PushMessage(&peer, std::move(msg));
+ m_connman.PushMessage(&peer, std::move(msg));
}
void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
@@ -2489,6 +2324,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool fRelay = true;
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
+ if (nTime < 0) {
+ nTime = 0;
+ }
nServices = ServiceFlags(nServiceInt);
if (!pfrom.IsInboundConn())
{
@@ -2601,7 +2439,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
//
// We skip this for block-relay-only peers to avoid potentially leaking
// information about our block-relay-only connections via address relay.
- if (fListen && !::ChainstateActive().IsInitialBlockDownload())
+ if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
FastRandomContext insecure_rand;
@@ -2745,12 +2583,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- // Feature negotiation of wtxidrelay must happen between VERSION and VERACK
- // to avoid relay problems from switching after a connection is up.
+ // BIP339 defines feature negotiation of wtxidrelay, which must happen between
+ // VERSION and VERACK to avoid relay problems from switching after a connection is up.
if (msg_type == NetMsgType::WTXIDRELAY) {
if (pfrom.fSuccessfullyConnected) {
- // Disconnect peers that send wtxidrelay message after VERACK; this
- // must be negotiated between VERSION and VERACK.
+ // Disconnect peers that send a wtxidrelay message after VERACK.
LogPrint(BCLog::NET, "wtxidrelay received after verack from peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -2759,7 +2596,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
if (!State(pfrom.GetId())->m_wtxid_relay) {
State(pfrom.GetId())->m_wtxid_relay = true;
- g_wtxid_relay_peers++;
+ m_wtxid_relay_peers++;
} else {
LogPrint(BCLog::NET, "ignoring duplicate wtxidrelay from peer=%d\n", pfrom.GetId());
}
@@ -2769,10 +2606,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ // BIP155 defines feature negotiation of addrv2 and sendaddrv2, which must happen
+ // between VERSION and VERACK.
if (msg_type == NetMsgType::SENDADDRV2) {
if (pfrom.fSuccessfullyConnected) {
- // Disconnect peers that send SENDADDRV2 message after VERACK; this
- // must be negotiated between VERSION and VERACK.
+ // Disconnect peers that send a SENDADDRV2 message after VERACK.
LogPrint(BCLog::NET, "sendaddrv2 received after verack from peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -2901,7 +2739,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
} else if (inv.IsGenTxMsg()) {
const GenTxid gtxid = ToGenTxid(inv);
- const bool fAlreadyHave = AlreadyHaveTx(gtxid, m_mempool);
+ const bool fAlreadyHave = AlreadyHaveTx(gtxid);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
pfrom.AddKnownTx(inv.hash);
@@ -2918,7 +2756,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (best_block != nullptr) {
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
@@ -2943,7 +2781,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(peer->m_getdata_requests_mutex);
peer->m_getdata_requests.insert(peer->m_getdata_requests.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(pfrom, *peer, interruptMsgProc);
}
return;
@@ -2974,7 +2812,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
a_recent_block = most_recent_block;
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, m_chainparams, a_recent_block)) {
+ if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -2982,14 +2820,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
// Find the last block the caller has in the main chain
- const CBlockIndex* pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
+ const CBlockIndex* pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator);
// Send the rest of the chain
if (pindex)
- pindex = ::ChainActive().Next(pindex);
+ pindex = m_chainman.ActiveChain().Next(pindex);
int nLimit = 500;
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
- for (; pindex; pindex = ::ChainActive().Next(pindex))
+ for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
@@ -2999,7 +2837,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
- if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
+ if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
@@ -3035,13 +2873,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(req.blockhash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(req.blockhash);
if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
return;
}
- if (pindex->nHeight >= ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
+ if (pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_BLOCKTXN_DEPTH) {
CBlock block;
bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
assert(ret);
@@ -3079,7 +2917,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
return;
}
@@ -3089,12 +2927,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (locator.IsNull())
{
// If locator is null, return the hashStop block
- pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop);
+ pindex = m_chainman.m_blockman.LookupBlockIndex(hashStop);
if (!pindex) {
return;
}
- if (!BlockRequestAllowed(pindex, m_chainparams.GetConsensus())) {
+ if (!BlockRequestAllowed(pindex)) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
return;
}
@@ -3102,23 +2940,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
else
{
// Find the last block the caller has in the main chain
- pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
+ pindex = m_chainman.m_blockman.FindForkInGlobalIndex(m_chainman.ActiveChain(), locator);
if (pindex)
- pindex = ::ChainActive().Next(pindex);
+ pindex = m_chainman.ActiveChain().Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
std::vector<CBlock> vHeaders;
int nLimit = MAX_HEADERS_RESULTS;
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
- for (; pindex; pindex = ::ChainActive().Next(pindex))
+ for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
- // pindex can be nullptr either if we sent ::ChainActive().Tip() OR
- // if our peer has ::ChainActive().Tip() (and thus we are sending an empty
+ // pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR
+ // if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
// pindexBestHeaderSent to be our tip.
//
@@ -3129,7 +2967,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// without the new block. By resetting the BestHeaderSent, we ensure we
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
- nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
+ nodestate->pindexBestHeaderSent = pindex ? pindex : m_chainman.ActiveChain().Tip();
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return;
}
@@ -3182,7 +3020,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool)) {
+ if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid))) {
if (pfrom.HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
@@ -3191,30 +3029,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
}
}
return;
}
- TxValidationState state;
- std::list<CTransactionRef> lRemovedTxn;
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
+ const TxValidationState& state = result.m_state;
- if (AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
- m_mempool.check(&::ChainstateActive().CoinsTip());
+ if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ m_mempool.check(m_chainman.ActiveChainstate());
// As this version of the transaction was acceptable, we can forget about any
// requests for it.
m_txrequest.ForgetTxHash(tx.GetHash());
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
- if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
- for (const auto& elem : it_by_prev->second) {
- peer->m_orphan_work_set.insert(elem->first);
- }
- }
- }
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
+ m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
pfrom.nLastTXTime = GetTime();
@@ -3223,7 +3054,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
tx.GetHash().ToString(),
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
- for (const CTransactionRef& removedTx : lRemovedTxn) {
+ for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3261,19 +3092,22 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// protocol for getting all unconfirmed parents.
const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
pfrom.AddKnownTx(parent_txid);
- if (!AlreadyHaveTx(gtxid, m_mempool)) AddTxAnnouncement(pfrom, gtxid, current_time);
+ if (!AlreadyHaveTx(gtxid)) AddTxAnnouncement(pfrom, gtxid, current_time);
+ }
+
+ if (m_orphanage.AddTx(ptx, pfrom.GetId())) {
+ AddToCompactExtraTransactions(ptx);
}
- AddOrphanTx(ptx, pfrom.GetId());
// Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
m_txrequest.ForgetTxHash(tx.GetHash());
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
+ // DoS prevention: do not allow m_orphanage 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);
+ unsigned int nEvicted = m_orphanage.LimitOrphans(nMaxOrphanTx);
if (nEvicted > 0) {
- LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted);
+ LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
}
} else {
LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
@@ -3366,14 +3200,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(cs_main);
- if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
- if (!::ChainstateActive().IsInitialBlockDownload())
- m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ if (!m_chainman.ActiveChainstate().IsInitialBlockDownload())
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
return;
}
- if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
+ if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
received_new_header = true;
}
}
@@ -3413,7 +3247,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// If this was a new header with more work than our tip, update the
// peer's last block announcement time
- if (received_new_header && pindex->nChainWork > ::ChainActive().Tip()->nChainWork) {
+ if (received_new_header && pindex->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
@@ -3423,7 +3257,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
return;
- if (pindex->nChainWork <= ::ChainActive().Tip()->nChainWork || // We know something better
+ if (pindex->nChainWork <= m_chainman.ActiveChain().Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
if (fAlreadyInFlight) {
// We requested this block for some reason, but our mempool will probably be useless
@@ -3436,8 +3270,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
- if (!fAlreadyInFlight && !CanDirectFetch(m_chainparams.GetConsensus()))
+ if (!fAlreadyInFlight && !CanDirectFetch()) {
return;
+ }
if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
@@ -3447,11 +3282,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// We want to be a bit conservative just to be extra careful about DoS
// possibilities in compact block processing...
- if (pindex->nHeight <= ::ChainActive().Height() + 2) {
+ if (pindex->nHeight <= m_chainman.ActiveChain().Height() + 2) {
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -3807,15 +3642,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom.nPingNonceSent != 0) {
- if (nonce == pfrom.nPingNonceSent) {
+ if (peer->m_ping_nonce_sent != 0) {
+ if (nonce == peer->m_ping_nonce_sent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
- const auto ping_time = ping_end - pfrom.m_ping_start.load();
+ const auto ping_time = ping_end - peer->m_ping_start.load();
if (ping_time.count() >= 0) {
- // Successful ping time measurement, replace previous
- pfrom.nPingUsecTime = count_microseconds(ping_time);
- pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), count_microseconds(ping_time));
+ // Let connman know about this successful ping-pong
+ pfrom.PongReceived(ping_time);
} else {
// This should never happen
sProblem = "Timing mishap";
@@ -3842,12 +3676,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
pfrom.GetId(),
sProblem,
- pfrom.nPingNonceSent,
+ peer->m_ping_nonce_sent,
nonce,
nAvail);
}
if (bPingFinished) {
- pfrom.nPingNonceSent = 0;
+ peer->m_ping_nonce_sent = 0;
}
return;
}
@@ -3931,17 +3765,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
if (msg_type == NetMsgType::GETCFILTERS) {
- ProcessGetCFilters(pfrom, vRecv, m_chainparams, m_connman);
+ ProcessGetCFilters(pfrom, vRecv);
return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(pfrom, vRecv, m_chainparams, m_connman);
+ ProcessGetCFHeaders(pfrom, vRecv);
return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman);
+ ProcessGetCFCheckPt(pfrom, vRecv);
return;
}
@@ -3966,43 +3800,40 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
-bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode)
+bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
{
- const NodeId peer_id{pnode.GetId()};
- PeerRef peer = GetPeerRef(peer_id);
- if (peer == nullptr) return false;
-
{
- LOCK(peer->m_misbehavior_mutex);
+ LOCK(peer.m_misbehavior_mutex);
// There's nothing to do if the m_should_discourage flag isn't set
- if (!peer->m_should_discourage) return false;
+ if (!peer.m_should_discourage) return false;
- peer->m_should_discourage = false;
+ peer.m_should_discourage = false;
} // peer.m_misbehavior_mutex
if (pnode.HasPermission(PF_NOBAN)) {
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
- LogPrintf("Warning: not punishing noban peer %d!\n", peer_id);
+ LogPrintf("Warning: not punishing noban peer %d!\n", peer.m_id);
return false;
}
if (pnode.IsManualConn()) {
// We never disconnect or discourage manual peers for bad behavior
- LogPrintf("Warning: not punishing manually connected peer %d!\n", peer_id);
+ LogPrintf("Warning: not punishing manually connected peer %d!\n", peer.m_id);
return false;
}
if (pnode.addr.IsLocal()) {
// We disconnect local peers for bad behavior but don't discourage (since that would discourage
// all peers on the same local address)
- LogPrintf("Warning: disconnecting but not discouraging local peer %d!\n", peer_id);
+ LogPrint(BCLog::NET, "Warning: disconnecting but not discouraging %s peer %d!\n",
+ pnode.m_inbound_onion ? "inbound onion" : "local", peer.m_id);
pnode.fDisconnect = true;
return true;
}
// Normal case: Disconnect the peer and discourage all nodes sharing the address
- LogPrint(BCLog::NET, "Disconnecting and discouraging peer %d!\n", peer_id);
+ LogPrint(BCLog::NET, "Disconnecting and discouraging peer %d!\n", peer.m_id);
if (m_banman) m_banman->Discourage(pnode.addr);
m_connman.DisconnectNode(pnode.addr);
return true;
@@ -4018,7 +3849,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
{
LOCK(peer->m_getdata_requests_mutex);
if (!peer->m_getdata_requests.empty()) {
- ProcessGetData(*pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, *peer, interruptMsgProc);
}
}
@@ -4099,7 +3930,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// their chain has more work than ours, we should sync to it,
// unless it's invalid, in which case we should find that out and
// disconnect from them elsewhere).
- if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork) {
+ if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= m_chainman.ActiveChain().Tip()->nChainWork) {
if (state.m_chain_sync.m_timeout != 0) {
state.m_chain_sync.m_timeout = 0;
state.m_chain_sync.m_work_header = nullptr;
@@ -4111,7 +3942,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
// where we checked against our tip.
// Either way, set a new timeout based on current tip.
state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT;
- state.m_chain_sync.m_work_header = ::ChainActive().Tip();
+ state.m_chain_sync.m_work_header = m_chainman.ActiveChain().Tip();
state.m_chain_sync.m_sent_getheaders = false;
} else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) {
// No evidence yet that our peer has synced to a chain with work equal to that
@@ -4124,7 +3955,7 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
} else {
assert(state.m_chain_sync.m_work_header);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -4250,8 +4081,8 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
if (time_in_seconds > m_stale_tip_check_time) {
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(m_chainparams.GetConsensus())) {
- LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
+ if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) {
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - m_last_tip_update);
m_connman.SetTryNewOutboundPeer(true);
} else if (m_connman.GetTryNewOutboundPeer()) {
m_connman.SetTryNewOutboundPeer(false);
@@ -4259,12 +4090,56 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
}
- if (!m_initial_sync_finished && CanDirectFetch(m_chainparams.GetConsensus())) {
+ if (!m_initial_sync_finished && CanDirectFetch()) {
m_connman.StartExtraBlockRelayPeers();
m_initial_sync_finished = true;
}
}
+void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer)
+{
+ // Use mockable time for ping timeouts.
+ // This means that setmocktime may cause pings to time out.
+ auto now = GetTime<std::chrono::microseconds>();
+
+ if (m_connman.RunInactivityChecks(node_to) && peer.m_ping_nonce_sent &&
+ now > peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) {
+ LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), peer.m_id);
+ node_to.fDisconnect = true;
+ return;
+ }
+
+ const CNetMsgMaker msgMaker(node_to.GetCommonVersion());
+ bool pingSend = false;
+
+ if (peer.m_ping_queued) {
+ // RPC ping request by user
+ pingSend = true;
+ }
+
+ if (peer.m_ping_nonce_sent == 0 && now > peer.m_ping_start.load() + PING_INTERVAL) {
+ // Ping automatically sent as a latency probe & keepalive.
+ pingSend = true;
+ }
+
+ if (pingSend) {
+ uint64_t nonce = 0;
+ while (nonce == 0) {
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
+ }
+ peer.m_ping_queued = false;
+ peer.m_ping_start = now;
+ if (node_to.GetCommonVersion() > BIP0031_VERSION) {
+ peer.m_ping_nonce_sent = nonce;
+ m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING, nonce));
+ } else {
+ // Peer is too old to support ping command with nonce, pong will never arrive.
+ peer.m_ping_nonce_sent = 0;
+ m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING));
+ }
+ }
+}
+
namespace {
class CompareInvMempoolOrder
{
@@ -4289,11 +4164,12 @@ public:
bool PeerManagerImpl::SendMessages(CNode* pto)
{
PeerRef peer = GetPeerRef(pto->GetId());
+ if (!peer) return false;
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
// We must call MaybeDiscourageAndDisconnect first, to ensure that we'll
// disconnect misbehaving peers even before the version handshake is complete.
- if (MaybeDiscourageAndDisconnect(*pto)) return true;
+ if (MaybeDiscourageAndDisconnect(*pto, *peer)) return true;
// Don't send anything until the version handshake is complete
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
@@ -4302,34 +4178,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// If we get here, the outgoing message serialization version is set and can't change.
const CNetMsgMaker msgMaker(pto->GetCommonVersion());
- //
- // Message: ping
- //
- bool pingSend = false;
- if (pto->fPingQueued) {
- // RPC ping request by user
- pingSend = true;
- }
- if (pto->nPingNonceSent == 0 && pto->m_ping_start.load() + PING_INTERVAL < GetTime<std::chrono::microseconds>()) {
- // Ping automatically sent as a latency probe & keepalive.
- pingSend = true;
- }
- if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
- }
- pto->fPingQueued = false;
- pto->m_ping_start = GetTime<std::chrono::microseconds>();
- if (pto->GetCommonVersion() > BIP0031_VERSION) {
- pto->nPingNonceSent = nonce;
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
- } else {
- // Peer is too old to support ping command with nonce, pong will never arrive.
- pto->nPingNonceSent = 0;
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
- }
- }
+ MaybeSendPing(*pto, *peer);
+
+ // MaybeSendPing may have marked peer for disconnection
+ if (pto->fDisconnect) return true;
{
LOCK(cs_main);
@@ -4339,7 +4191,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Address refresh broadcast
auto current_time = GetTime<std::chrono::microseconds>();
- if (pto->RelayAddrsWithConn() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) {
+ if (fListen && pto->RelayAddrsWithConn() &&
+ !m_chainman.ActiveChainstate().IsInitialBlockDownload() &&
+ pto->m_next_local_addr_send < current_time) {
// If we've sent before, clear the bloom filter for the peer, so that our
// self-announcement will actually go out.
// This might be unnecessary if the bloom filter has already rolled
@@ -4349,7 +4203,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_next_local_addr_send != 0us) {
pto->m_addr_known->reset();
}
- AdvertiseLocal(pto);
+ if (std::optional<CAddress> local_addr = GetLocalAddrForPeer(pto)) {
+ FastRandomContext insecure_rand;
+ pto->PushAddress(*local_addr, insecure_rand);
+ }
pto->m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -4396,13 +4253,19 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
- pindexBestHeader = ::ChainActive().Tip();
+ pindexBestHeader = m_chainman.ActiveChain().Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
- state.nHeadersSyncTimeout = count_microseconds(current_time) + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
+ state.m_headers_sync_timeout = current_time + HEADERS_DOWNLOAD_TIMEOUT_BASE +
+ (
+ // Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
+ // to maintain precision
+ std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
+ (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / consensusParams.nPowTargetSpacing
+ );
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
/* If possible, start at the block preceding the currently
@@ -4415,7 +4278,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexStart), uint256()));
}
}
@@ -4442,11 +4305,11 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
bool fFoundStartingHeader = false;
// Try to find first header that our peer doesn't have, and
// then send all headers past that one. If we come across any
- // headers that aren't on ::ChainActive(), give up.
+ // headers that aren't on m_chainman.ActiveChain(), give up.
for (const uint256& hash : peer->m_blocks_for_headers_relay) {
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
assert(pindex);
- if (::ChainActive()[pindex->nHeight] != pindex) {
+ if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) {
// Bail out if we reorged away from this block
fRevertToInv = true;
break;
@@ -4536,15 +4399,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// in the past.
if (!peer->m_blocks_for_headers_relay.empty()) {
const uint256& hashToAnnounce = peer->m_blocks_for_headers_relay.back();
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);
+ const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);
assert(pindex);
// Warn if we're announcing a block that is not on the main chain.
// This should be very rare and could be optimized out.
// Just log for now.
- if (::ChainActive()[pindex->nHeight] != pindex) {
+ if (m_chainman.ActiveChain()[pindex->nHeight] != pindex) {
LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n",
- hashToAnnounce.ToString(), ::ChainActive().Tip()->GetBlockHash().ToString());
+ hashToAnnounce.ToString(), m_chainman.ActiveChain().Tip()->GetBlockHash().ToString());
}
// If the peer's chain has this block, don't inv it back.
@@ -4583,10 +4446,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->IsInboundConn()) {
- pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{m_connman.PoissonNextSendInbound(count_microseconds(current_time), INVENTORY_BROADCAST_INTERVAL)};
+ pto->m_tx_relay->nNextInvSend = m_connman.PoissonNextSendInbound(current_time, INBOUND_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(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
+ pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
}
}
@@ -4674,20 +4536,20 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
nRelayedTransactions++;
{
// Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < count_microseconds(current_time))
+ while (!g_relay_expiration.empty() && g_relay_expiration.front().first < current_time)
{
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
+ mapRelay.erase(g_relay_expiration.front().second);
+ g_relay_expiration.pop_front();
}
auto ret = mapRelay.emplace(txid, std::move(txinfo.tx));
if (ret.second) {
- vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret.first);
+ g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret.first);
}
// Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
auto ret2 = mapRelay.emplace(wtxid, ret.first->second);
if (ret2.second) {
- vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret2.first);
+ g_relay_expiration.emplace_back(current_time + RELAY_TX_CACHE_TIME, ret2.first);
}
}
if (vInv.size() == MAX_INV_SZ) {
@@ -4712,7 +4574,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Detect whether we're stalling
current_time = GetTime<std::chrono::microseconds>();
- if (state.nStallingSince && state.nStallingSince < count_microseconds(current_time) - 1000000 * BLOCK_STALLING_TIMEOUT) {
+ if (state.m_stalling_since.count() && state.m_stalling_since < current_time - BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
// should only happen during initial block download.
@@ -4720,7 +4582,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
pto->fDisconnect = true;
return true;
}
- // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
+ // In case there is a block that has been in flight from this peer for block_interval * (1 + 0.5 * N)
// (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
// We compensate for other peers to prevent killing off peers due to our own downstream link
// being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
@@ -4728,17 +4590,17 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
- if (count_microseconds(current_time) > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
+ if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
}
// Check for headers sync timeouts
- if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
+ if (state.fSyncStarted && state.m_headers_sync_timeout < std::chrono::microseconds::max()) {
// Detect whether this is a stalling initial-headers-sync peer
if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
- if (count_microseconds(current_time) > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
+ if (current_time > state.m_headers_sync_timeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without the noban permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
@@ -4757,13 +4619,13 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// this peer (eventually).
state.fSyncStarted = false;
nSyncStarted--;
- state.nHeadersSyncTimeout = 0;
+ state.m_headers_sync_timeout = 0us;
}
}
} else {
// After we've caught up once, reset the timeout so we can't trigger
// disconnect later.
- state.nHeadersSyncTimeout = std::numeric_limits<int64_t>::max();
+ state.m_headers_sync_timeout = std::chrono::microseconds::max();
}
}
@@ -4775,20 +4637,20 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
- if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !m_chainman.ActiveChainstate().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
- FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
+ FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(m_mempool, pto->GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
if (state.nBlocksInFlight == 0 && staller != -1) {
- if (State(staller)->nStallingSince == 0) {
- State(staller)->nStallingSince = count_microseconds(current_time);
+ if (State(staller)->m_stalling_since == 0us) {
+ State(staller)->m_stalling_since = current_time;
LogPrint(BCLog::NET, "Stall started peer=%d\n", staller);
}
}
@@ -4804,7 +4666,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
entry.second.GetHash().ToString(), entry.first);
}
for (const GenTxid& gtxid : requestable) {
- if (!AlreadyHaveTx(gtxid, m_mempool)) {
+ if (!AlreadyHaveTx(gtxid)) {
LogPrint(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
gtxid.GetHash().ToString(), pto->GetId());
vGetData.emplace_back(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
@@ -4841,10 +4703,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
- pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) - 1;
+ pto->m_tx_relay->m_next_send_feefilter = 0us;
}
}
- if (count_microseconds(current_time) > pto->m_tx_relay->nextSendTimeFeeFilter) {
+ if (current_time > pto->m_tx_relay->m_next_send_feefilter) {
CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least minRelayTxFee
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
@@ -4852,28 +4714,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->m_tx_relay->lastSentFeeFilter = filterToSend;
}
- pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(count_microseconds(current_time), AVG_FEEFILTER_BROADCAST_INTERVAL);
+ pto->m_tx_relay->m_next_send_feefilter = PoissonNextSend(current_time, 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 (count_microseconds(current_time) + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
+ else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < pto->m_tx_relay->m_next_send_feefilter &&
(currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
- pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ pto->m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
}
}
} // release cs_main
return true;
}
-
-class CNetProcessingCleanup
-{
-public:
- CNetProcessingCleanup() {}
- ~CNetProcessingCleanup() {
- // orphan transactions
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
- g_orphans_by_wtxid.clear();
- }
-};
-static CNetProcessingCleanup instance_of_cnetprocessingcleanup;
diff --git a/src/net_processing.h b/src/net_processing.h
index eaa3b142a8..f6f2d73721 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -15,7 +15,6 @@ class CTxMemPool;
class ChainstateManager;
extern RecursiveMutex cs_main;
-extern RecursiveMutex g_cs_orphans;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
@@ -30,6 +29,7 @@ struct CNodeStateStats {
int nSyncHeight = -1;
int nCommonHeight = -1;
int m_starting_height = -1;
+ std::chrono::microseconds m_ping_wait;
std::vector<int> vHeightInFlight;
};
@@ -47,6 +47,13 @@ public:
/** Whether this node ignores txs received over p2p. */
virtual bool IgnoresIncomingTxs() = 0;
+ /** Relay transaction to all peers. */
+ virtual void RelayTransaction(const uint256& txid, const uint256& wtxid)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main) = 0;
+
+ /** Send ping message to all peers */
+ virtual void SendPings() = 0;
+
/** Set the best height */
virtual void SetBestHeight(int height) = 0;
@@ -68,7 +75,4 @@ public:
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0;
};
-/** Relay transaction to every node */
-void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 85e46fd373..69edc15c66 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -221,25 +221,34 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS
}; // namespace torv3
-/**
- * Parse a TOR address and set this object to it.
- *
- * @returns Whether or not the operation was successful.
- *
- * @see CNetAddr::IsTor()
- */
-bool CNetAddr::SetSpecial(const std::string& str)
+bool CNetAddr::SetSpecial(const std::string& addr)
+{
+ if (!ValidAsCString(addr)) {
+ return false;
+ }
+
+ if (SetTor(addr)) {
+ return true;
+ }
+
+ if (SetI2P(addr)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool CNetAddr::SetTor(const std::string& addr)
{
static const char* suffix{".onion"};
static constexpr size_t suffix_len{6};
- if (!ValidAsCString(str) || str.size() <= suffix_len ||
- str.substr(str.size() - suffix_len) != suffix) {
+ if (addr.size() <= suffix_len || addr.substr(addr.size() - suffix_len) != suffix) {
return false;
}
bool invalid;
- const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
+ const auto& input = DecodeBase32(addr.substr(0, addr.size() - suffix_len).c_str(), &invalid);
if (invalid) {
return false;
@@ -275,6 +284,34 @@ bool CNetAddr::SetSpecial(const std::string& str)
return false;
}
+bool CNetAddr::SetI2P(const std::string& addr)
+{
+ // I2P addresses that we support consist of 52 base32 characters + ".b32.i2p".
+ static constexpr size_t b32_len{52};
+ static const char* suffix{".b32.i2p"};
+ static constexpr size_t suffix_len{8};
+
+ if (addr.size() != b32_len + suffix_len || ToLower(addr.substr(b32_len)) != suffix) {
+ return false;
+ }
+
+ // Remove the ".b32.i2p" suffix and pad to a multiple of 8 chars, so DecodeBase32()
+ // can decode it.
+ const std::string b32_padded = addr.substr(0, b32_len) + "====";
+
+ bool invalid;
+ const auto& address_bytes = DecodeBase32(b32_padded.c_str(), &invalid);
+
+ if (invalid || address_bytes.size() != ADDR_I2P_SIZE) {
+ return false;
+ }
+
+ m_net = NET_I2P;
+ m_addr.assign(address_bytes.begin(), address_bytes.end());
+
+ return true;
+}
+
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
m_net = NET_IPV4;
@@ -841,6 +878,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well
case NET_ONION: return REACH_PRIVATE;
}
+ case NET_I2P:
+ switch (ourNet) {
+ case NET_I2P: return REACH_PRIVATE;
+ default: return REACH_DEFAULT;
+ }
case NET_TEREDO:
switch(ourNet) {
default: return REACH_DEFAULT;
diff --git a/src/netaddress.h b/src/netaddress.h
index b9eade7fd5..897ce46cda 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -36,7 +36,7 @@ static constexpr int ADDRV2_FORMAT = 0x20000000;
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
* Keep these sequential starting from 0 and `NET_MAX` as the last entry.
- * We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate
+ * We have loops like `for (int i = 0; i < NET_MAX; ++i)` that expect to iterate
* over all enum values and also `GetExtNetwork()` "extends" this enum by
* introducing standalone constants starting from `NET_MAX`.
*/
@@ -151,7 +151,16 @@ class CNetAddr
bool SetInternal(const std::string& name);
- bool SetSpecial(const std::string &strName); // for Tor addresses
+ /**
+ * Parse a Tor or I2P address and set this object to it.
+ * @param[in] addr Address to parse, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
+ */
+ bool SetSpecial(const std::string& addr);
+
bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@@ -249,6 +258,25 @@ class CNetAddr
private:
/**
+ * Parse a Tor address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * 6hzph5hv6337r6p2.onion.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor()
+ */
+ bool SetTor(const std::string& addr);
+
+ /**
+ * Parse an I2P address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsI2P()
+ */
+ bool SetI2P(const std::string& addr);
+
+ /**
* BIP155 network ids recognized by this software.
*/
enum BIP155Network : uint8_t {
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 264029d8a2..49e455aa84 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -5,15 +5,21 @@
#include <netbase.h>
+#include <compat.h>
#include <sync.h>
#include <tinyformat.h>
+#include <util/sock.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/time.h>
#include <atomic>
+#include <chrono>
#include <cstdint>
+#include <functional>
#include <limits>
+#include <memory>
#ifndef WIN32
#include <fcntl.h>
@@ -25,10 +31,6 @@
#include <poll.h>
#endif
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif
-
// Settings
static Mutex g_proxyinfo_mutex;
static proxyType proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
@@ -37,9 +39,53 @@ int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
-static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
+int g_socks5_recv_timeout = 20 * 1000;
static std::atomic<bool> interruptSocks5Recv(false);
+std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
+{
+ addrinfo ai_hint{};
+ // We want a TCP port, which is a streaming socket type
+ ai_hint.ai_socktype = SOCK_STREAM;
+ ai_hint.ai_protocol = IPPROTO_TCP;
+ // We don't care which address family (IPv4 or IPv6) is returned
+ ai_hint.ai_family = AF_UNSPEC;
+ // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
+ // return addresses whose family we have an address configured for.
+ //
+ // If we don't allow lookups, then use the AI_NUMERICHOST flag for
+ // getaddrinfo to only decode numerical network addresses and suppress
+ // hostname lookups.
+ ai_hint.ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
+
+ addrinfo* ai_res{nullptr};
+ const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
+ if (n_err != 0) {
+ return {};
+ }
+
+ // Traverse the linked list starting with ai_trav.
+ addrinfo* ai_trav{ai_res};
+ std::vector<CNetAddr> resolved_addresses;
+ while (ai_trav != nullptr) {
+ if (ai_trav->ai_family == AF_INET) {
+ assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in));
+ resolved_addresses.emplace_back(reinterpret_cast<sockaddr_in*>(ai_trav->ai_addr)->sin_addr);
+ }
+ if (ai_trav->ai_family == AF_INET6) {
+ assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6));
+ const sockaddr_in6* s6{reinterpret_cast<sockaddr_in6*>(ai_trav->ai_addr)};
+ resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id);
+ }
+ ai_trav = ai_trav->ai_next;
+ }
+ freeaddrinfo(ai_res);
+
+ return resolved_addresses;
+}
+
+DNSLookupFn g_dns_lookup{WrappedGetAddrInfo};
+
enum Network ParseNetwork(const std::string& net_in) {
std::string net = ToLower(net_in);
if (net == "ipv4") return NET_IPV4;
@@ -49,13 +95,16 @@ enum Network ParseNetwork(const std::string& net_in) {
LogPrintf("Warning: net name 'tor' is deprecated and will be removed in the future. You should use 'onion' instead.\n");
return NET_ONION;
}
+ if (net == "i2p") {
+ return NET_I2P;
+ }
return NET_UNROUTABLE;
}
std::string GetNetworkName(enum Network net)
{
switch (net) {
- case NET_UNROUTABLE: return "unroutable";
+ case NET_UNROUTABLE: return "not_publicly_routable";
case NET_IPV4: return "ipv4";
case NET_IPV6: return "ipv6";
case NET_ONION: return "onion";
@@ -68,7 +117,21 @@ std::string GetNetworkName(enum Network net)
assert(false);
}
-bool static LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
+std::vector<std::string> GetNetworkNames(bool append_unroutable)
+{
+ std::vector<std::string> names;
+ for (int n = 0; n < NET_MAX; ++n) {
+ const enum Network network{static_cast<Network>(n)};
+ if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ names.emplace_back(GetNetworkName(network));
+ }
+ if (append_unroutable) {
+ names.emplace_back(GetNetworkName(NET_UNROUTABLE));
+ }
+ return names;
+}
+
+static bool LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{
vIP.clear();
@@ -90,73 +153,20 @@ bool static LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, un
}
}
- struct addrinfo aiHint;
- memset(&aiHint, 0, sizeof(struct addrinfo));
-
- // We want a TCP port, which is a streaming socket type
- aiHint.ai_socktype = SOCK_STREAM;
- aiHint.ai_protocol = IPPROTO_TCP;
- // We don't care which address family (IPv4 or IPv6) is returned
- aiHint.ai_family = AF_UNSPEC;
- // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
- // return addresses whose family we have an address configured for.
- //
- // If we don't allow lookups, then use the AI_NUMERICHOST flag for
- // getaddrinfo to only decode numerical network addresses and suppress
- // hostname lookups.
- aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
- struct addrinfo *aiRes = nullptr;
- int nErr = getaddrinfo(name.c_str(), nullptr, &aiHint, &aiRes);
- if (nErr)
- return false;
-
- // Traverse the linked list starting with aiTrav, add all non-internal
- // IPv4,v6 addresses to vIP while respecting nMaxSolutions.
- struct addrinfo *aiTrav = aiRes;
- while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
- {
- CNetAddr resolved;
- if (aiTrav->ai_family == AF_INET)
- {
- assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
- resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr);
- }
-
- if (aiTrav->ai_family == AF_INET6)
- {
- assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
- struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr;
- resolved = CNetAddr(s6->sin6_addr, s6->sin6_scope_id);
+ for (const CNetAddr& resolved : dns_lookup_function(name, fAllowLookup)) {
+ if (nMaxSolutions > 0 && vIP.size() >= nMaxSolutions) {
+ break;
}
/* Never allow resolving to an internal address. Consider any such result invalid */
if (!resolved.IsInternal()) {
vIP.push_back(resolved);
}
-
- aiTrav = aiTrav->ai_next;
}
- freeaddrinfo(aiRes);
-
return (vIP.size() > 0);
}
-/**
- * Resolve a host string to its corresponding network addresses.
- *
- * @param name The string representing a host. Could be a name or a numerical
- * IP address (IPv6 addresses in their bracketed form are
- * allowed).
- * @param[out] vIP The resulting network addresses to which the specified host
- * string resolved.
- *
- * @returns Whether or not the specified host string successfully resolved to
- * any resulting network addresses.
- *
- * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int)
- * for additional parameter descriptions.
- */
-bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
+bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(name)) {
return false;
@@ -168,59 +178,33 @@ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned in
strHost = strHost.substr(1, strHost.size() - 2);
}
- return LookupIntern(strHost, vIP, nMaxSolutions, fAllowLookup);
+ return LookupIntern(strHost, vIP, nMaxSolutions, fAllowLookup, dns_lookup_function);
}
- /**
- * Resolve a host string to its first corresponding network address.
- *
- * @see LookupHost(const std::string&, std::vector<CNetAddr>&, unsigned int, bool) for
- * additional parameter descriptions.
- */
-bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup)
+bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(name)) {
return false;
}
std::vector<CNetAddr> vIP;
- LookupHost(name, vIP, 1, fAllowLookup);
+ LookupHost(name, vIP, 1, fAllowLookup, dns_lookup_function);
if(vIP.empty())
return false;
addr = vIP.front();
return true;
}
-/**
- * Resolve a service string to its corresponding service.
- *
- * @param name The string representing a service. Could be a name or a
- * numerical IP address (IPv6 addresses should be in their
- * disambiguated bracketed form), optionally followed by a port
- * number. (e.g. example.com:8333 or
- * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
- * @param[out] vAddr The resulting services to which the specified service string
- * resolved.
- * @param portDefault The default port for resulting services if not specified
- * by the service string.
- * @param fAllowLookup Whether or not hostname lookups are permitted. If yes,
- * external queries may be performed.
- * @param nMaxSolutions The maximum number of results we want, specifying 0
- * means "as many solutions as we get."
- *
- * @returns Whether or not the service string successfully resolved to any
- * resulting services.
- */
-bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
+bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
{
if (name.empty() || !ValidAsCString(name)) {
return false;
}
- int port = portDefault;
+ uint16_t port{portDefault};
std::string hostname;
SplitHostPort(name, port, hostname);
std::vector<CNetAddr> vIP;
- bool fRet = LookupIntern(hostname, vIP, nMaxSolutions, fAllowLookup);
+ bool fRet = LookupIntern(hostname, vIP, nMaxSolutions, fAllowLookup, dns_lookup_function);
if (!fRet)
return false;
vAddr.resize(vIP.size());
@@ -229,36 +213,20 @@ bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefau
return true;
}
-/**
- * Resolve a service string to its first corresponding service.
- *
- * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int)
- * for additional parameter descriptions.
- */
-bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup)
+bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(name)) {
return false;
}
std::vector<CService> vService;
- bool fRet = Lookup(name, vService, portDefault, fAllowLookup, 1);
+ bool fRet = Lookup(name, vService, portDefault, fAllowLookup, 1, dns_lookup_function);
if (!fRet)
return false;
addr = vService[0];
return true;
}
-/**
- * Resolve a service string with a numeric IP to its first corresponding
- * service.
- *
- * @returns The resulting CService if the resolution was successful, [::]:0
- * otherwise.
- *
- * @see Lookup(const char *, CService&, int, bool) for additional parameter
- * descriptions.
- */
-CService LookupNumeric(const std::string& name, int portDefault)
+CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(name)) {
return {};
@@ -266,19 +234,11 @@ CService LookupNumeric(const std::string& name, int portDefault)
CService addr;
// "1.2:345" will fail to resolve the ip, but will still set the port.
// If the ip fails to resolve, re-init the result.
- if(!Lookup(name, addr, portDefault, false))
+ if(!Lookup(name, addr, portDefault, false, dns_lookup_function))
addr = CService();
return addr;
}
-struct timeval MillisToTimeval(int64_t nTimeout)
-{
- struct timeval timeout;
- timeout.tv_sec = nTimeout / 1000;
- timeout.tv_usec = (nTimeout % 1000) * 1000;
- return timeout;
-}
-
/** SOCKS version */
enum SOCKSVersion: uint8_t {
SOCKS4 = 0x04,
@@ -336,8 +296,7 @@ enum class IntrRecvError {
* @param data The buffer where the read bytes should be stored.
* @param len The number of bytes to read into the specified buffer.
* @param timeout The total timeout in milliseconds for this read.
- * @param hSocket The socket (has to be in non-blocking mode) from which to read
- * bytes.
+ * @param sock The socket (has to be in non-blocking mode) from which to read bytes.
*
* @returns An IntrRecvError indicating the resulting status of this read.
* IntrRecvError::OK only if all of the specified number of bytes were
@@ -347,15 +306,12 @@ enum class IntrRecvError {
* Sockets can be made non-blocking with SetSocketNonBlocking(const
* SOCKET&, bool).
*/
-static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
+static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
- // Maximum time to wait for I/O readiness. It will take up until this time
- // (in millis) to break off in case of an interruption.
- const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
- ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
+ ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
@@ -364,25 +320,11 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
} else { // Other error or blocking
int nErr = WSAGetLastError();
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
- if (!IsSelectableSocket(hSocket)) {
- return IntrRecvError::NetworkError;
- }
- // Only wait at most maxWait milliseconds at a time, unless
+ // Only wait at most MAX_WAIT_FOR_IO at a time, unless
// we're approaching the end of the specified total timeout
- int timeout_ms = std::min(endTime - curTime, maxWait);
-#ifdef USE_POLL
- struct pollfd pollfd = {};
- pollfd.fd = hSocket;
- pollfd.events = POLLIN;
- int nRet = poll(&pollfd, 1, timeout_ms);
-#else
- struct timeval tval = MillisToTimeval(timeout_ms);
- fd_set fdset;
- FD_ZERO(&fdset);
- FD_SET(hSocket, &fdset);
- int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
-#endif
- if (nRet == SOCKET_ERROR) {
+ const auto remaining = std::chrono::milliseconds{endTime - curTime};
+ const auto timeout = std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ if (!sock.Wait(timeout, Sock::RECV)) {
return IntrRecvError::NetworkError;
}
} else {
@@ -396,13 +338,6 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}
-/** Credentials for proxy authentication */
-struct ProxyCredentials
-{
- std::string username;
- std::string password;
-};
-
/** Convert SOCKS5 reply to an error message */
static std::string Socks5ErrorString(uint8_t err)
{
@@ -428,25 +363,7 @@ static std::string Socks5ErrorString(uint8_t err)
}
}
-/**
- * Connect to a specified destination service through an already connected
- * SOCKS5 proxy.
- *
- * @param strDest The destination fully-qualified domain name.
- * @param port The destination port.
- * @param auth The credentials with which to authenticate with the specified
- * SOCKS5 proxy.
- * @param hSocket The SOCKS5 proxy socket.
- *
- * @returns Whether or not the operation succeeded.
- *
- * @note The specified SOCKS5 proxy socket must already be connected to the
- * SOCKS5 proxy.
- *
- * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
- * Version 5</a>
- */
-static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
+bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& sock)
{
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
@@ -464,12 +381,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5Init.push_back(0x01); // 1 method identifier follows...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
- ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
+ ssize_t ret = sock.Send(vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5Init.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet1[2];
- if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet1, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
@@ -486,13 +403,13 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
- ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vAuth.size()) {
return error("Error sending authentication to proxy");
}
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
uint8_t pchRetA[2];
- if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRetA, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
return error("Error reading proxy authentication response");
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
@@ -512,12 +429,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
vSocks5.push_back((port >> 0) & 0xFF);
- ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet2[4];
- if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet2, 4, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
if (recvr == IntrRecvError::Timeout) {
/* If a timeout happens here, this effectively means we timed out while connecting
* to the remote node. This is very common for Tor, so do not print an
@@ -541,16 +458,16 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
uint8_t pchRet3[256];
switch (pchRet2[3])
{
- case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
- case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
+ case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, g_socks5_recv_timeout, sock); break;
+ case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, g_socks5_recv_timeout, sock); break;
case SOCKS5Atyp::DOMAINNAME:
{
- recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, 1, g_socks5_recv_timeout, sock);
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
- recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, nRecv, g_socks5_recv_timeout, sock);
break;
}
default: return error("Error: malformed proxy response");
@@ -558,41 +475,35 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
- if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet3, 2, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) {
return error("Error reading from proxy");
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
}
-/**
- * Try to create a socket file descriptor with specific properties in the
- * communications domain (address family) of the specified service.
- *
- * For details on the desired properties, see the inline comments in the source
- * code.
- */
-SOCKET CreateSocket(const CService &addrConnect)
+std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
{
// Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
- return INVALID_SOCKET;
+ if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
+ LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToString());
+ return nullptr;
}
// Create a TCP socket in the address family of the specified service.
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
- if (hSocket == INVALID_SOCKET)
- return INVALID_SOCKET;
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
if (!IsSelectableSocket(hSocket)) {
CloseSocket(hSocket);
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
- return INVALID_SOCKET;
+ return nullptr;
}
#ifdef SO_NOSIGPIPE
@@ -608,11 +519,14 @@ SOCKET CreateSocket(const CService &addrConnect)
// Set the non-blocking option on the socket.
if (!SetSocketNonBlocking(hSocket, true)) {
CloseSocket(hSocket);
- LogPrintf("CreateSocket: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
+ return nullptr;
}
- return hSocket;
+ return std::make_unique<Sock>(hSocket);
}
+std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
+
template<typename... Args>
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
std::string error_message = tfm::format(fmt, args...);
@@ -623,18 +537,6 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg
}
}
-/**
- * Try to connect to the specified service on the specified socket.
- *
- * @param addrConnect The service to which to connect.
- * @param hSocket The socket on which to connect.
- * @param nTimeout Wait this many milliseconds for the connection to be
- * established.
- * @param manual_connection Whether or not the connection was manually requested
- * (e.g. through the addnode RPC)
- *
- * @returns Whether or not a connection was successfully made.
- */
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection)
{
// Create a sockaddr from the specified service.
@@ -733,22 +635,6 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
return true;
}
-/**
- * Set the name proxy to use for all connections to nodes specified by a
- * hostname. After setting this proxy, connecting to a node specified by a
- * hostname won't result in a local lookup of said hostname, rather, connect to
- * the node by asking the name proxy for a proxy connection to the hostname,
- * effectively delegating the hostname lookup to the specified proxy.
- *
- * This delegation increases privacy for those who set the name proxy as they no
- * longer leak their external hostname queries to their DNS servers.
- *
- * @returns Whether or not the operation succeeded.
- *
- * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5
- * server in common use (most notably Tor) actually implements UDP
- * support, and a DNS resolver is beyond the scope of this project.
- */
bool SetNameProxy(const proxyType &addrProxy) {
if (!addrProxy.IsValid())
return false;
@@ -779,25 +665,10 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}
-/**
- * Connect to a specified destination service through a SOCKS5 proxy by first
- * connecting to the SOCKS5 proxy.
- *
- * @param proxy The SOCKS5 proxy.
- * @param strDest The destination service to which to connect.
- * @param port The destination port.
- * @param hSocket The socket on which to connect to the SOCKS5 proxy.
- * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
- * proxy to be established.
- * @param[out] outProxyConnectionFailed Whether or not the connection to the
- * SOCKS5 proxy failed.
- *
- * @returns Whether or not the operation succeeded.
- */
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool& outProxyConnectionFailed)
+bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{
// first connect to proxy server
- if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) {
+ if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) {
outProxyConnectionFailed = true;
return false;
}
@@ -806,29 +677,18 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
- if (!Socks5(strDest, (uint16_t)port, &random_auth, hSocket)) {
+ if (!Socks5(strDest, port, &random_auth, sock)) {
return false;
}
} else {
- if (!Socks5(strDest, (uint16_t)port, 0, hSocket)) {
+ if (!Socks5(strDest, port, 0, sock)) {
return false;
}
}
return true;
}
-/**
- * Parse and resolve a specified subnet string into the appropriate internal
- * representation.
- *
- * @param strSubnet A string representation of a subnet of the form `network
- * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g.
- * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`).
- * @param ret The resulting internal representation of a subnet.
- *
- * @returns Whether the operation succeeded or not.
- */
-bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
+bool LookupSubNet(const std::string& strSubnet, CSubNet& ret, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(strSubnet)) {
return false;
@@ -839,7 +699,7 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
std::string strAddress = strSubnet.substr(0, slash);
// TODO: Use LookupHost(const std::string&, CNetAddr&, bool) instead to just get
// one CNetAddr.
- if (LookupHost(strAddress, vIP, 1, false))
+ if (LookupHost(strAddress, vIP, 1, false, dns_lookup_function))
{
CNetAddr network = vIP[0];
if (slash != strSubnet.npos)
@@ -854,7 +714,7 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
else // If not a valid number, try full netmask syntax
{
// Never allow lookup for netmask
- if (LookupHost(strNetmask, vIP, 1, false)) {
+ if (LookupHost(strNetmask, vIP, 1, false, dns_lookup_function)) {
ret = CSubNet(network, vIP[0]);
return ret.IsValid();
}
@@ -869,57 +729,6 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
return false;
}
-#ifdef WIN32
-std::string NetworkErrorString(int err)
-{
- wchar_t buf[256];
- buf[0] = 0;
- if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
- nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- buf, ARRAYSIZE(buf), nullptr))
- {
- return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
- }
- else
- {
- return strprintf("Unknown error (%d)", err);
- }
-}
-#else
-std::string NetworkErrorString(int err)
-{
- char buf[256];
- buf[0] = 0;
- /* Too bad there are two incompatible implementations of the
- * thread-safe strerror. */
- const char *s;
-#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
- s = strerror_r(err, buf, sizeof(buf));
-#else /* POSIX variant always returns message in buffer */
- s = buf;
- if (strerror_r(err, buf, sizeof(buf)))
- buf[0] = 0;
-#endif
- return strprintf("%s (%d)", s, err);
-}
-#endif
-
-bool CloseSocket(SOCKET& hSocket)
-{
- if (hSocket == INVALID_SOCKET)
- return false;
-#ifdef WIN32
- int ret = closesocket(hSocket);
-#else
- int ret = close(hSocket);
-#endif
- if (ret) {
- LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
- }
- hSocket = INVALID_SOCKET;
- return ret != SOCKET_ERROR;
-}
-
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
{
if (fNonBlocking) {
diff --git a/src/netbase.h b/src/netbase.h
index ac4cd97673..08172b9984 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -12,9 +12,13 @@
#include <compat.h>
#include <netaddress.h>
#include <serialize.h>
+#include <util/sock.h>
+#include <functional>
+#include <memory>
#include <stdint.h>
#include <string>
+#include <type_traits>
#include <vector>
extern int nConnectTimeout;
@@ -25,6 +29,22 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000;
//! -dns default
static const int DEFAULT_NAME_LOOKUP = true;
+enum class ConnectionDirection {
+ None = 0,
+ In = (1U << 0),
+ Out = (1U << 1),
+ Both = (In | Out),
+};
+static inline ConnectionDirection& operator|=(ConnectionDirection& a, ConnectionDirection b) {
+ using underlying = typename std::underlying_type<ConnectionDirection>::type;
+ a = ConnectionDirection(underlying(a) | underlying(b));
+ return a;
+}
+static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
+ using underlying = typename std::underlying_type<ConnectionDirection>::type;
+ return (underlying(a) & underlying(b));
+}
+
class proxyType
{
public:
@@ -37,35 +57,194 @@ public:
bool randomize_credentials;
};
+/** Credentials for proxy authentication */
+struct ProxyCredentials
+{
+ std::string username;
+ std::string password;
+};
+
+/**
+ * Wrapper for getaddrinfo(3). Do not use directly: call Lookup/LookupHost/LookupNumeric/LookupSubNet.
+ */
+std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup);
+
enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
+/** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */
+std::vector<std::string> GetNetworkNames(bool append_unroutable = false);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
+/**
+ * Set the name proxy to use for all connections to nodes specified by a
+ * hostname. After setting this proxy, connecting to a node specified by a
+ * hostname won't result in a local lookup of said hostname, rather, connect to
+ * the node by asking the name proxy for a proxy connection to the hostname,
+ * effectively delegating the hostname lookup to the specified proxy.
+ *
+ * This delegation increases privacy for those who set the name proxy as they no
+ * longer leak their external hostname queries to their DNS servers.
+ *
+ * @returns Whether or not the operation succeeded.
+ *
+ * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5
+ * server in common use (most notably Tor) actually implements UDP
+ * support, and a DNS resolver is beyond the scope of this project.
+ */
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
bool GetNameProxy(proxyType &nameProxyOut);
-bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
-bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup);
-bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup);
-bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
-CService LookupNumeric(const std::string& name, int portDefault = 0);
-bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet);
-SOCKET CreateSocket(const CService &addrConnect);
-bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection);
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool& outProxyConnectionFailed);
-/** Return readable error string for a network error code */
-std::string NetworkErrorString(int err);
-/** Close socket and set hSocket to INVALID_SOCKET */
-bool CloseSocket(SOCKET& hSocket);
+
+using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>;
+extern DNSLookupFn g_dns_lookup;
+
+/**
+ * Resolve a host string to its corresponding network addresses.
+ *
+ * @param name The string representing a host. Could be a name or a numerical
+ * IP address (IPv6 addresses in their bracketed form are
+ * allowed).
+ * @param[out] vIP The resulting network addresses to which the specified host
+ * string resolved.
+ *
+ * @returns Whether or not the specified host string successfully resolved to
+ * any resulting network addresses.
+ *
+ * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
+ * for additional parameter descriptions.
+ */
+bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Resolve a host string to its first corresponding network address.
+ *
+ * @see LookupHost(const std::string&, std::vector<CNetAddr>&, uint16_t, bool, DNSLookupFn)
+ * for additional parameter descriptions.
+ */
+bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Resolve a service string to its corresponding service.
+ *
+ * @param name The string representing a service. Could be a name or a
+ * numerical IP address (IPv6 addresses should be in their
+ * disambiguated bracketed form), optionally followed by a uint16_t port
+ * number. (e.g. example.com:8333 or
+ * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
+ * @param[out] vAddr The resulting services to which the specified service string
+ * resolved.
+ * @param portDefault The default port for resulting services if not specified
+ * by the service string.
+ * @param fAllowLookup Whether or not hostname lookups are permitted. If yes,
+ * external queries may be performed.
+ * @param nMaxSolutions The maximum number of results we want, specifying 0
+ * means "as many solutions as we get."
+ *
+ * @returns Whether or not the service string successfully resolved to any
+ * resulting services.
+ */
+bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Resolve a service string to its first corresponding service.
+ *
+ * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
+ * for additional parameter descriptions.
+ */
+bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Resolve a service string with a numeric IP to its first corresponding
+ * service.
+ *
+ * @returns The resulting CService if the resolution was successful, [::]:0 otherwise.
+ *
+ * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
+ * for additional parameter descriptions.
+ */
+CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Parse and resolve a specified subnet string into the appropriate internal
+ * representation.
+ *
+ * @param strSubnet A string representation of a subnet of the form `network
+ * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g.
+ * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`).
+ * @param ret The resulting internal representation of a subnet.
+ *
+ * @returns Whether the operation succeeded or not.
+ */
+bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet, DNSLookupFn dns_lookup_function = g_dns_lookup);
+
+/**
+ * Create a TCP socket in the given address family.
+ * @param[in] address_family The socket is created in the same address family as this address.
+ * @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure
+ */
+std::unique_ptr<Sock> CreateSockTCP(const CService& address_family);
+
+/**
+ * Socket factory. Defaults to `CreateSockTCP()`, but can be overridden by unit tests.
+ */
+extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
+
+/**
+ * Try to connect to the specified service on the specified socket.
+ *
+ * @param addrConnect The service to which to connect.
+ * @param hSocket The socket on which to connect.
+ * @param nTimeout Wait this many milliseconds for the connection to be
+ * established.
+ * @param manual_connection Whether or not the connection was manually requested
+ * (e.g. through the addnode RPC)
+ *
+ * @returns Whether or not a connection was successfully made.
+ */
+bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection);
+
+/**
+ * Connect to a specified destination service through a SOCKS5 proxy by first
+ * connecting to the SOCKS5 proxy.
+ *
+ * @param proxy The SOCKS5 proxy.
+ * @param strDest The destination service to which to connect.
+ * @param port The destination port.
+ * @param sock The socket on which to connect to the SOCKS5 proxy.
+ * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
+ * proxy to be established.
+ * @param[out] outProxyConnectionFailed Whether or not the connection to the
+ * SOCKS5 proxy failed.
+ *
+ * @returns Whether or not the operation succeeded.
+ */
+bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
+
/** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
/** Set the TCP_NODELAY flag on a socket */
bool SetSocketNoDelay(const SOCKET& hSocket);
+void InterruptSocks5(bool interrupt);
+
/**
- * Convert milliseconds to a struct timeval for e.g. select.
+ * Connect to a specified destination service through an already connected
+ * SOCKS5 proxy.
+ *
+ * @param strDest The destination fully-qualified domain name.
+ * @param port The destination port.
+ * @param auth The credentials with which to authenticate with the specified
+ * SOCKS5 proxy.
+ * @param sock The SOCKS5 proxy socket.
+ *
+ * @returns Whether or not the operation succeeded.
+ *
+ * @note The specified SOCKS5 proxy socket must already be connected to the
+ * SOCKS5 proxy.
+ *
+ * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
+ * Version 5</a>
*/
-struct timeval MillisToTimeval(int64_t nTimeout);
-void InterruptSocks5(bool interrupt);
+bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& socket);
#endif // BITCOIN_NETBASE_H
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index f4f86cdbe9..263dcff657 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -12,7 +12,8 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
{
assert(node.mempool);
LOCK2(cs_main, node.mempool->cs);
- CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
+ assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
+ CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.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
index 56113cb731..88bdba5953 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -6,6 +6,7 @@
#include <node/coinstats.h>
#include <coins.h>
+#include <crypto/muhash.h>
#include <hash.h>
#include <serialize.h>
#include <uint256.h>
@@ -24,37 +25,65 @@ static uint64_t GetBogoSize(const CScript& scriptPubKey)
scriptPubKey.size() /* scriptPubKey */;
}
-static void ApplyStats(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+static void ApplyHash(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it)
{
- 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_MODE(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
- stats.nTransactionOutputs++;
- stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
+ if (it == outputs.begin()) {
+ ss << hash;
+ ss << VARINT(it->second.nHeight * 2 + it->second.fCoinBase ? 1u : 0u);
+ }
+
+ ss << VARINT(it->first + 1);
+ ss << it->second.out.scriptPubKey;
+ ss << VARINT_MODE(it->second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
+
+ if (it == std::prev(outputs.end())) {
+ ss << VARINT(0u);
}
- ss << VARINT(0u);
}
-static void ApplyStats(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+static void ApplyHash(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it) {}
+
+static void ApplyHash(CCoinsStats& stats, MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it)
+{
+ COutPoint outpoint = COutPoint(hash, it->first);
+ Coin coin = it->second;
+
+ CDataStream ss(SER_DISK, PROTOCOL_VERSION);
+ ss << outpoint;
+ ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
+ ss << coin.out;
+ muhash.Insert(MakeUCharSpan(ss));
+}
+
+//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
+//! validation commitments are reliant on the hash constructed by this
+//! function.
+//!
+//! If the construction of this hash is changed, it will invalidate
+//! existing UTXO snapshots. This will not result in any kind of consensus
+//! failure, but it will force clients that were expecting to make use of
+//! assumeutxo to do traditional IBD instead.
+//!
+//! It is also possible, though very unlikely, that a change in this
+//! construction could cause a previously invalid (and potentially malicious)
+//! UTXO snapshot to be considered valid.
+template <typename T>
+static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
stats.nTransactions++;
- for (const auto& output : outputs) {
+ for (auto it = outputs.begin(); it != outputs.end(); ++it) {
+ ApplyHash(stats, hash_obj, hash, outputs, it);
+
stats.nTransactionOutputs++;
- stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
+ stats.nTotalAmount += it->second.out.nValue;
+ stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
}
}
//! Calculate statistics about the unspent transaction output set
template <typename T>
-static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
+static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function<void()>& interruption_point)
{
stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
@@ -63,7 +92,8 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const
stats.hashBlock = pcursor->GetBestBlock();
{
LOCK(cs_main);
- stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
+ assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
+ stats.nHeight = blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
}
PrepareHash(hash_obj, stats);
@@ -97,15 +127,19 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const
return true;
}
-bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
{
switch (hash_type) {
case(CoinStatsHashType::HASH_SERIALIZED): {
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- return GetUTXOStats(view, stats, ss, interruption_point);
+ return GetUTXOStats(view, blockman, stats, ss, interruption_point);
+ }
+ case(CoinStatsHashType::MUHASH): {
+ MuHash3072 muhash;
+ return GetUTXOStats(view, blockman, stats, muhash, interruption_point);
}
case(CoinStatsHashType::NONE): {
- return GetUTXOStats(view, stats, nullptr, interruption_point);
+ return GetUTXOStats(view, blockman, stats, nullptr, interruption_point);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
@@ -116,10 +150,18 @@ static void PrepareHash(CHashWriter& ss, const CCoinsStats& stats)
{
ss << stats.hashBlock;
}
+// MuHash does not need the prepare step
+static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
{
stats.hashSerialized = ss.GetHash();
}
+static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
+{
+ uint256 out;
+ muhash.Finalize(out);
+ stats.hashSerialized = out;
+}
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 7c56bfc2ad..83f228aa7e 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <uint256.h>
+#include <validation.h>
#include <cstdint>
#include <functional>
@@ -16,6 +17,7 @@ class CCoinsView;
enum class CoinStatsHashType {
HASH_SERIALIZED,
+ MUHASH,
NONE,
};
@@ -35,6 +37,6 @@ struct CCoinsStats
};
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index b7efd68cfd..50c8c29175 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -50,6 +50,7 @@
#endif
#include <memory>
+#include <optional>
#include <utility>
using interfaces::BlockTip;
@@ -96,7 +97,7 @@ public:
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
- size_t getNodeCount(CConnman::NumConnections flags) override
+ size_t getNodeCount(ConnectionDirection flags) override
{
return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0;
}
@@ -182,18 +183,21 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- return ::ChainActive().Height();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ return m_context->chainman->ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip());
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return m_context->chainman->ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- if (::ChainActive().Tip()) {
- return ::ChainActive().Tip()->GetBlockTime();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ if (m_context->chainman->ActiveChain().Tip()) {
+ return m_context->chainman->ActiveChain().Tip()->GetBlockTime();
}
return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network
}
@@ -202,11 +206,15 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- tip = ::ChainActive().Tip();
+ assert(std::addressof(::ChainActive()) == std::addressof(m_context->chainman->ActiveChain()));
+ tip = m_context->chainman->ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
- bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate()));
+ return m_context->chainman->ActiveChainstate().IsInitialBlockDownload();
+ }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override
@@ -231,7 +239,8 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_context->chainman->ActiveChainstate()));
+ return m_context->chainman->ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
{
@@ -407,7 +416,7 @@ class ChainImpl : public Chain
{
public:
explicit ChainImpl(NodeContext& node) : m_node(node) {}
- Optional<int> getHeight() override
+ std::optional<int> getHeight() override
{
LOCK(::cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
@@ -415,7 +424,7 @@ public:
if (height >= 0) {
return height;
}
- return nullopt;
+ return std::nullopt;
}
uint256 getBlockHash(int height) override
{
@@ -441,22 +450,25 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- return CheckFinalTx(tx);
+ assert(std::addressof(::ChainActive()) == std::addressof(m_node.chainman->ActiveChain()));
+ return CheckFinalTx(m_node.chainman->ActiveChain().Tip(), tx);
}
- Optional<int> findLocatorFork(const CBlockLocator& locator) override
+ std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(active, locator)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
- return nullopt;
+ return std::nullopt;
}
bool findBlock(const uint256& hash, const FoundBlock& block) override
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- return FillBlock(g_chainman.m_blockman.LookupBlockIndex(hash), block, lock, active);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
{
@@ -468,7 +480,8 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- if (const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
}
@@ -479,8 +492,10 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash);
- const CBlockIndex* ancestor = g_chainman.m_blockman.LookupBlockIndex(ancestor_hash);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
}
@@ -488,8 +503,10 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- const CBlockIndex* block1 = g_chainman.m_blockman.LookupBlockIndex(block_hash1);
- const CBlockIndex* block2 = g_chainman.m_blockman.LookupBlockIndex(block_hash2);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
// output uninitialized.
@@ -499,9 +516,10 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash));
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ return GuessVerificationProgress(Params().TxData(), m_node.chainman->m_blockman.LookupBlockIndex(block_hash));
}
- bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override
+ bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
{
// hasBlocks returns true if all ancestors of block_hash in specified
// range have block data (are not pruned), false if any ancestors in
@@ -511,7 +529,8 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- if (CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) {
+ assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
+ if (CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
// Check pprev to not segfault if min_height is too low
@@ -526,6 +545,12 @@ public:
LOCK(m_node.mempool->cs);
return IsRBFOptIn(tx, *m_node.mempool);
}
+ bool isInMempool(const uint256& txid) override
+ {
+ if (!m_node.mempool) return false;
+ LOCK(m_node.mempool->cs);
+ return m_node.mempool->exists(txid);
+ }
bool hasDescendantsInMempool(const uint256& txid) override
{
if (!m_node.mempool) return false;
@@ -595,7 +620,10 @@ public:
return ::fHavePruned;
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
- bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
+ bool isInitialBlockDownload() override {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_node.chainman->ActiveChainstate()));
+ return m_node.chainman->ActiveChainstate().IsInitialBlockDownload();
+ }
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
@@ -607,7 +635,7 @@ public:
}
std::unique_ptr<Handler> handleNotifications(std::shared_ptr<Notifications> notifications) override
{
- return MakeUnique<NotificationsHandlerImpl>(std::move(notifications));
+ return std::make_unique<NotificationsHandlerImpl>(std::move(notifications));
}
void waitForNotificationsIfTipChanged(const uint256& old_tip) override
{
@@ -620,7 +648,7 @@ public:
}
std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override
{
- return MakeUnique<RpcHandlerImpl>(command);
+ return std::make_unique<RpcHandlerImpl>(command);
}
bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); }
void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) override
@@ -663,6 +691,6 @@ public:
} // namespace node
namespace interfaces {
-std::unique_ptr<Node> MakeNode(NodeContext* context) { return MakeUnique<node::NodeImpl>(context); }
-std::unique_ptr<Chain> MakeChain(NodeContext& context) { return MakeUnique<node::ChainImpl>(context); }
+std::unique_ptr<Node> MakeNode(NodeContext* context) { return std::make_unique<node::NodeImpl>(context); }
+std::unique_ptr<Chain> MakeChain(NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
} // namespace interfaces
diff --git a/src/node/psbt.h b/src/node/psbt.h
index 7384dc415c..def4385c09 100644
--- a/src/node/psbt.h
+++ b/src/node/psbt.h
@@ -7,6 +7,8 @@
#include <psbt.h>
+#include <optional>
+
/**
* Holds an analysis of one input from a PSBT
*/
@@ -25,18 +27,18 @@ struct PSBTInputAnalysis {
* Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
*/
struct PSBTAnalysis {
- Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
- Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
- Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
- std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
- PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
- std::string error; //!< Error message
+ std::optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
+ std::optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
+ std::optional<CAmount> fee; //!< Amount of fee being paid by the transaction
+ std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
+ PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
+ std::string error; //!< Error message
void SetInvalid(std::string err_msg)
{
- estimated_vsize = nullopt;
- estimated_feerate = nullopt;
- fee = nullopt;
+ estimated_vsize = std::nullopt;
+ estimated_feerate = std::nullopt;
+ fee = std::nullopt;
inputs.clear();
next = PSBTRole::CREATOR;
error = err_msg;
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index d3bb9687a8..f47e85aceb 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -29,9 +29,9 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
TransactionError BroadcastTransaction(NodeContext& node, 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.
- // node.connman is assigned both before chain clients and before RPC server is accepting calls,
- // and reset after chain clients and RPC sever are stopped. node.connman should never be null here.
- assert(node.connman);
+ // node.peerman is assigned both before chain clients and before RPC server is accepting calls,
+ // and reset after chain clients and RPC sever are stopped. node.peerman should never be null here.
+ assert(node.peerman);
assert(node.mempool);
std::promise<void> promise;
uint256 hashTx = tx->GetHash();
@@ -39,9 +39,10 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
LOCK(cs_main);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
- CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
+ CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
// IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
@@ -50,22 +51,22 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
if (!node.mempool->exists(hashTx)) {
// Transaction is not already in the mempool.
- TxValidationState state;
if (max_tx_fee > 0) {
// First, call ATMP with test_accept and check the fee. If ATMP
// fails here, return error immediately.
- CAmount fee{0};
- if (!AcceptToMemoryPool(*node.mempool, state, tx,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
- return HandleATMPError(state, err_string);
- } else if (fee > max_tx_fee) {
+ const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
+ true /* test_accept */);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ return HandleATMPError(result.m_state, err_string);
+ } else if (result.m_base_fees.value() > max_tx_fee) {
return TransactionError::MAX_FEE_EXCEEDED;
}
}
// Try to submit the transaction to the mempool.
- if (!AcceptToMemoryPool(*node.mempool, state, tx,
- nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
- return HandleATMPError(state, err_string);
+ const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
+ false /* test_accept */);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ return HandleATMPError(result.m_state, err_string);
}
// Transaction was accepted to the mempool.
@@ -100,7 +101,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
node.mempool->AddUnbroadcastTx(hashTx);
LOCK(cs_main);
- RelayTransaction(hashTx, tx->GetWitnessHash(), *node.connman);
+ node.peerman->RelayTransaction(hashTx, tx->GetWitnessHash());
}
return TransactionError::OK;
diff --git a/src/optional.h b/src/optional.h
deleted file mode 100644
index 583c56eabd..0000000000
--- a/src/optional.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2017-2020 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_OPTIONAL_H
-#define BITCOIN_OPTIONAL_H
-
-#include <optional>
-#include <utility>
-
-//! Substitute for C++17 std::optional
-//! DEPRECATED use std::optional in new code.
-template <typename T>
-using Optional = std::optional<T>;
-
-//! Substitute for C++17 std::nullopt
-//! DEPRECATED use std::nullopt in new code.
-static auto& nullopt = std::nullopt;
-
-#endif // BITCOIN_OPTIONAL_H
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 56e738eaa8..0b893b9272 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -5,7 +5,6 @@
#include <protocol.h>
-#include <util/strencodings.h>
#include <util/system.h>
static std::atomic<bool> g_initial_block_download_completed(false);
@@ -86,7 +85,7 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::CFCHECKPT,
NetMsgType::WTXIDRELAY,
};
-const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
+const static std::vector<std::string> allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes));
CMessageHeader::CMessageHeader()
{
diff --git a/src/psbt.h b/src/psbt.h
index b566726ee3..96ae39fdb8 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -7,13 +7,14 @@
#include <attributes.h>
#include <node/transaction.h>
-#include <optional.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/sign.h>
#include <script/signingprovider.h>
+#include <optional>
+
// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
@@ -389,7 +390,7 @@ struct PSBTOutput
/** A version of CTransaction with the PSBT format*/
struct PartiallySignedTransaction
{
- Optional<CMutableTransaction> tx;
+ std::optional<CMutableTransaction> tx;
std::vector<PSBTInput> inputs;
std::vector<PSBTOutput> outputs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index ab6168a541..9927e925ac 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -295,7 +295,7 @@ void AddressBookPage::on_exportButton_clicked()
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Address List"), QString(),
- tr("Comma separated file (*.csv)"), nullptr);
+ tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
return;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index bb444f22b3..ee2462ed74 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -198,42 +198,38 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
- if(role == Qt::DisplayRole || role == Qt::EditRole)
- {
- switch(index.column())
- {
+ const auto column = static_cast<ColumnIndex>(index.column());
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ switch (column) {
case Label:
- if(rec->label.isEmpty() && role == Qt::DisplayRole)
- {
+ if (rec->label.isEmpty() && role == Qt::DisplayRole) {
return tr("(no label)");
- }
- else
- {
+ } else {
return rec->label;
}
case Address:
return rec->address;
- }
- }
- else if (role == Qt::FontRole)
- {
- QFont font;
- if(index.column() == Address)
- {
- font = GUIUtil::fixedPitchFont();
- }
- return font;
- }
- else if (role == TypeRole)
- {
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ } else if (role == Qt::FontRole) {
+ switch (column) {
+ case Label:
+ return QFont();
+ case Address:
+ return GUIUtil::fixedPitchFont();
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ } else if (role == TypeRole) {
switch(rec->type)
{
case AddressTableEntry::Sending:
return Send;
case AddressTableEntry::Receiving:
return Receive;
- default: break;
- }
+ case AddressTableEntry::Hidden:
+ return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
return QVariant();
}
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index a01a7bc386..6cb4a4c546 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -23,15 +23,13 @@ bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
- switch(column)
- {
+ switch (static_cast<BanTableModel::ColumnIndex>(column)) {
case BanTableModel::Address:
return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
case BanTableModel::Bantime:
return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
- }
-
- return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
// private implementation
@@ -119,16 +117,17 @@ QVariant BanTableModel::data(const QModelIndex &index, int role) const
CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());
+ const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole) {
- switch(index.column())
- {
+ switch (column) {
case Address:
return QString::fromStdString(rec->subnet.ToString());
case Bantime:
QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
date = date.addSecs(rec->banEntry.nBanUntil);
return QLocale::system().toString(date, QLocale::LongFormat);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
return QVariant();
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index d6d5ba6968..791a29f7a0 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -44,6 +44,7 @@
#include <QApplication>
#include <QDebug>
+#include <QFontDatabase>
#include <QLibraryInfo>
#include <QLocale>
#include <QMessageBox>
@@ -51,7 +52,6 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
-#include <QtGlobal>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -61,6 +61,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
+Q_IMPORT_PLUGIN(QMacStylePlugin);
#endif
#endif
@@ -463,18 +464,10 @@ int GuiMain(int argc, char* argv[])
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#if QT_VERSION >= 0x050600
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-#endif
-
-#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS)
- const auto os_name = QSysInfo::prettyProductName();
- if (os_name.startsWith("macOS 11") || os_name.startsWith("macOS 10.16")) {
- QApplication::setStyle("fusion");
- }
-#endif
BitcoinApplication app;
+ QFontDatabase::addApplicationFont(":/fonts/monospace");
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index 7115459808..fed373e551 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -83,4 +83,7 @@
<file alias="spinner-034">res/animation/spinner-034.png</file>
<file alias="spinner-035">res/animation/spinner-035.png</file>
</qresource>
+ <qresource prefix="/fonts">
+ <file alias="monospace">res/fonts/RobotoMono-Bold.ttf</file>
+ </qresource>
</RCC>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ccc0a6828c..6677c9e3b5 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -88,6 +88,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
}
+ setContextMenuPolicy(Qt::PreventContextMenu);
+
#ifdef ENABLE_WALLET
enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET
@@ -544,7 +546,6 @@ void BitcoinGUI::createToolBars()
{
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
appToolBar = toolbar;
- toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
toolbar->setMovable(false);
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addAction(overviewAction);
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 56813b2d19..b244bc94f2 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -71,14 +71,14 @@ ClientModel::~ClientModel()
int ClientModel::getNumConnections(unsigned int flags) const
{
- CConnman::NumConnections connections = CConnman::CONNECTIONS_NONE;
+ ConnectionDirection connections = ConnectionDirection::None;
if(flags == CONNECTIONS_IN)
- connections = CConnman::CONNECTIONS_IN;
+ connections = ConnectionDirection::In;
else if (flags == CONNECTIONS_OUT)
- connections = CConnman::CONNECTIONS_OUT;
+ connections = ConnectionDirection::Out;
else if (flags == CONNECTIONS_ALL)
- connections = CConnman::CONNECTIONS_ALL;
+ connections = ConnectionDirection::Both;
return m_node.getNodeCount(connections);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 3831852185..e45cafe48a 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -988,7 +988,7 @@
</item>
</layout>
</widget>
- <widget class="QWidget" name="widget_2" native="true">
+ <widget class="QWidget" name="peersTabRightPanel" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -1079,10 +1079,10 @@
<item row="1" column="0">
<widget class="QLabel" name="peerConnectionTypeLabel">
<property name="toolTip">
- <string>The type of peer connection: %1</string>
+ <string>The direction and type of peer connection: %1</string>
</property>
<property name="text">
- <string>Connection Type</string>
+ <string>Direction/Type</string>
</property>
</widget>
</item>
@@ -1198,13 +1198,65 @@
</widget>
</item>
<item row="6" column="0">
+ <widget class="QLabel" name="peerRelayTxesLabel">
+ <property name="toolTip">
+ <string>Whether the peer requested us to relay transactions.</string>
+ </property>
+ <property name="text">
+ <string>Wants Tx Relay</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLabel" name="peerRelayTxes">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="peerHighBandwidthLabel">
+ <property name="toolTip">
+ <string>High bandwidth BIP152 compact block relay: %1</string>
+ </property>
+ <property name="text">
+ <string>High Bandwidth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLabel" name="peerHighBandwidth">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Starting Block</string>
</property>
</widget>
</item>
- <item row="6" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="peerHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1220,14 +1272,14 @@
</property>
</widget>
</item>
- <item row="7" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Synced Headers</string>
</property>
</widget>
</item>
- <item row="7" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="peerSyncHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1243,14 +1295,14 @@
</property>
</widget>
</item>
- <item row="8" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Synced Blocks</string>
</property>
</widget>
</item>
- <item row="8" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="peerCommonHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1266,14 +1318,14 @@
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Connection Time</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1289,14 +1341,66 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="12" column="0">
+ <widget class="QLabel" name="peerLastBlockLabel">
+ <property name="toolTip">
+ <string>Elapsed time since a novel block passing initial validity checks was received from this peer.</string>
+ </property>
+ <property name="text">
+ <string>Last Block</string>
+ </property>
+ </widget>
+ </item>
+ <item row="12" column="1">
+ <widget class="QLabel" name="peerLastBlock">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="13" column="0">
+ <widget class="QLabel" name="peerLastTxLabel">
+ <property name="toolTip">
+ <string>Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
+ </property>
+ <property name="text">
+ <string>Last Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item row="13" column="1">
+ <widget class="QLabel" name="peerLastTx">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="14" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1312,14 +1416,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1335,14 +1439,14 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1358,14 +1462,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1381,14 +1485,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="18" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="18" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1404,7 +1508,7 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="19" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1414,7 +1518,7 @@
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="19" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1430,14 +1534,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="20" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="20" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1453,14 +1557,14 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="21" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="21" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1476,7 +1580,7 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="22" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1486,7 +1590,7 @@
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="22" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1502,7 +1606,7 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="23" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 8181cc47e2..6d279540e9 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -706,6 +706,106 @@
</layout>
</item>
<item>
+ <widget class="QGroupBox" name="font_groupBox">
+ <property name="title">
+ <string>Monospaced font in the Overview tab:</string>
+ </property>
+ <layout class="QVBoxLayout" name="font_verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="embeddedFont_horizontalLayout">
+ <item>
+ <widget class="QRadioButton" name="embeddedFont_radioButton">
+ <property name="text">
+ <string>embedded &quot;%1&quot;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="embeddedFont_horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="embeddedFont_verticalLayout">
+ <item>
+ <widget class="QLabel" name="embeddedFont_label_1">
+ <property name="text">
+ <string>111.11111111 BTC</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="embeddedFont_label_9">
+ <property name="text">
+ <string>909.09090909 BTC</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="font_line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="systemFont_horizontalLayout">
+ <item>
+ <widget class="QRadioButton" name="systemFont_radioButton">
+ <property name="text">
+ <string>closest matching &quot;%1&quot;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="systemFont_horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="systemFont_verticalLayout">
+ <item>
+ <widget class="QLabel" name="systemFont_label_1">
+ <property name="text">
+ <string>111.11111111 BTC</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="systemFont_label_9">
+ <property name="text">
+ <string>909.09090909 BTC</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_Display">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui
index ee9d4a113c..f85de0811a 100644
--- a/src/qt/forms/overviewpage.ui
+++ b/src/qt/forms/overviewpage.ui
@@ -68,7 +68,7 @@
</property>
<property name="maximumSize">
<size>
- <width>30</width>
+ <width>45</width>
<height>16777215</height>
</size>
</property>
@@ -89,9 +89,6 @@
<height>24</height>
</size>
</property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
@@ -116,13 +113,6 @@
</property>
<item row="2" column="2">
<widget class="QLabel" name="labelWatchPending">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -142,13 +132,6 @@
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelUnconfirmed">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -168,13 +151,6 @@
</item>
<item row="3" column="2">
<widget class="QLabel" name="labelWatchImmature">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -227,13 +203,6 @@
</item>
<item row="3" column="1">
<widget class="QLabel" name="labelImmature">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -273,13 +242,6 @@
</item>
<item row="5" column="1">
<widget class="QLabel" name="labelTotal">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -299,13 +261,6 @@
</item>
<item row="5" column="2">
<widget class="QLabel" name="labelWatchTotal">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -342,13 +297,6 @@
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelBalance">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -368,13 +316,6 @@
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelWatchAvailable">
- <property name="font">
- <font>
- <family>Monospace</family>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -462,7 +403,7 @@
</property>
<property name="maximumSize">
<size>
- <width>30</width>
+ <width>45</width>
<height>16777215</height>
</size>
</property>
@@ -483,9 +424,6 @@
<height>24</height>
</size>
</property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 376acf0963..32890ed0fe 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -59,6 +59,8 @@
#include <QUrlQuery>
#include <QtGlobal>
+#include <chrono>
+
#if defined(Q_OS_MAC)
#include <QProcess>
@@ -78,8 +80,11 @@ QString dateTimeStr(qint64 nTime)
return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}
-QFont fixedPitchFont()
+QFont fixedPitchFont(bool use_embedded_font)
{
+ if (use_embedded_font) {
+ return {"Roboto Mono"};
+ }
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
}
@@ -470,120 +475,6 @@ bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event)
return QObject::eventFilter(watched, event);
}
-void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
-{
- connect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this, &TableViewLastColumnResizingFixer::on_sectionResized);
- connect(tableView->horizontalHeader(), &QHeaderView::geometriesChanged, this, &TableViewLastColumnResizingFixer::on_geometriesChanged);
-}
-
-// We need to disconnect these while handling the resize events, otherwise we can enter infinite loops.
-void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals()
-{
- disconnect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this, &TableViewLastColumnResizingFixer::on_sectionResized);
- disconnect(tableView->horizontalHeader(), &QHeaderView::geometriesChanged, this, &TableViewLastColumnResizingFixer::on_geometriesChanged);
-}
-
-// Setup the resize mode, handles compatibility for Qt5 and below as the method signatures changed.
-// Refactored here for readability.
-void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
-{
- tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
-}
-
-void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width)
-{
- tableView->setColumnWidth(nColumnIndex, width);
- tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
-}
-
-int TableViewLastColumnResizingFixer::getColumnsWidth()
-{
- int nColumnsWidthSum = 0;
- for (int i = 0; i < columnCount; i++)
- {
- nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
- }
- return nColumnsWidthSum;
-}
-
-int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column)
-{
- int nResult = lastColumnMinimumWidth;
- int nTableWidth = tableView->horizontalHeader()->width();
-
- if (nTableWidth > 0)
- {
- int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
- nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
- }
-
- return nResult;
-}
-
-// Make sure we don't make the columns wider than the table's viewport width.
-void TableViewLastColumnResizingFixer::adjustTableColumnsWidth()
-{
- disconnectViewHeadersSignals();
- resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
- connectViewHeadersSignals();
-
- int nTableWidth = tableView->horizontalHeader()->width();
- int nColsWidth = getColumnsWidth();
- if (nColsWidth > nTableWidth)
- {
- resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
- }
-}
-
-// Make column use all the space available, useful during window resizing.
-void TableViewLastColumnResizingFixer::stretchColumnWidth(int column)
-{
- disconnectViewHeadersSignals();
- resizeColumn(column, getAvailableWidthForColumn(column));
- connectViewHeadersSignals();
-}
-
-// When a section is resized this is a slot-proxy for ajustAmountColumnWidth().
-void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize)
-{
- adjustTableColumnsWidth();
- int remainingWidth = getAvailableWidthForColumn(logicalIndex);
- if (newSize > remainingWidth)
- {
- resizeColumn(logicalIndex, remainingWidth);
- }
-}
-
-// When the table's geometry is ready, we manually perform the stretch of the "Message" column,
-// as the "Stretch" resize mode does not allow for interactive resizing.
-void TableViewLastColumnResizingFixer::on_geometriesChanged()
-{
- if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
- {
- disconnectViewHeadersSignals();
- resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
- connectViewHeadersSignals();
- }
-}
-
-/**
- * Initializes all internal variables and prepares the
- * the resize modes of the last 2 columns of the table and
- */
-TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent) :
- QObject(parent),
- tableView(table),
- lastColumnMinimumWidth(lastColMinimumWidth),
- allColumnsMinimumWidth(allColsMinimumWidth)
-{
- columnCount = tableView->horizontalHeader()->count();
- lastColumnIndex = columnCount - 1;
- secondToLastColumnIndex = columnCount - 2;
- tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth);
- setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
- setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
-}
-
#ifdef WIN32
fs::path static StartupShortcutPath()
{
@@ -766,15 +657,19 @@ QString NetworkToQString(Network net)
assert(false);
}
-QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes)
+QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction)
{
+ QString prefix;
+ if (prepend_direction) {
+ prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " ";
+ }
switch (conn_type) {
- case ConnectionType::INBOUND: return relay_txes ? QObject::tr("Inbound Full Relay") : QObject::tr("Inbound Block Relay");
- case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay");
- case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay");
- case ConnectionType::MANUAL: return QObject::tr("Outbound Manual");
- case ConnectionType::FEELER: return QObject::tr("Outbound Feeler");
- case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch");
+ case ConnectionType::INBOUND: return prefix;
+ case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay");
+ case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay");
+ case ConnectionType::MANUAL: return prefix + QObject::tr("Manual");
+ case ConnectionType::FEELER: return prefix + QObject::tr("Feeler");
+ case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch");
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -788,13 +683,13 @@ QString formatDurationStr(int secs)
int seconds = secs % 60;
if (days)
- strList.append(QString(QObject::tr("%1 d")).arg(days));
+ strList.append(QObject::tr("%1 d").arg(days));
if (hours)
- strList.append(QString(QObject::tr("%1 h")).arg(hours));
+ strList.append(QObject::tr("%1 h").arg(hours));
if (mins)
- strList.append(QString(QObject::tr("%1 m")).arg(mins));
+ strList.append(QObject::tr("%1 m").arg(mins));
if (seconds || (!days && !hours && !mins))
- strList.append(QString(QObject::tr("%1 s")).arg(seconds));
+ strList.append(QObject::tr("%1 s").arg(seconds));
return strList.join(" ");
}
@@ -813,14 +708,16 @@ QString formatServicesStr(quint64 mask)
return QObject::tr("None");
}
-QString formatPingTime(int64_t ping_usec)
+QString formatPingTime(std::chrono::microseconds ping_time)
{
- return (ping_usec == std::numeric_limits<int64_t>::max() || ping_usec == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(ping_usec / 1000), 10));
+ return (ping_time == std::chrono::microseconds::max() || ping_time == 0us) ?
+ QObject::tr("N/A") :
+ QObject::tr("%1 ms").arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10));
}
QString formatTimeOffset(int64_t nTimeOffset)
{
- return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10));
+ return QObject::tr("%1 s").arg(QString::number((int)nTimeOffset, 10));
}
QString formatNiceTimeOffset(qint64 secs)
@@ -863,13 +760,13 @@ QString formatNiceTimeOffset(qint64 secs)
QString formatBytes(uint64_t bytes)
{
if(bytes < 1024)
- return QString(QObject::tr("%1 B")).arg(bytes);
+ return QObject::tr("%1 B").arg(bytes);
if(bytes < 1024 * 1024)
- return QString(QObject::tr("%1 KB")).arg(bytes / 1024);
+ return QObject::tr("%1 KB").arg(bytes / 1024);
if(bytes < 1024 * 1024 * 1024)
- return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024);
+ return QObject::tr("%1 MB").arg(bytes / 1024 / 1024);
- return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
+ return QObject::tr("%1 GB").arg(bytes / 1024 / 1024 / 1024);
}
qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index edfb5b13a2..6395ec6abd 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -20,6 +20,8 @@
#include <QString>
#include <QTableView>
+#include <chrono>
+
class QValidatedLineEdit;
class SendCoinsRecipient;
@@ -53,7 +55,7 @@ namespace GUIUtil
QString dateTimeStr(qint64 nTime);
// Return a monospace font
- QFont fixedPitchFont();
+ QFont fixedPitchFont(bool use_embedded_font = false);
// Set up widget for address
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
@@ -181,45 +183,6 @@ namespace GUIUtil
bool eventFilter(QObject* watched, QEvent* event) override;
};
- /**
- * Makes a QTableView last column feel as if it was being resized from its left border.
- * Also makes sure the column widths are never larger than the table's viewport.
- * In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right.
- * Usually our second to last columns behave as if stretched, and when on stretch mode, columns aren't resizable
- * interactively or programmatically.
- *
- * This helper object takes care of this issue.
- *
- */
- class TableViewLastColumnResizingFixer: public QObject
- {
- Q_OBJECT
-
- public:
- TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent);
- void stretchColumnWidth(int column);
-
- private:
- QTableView* tableView;
- int lastColumnMinimumWidth;
- int allColumnsMinimumWidth;
- int lastColumnIndex;
- int columnCount;
- int secondToLastColumnIndex;
-
- void adjustTableColumnsWidth();
- int getAvailableWidthForColumn(int column);
- int getColumnsWidth();
- void connectViewHeadersSignals();
- void disconnectViewHeadersSignals();
- void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode);
- void resizeColumn(int nColumnIndex, int width);
-
- private Q_SLOTS:
- void on_sectionResized(int logicalIndex, int oldSize, int newSize);
- void on_geometriesChanged();
- };
-
bool GetStartOnSystemStartup();
bool SetStartOnSystemStartup(bool fAutoStart);
@@ -233,7 +196,7 @@ namespace GUIUtil
QString NetworkToQString(Network net);
/** Convert enum ConnectionType to QString */
- QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes);
+ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction);
/** Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(int secs);
@@ -241,8 +204,8 @@ namespace GUIUtil
/** Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(quint64 mask);
- /** Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0 */
- QString formatPingTime(int64_t ping_usec);
+ /** Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0 */
+ QString formatPingTime(std::chrono::microseconds ping_time);
/** Format a CNodeCombinedStats.nTimeOffset into a user-readable string */
QString formatTimeOffset(int64_t nTimeOffset);
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index b1081f6aee..ee70c1bc30 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -22,7 +22,6 @@ static const struct {
{"signet", QAPP_APP_NAME_SIGNET, 35, 15},
{"regtest", QAPP_APP_NAME_REGTEST, 160, 30},
};
-static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
// titleAddText needs to be const char* for tr()
NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift, const int iconColorSaturationReduction, const char *_titleAddText):
@@ -81,14 +80,12 @@ NetworkStyle::NetworkStyle(const QString &_appName, const int iconColorHueShift,
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)
- {
+ for (const auto& network_style : network_styles) {
+ if (networkId == network_style.networkId) {
return new NetworkStyle(
- network_styles[x].appName,
- network_styles[x].iconColorHueShift,
- network_styles[x].iconColorSaturationReduction,
+ network_style.appName,
+ network_style.iconColorHueShift,
+ network_style.iconColorSaturationReduction,
titleAddText.c_str());
}
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 7b8d7871ec..e6b9488344 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -144,6 +144,20 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->minimizeToTray->setEnabled(false);
}
+ QFont embedded_font{GUIUtil::fixedPitchFont(true)};
+ ui->embeddedFont_radioButton->setText(ui->embeddedFont_radioButton->text().arg(QFontInfo(embedded_font).family()));
+ embedded_font.setWeight(QFont::Bold);
+ ui->embeddedFont_label_1->setFont(embedded_font);
+ ui->embeddedFont_label_9->setFont(embedded_font);
+
+ QFont system_font{GUIUtil::fixedPitchFont(false)};
+ ui->systemFont_radioButton->setText(ui->systemFont_radioButton->text().arg(QFontInfo(system_font).family()));
+ system_font.setWeight(QFont::Bold);
+ ui->systemFont_label_1->setFont(system_font);
+ ui->systemFont_label_9->setFont(system_font);
+ // Checking the embeddedFont_radioButton automatically unchecks the systemFont_radioButton.
+ ui->systemFont_radioButton->setChecked(true);
+
GUIUtil::handleCloseWindowShortcut(this);
}
@@ -246,6 +260,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->lang, OptionsModel::Language);
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
+ mapper->addMapping(ui->embeddedFont_radioButton, OptionsModel::UseEmbeddedMonospacedFont);
}
void OptionsDialog::setOkButtonState(bool fState)
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index 1cc96035c6..ba35ff3b67 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -67,7 +67,7 @@ private Q_SLOTS:
void updateDefaultProxyNets();
Q_SIGNALS:
- void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
+ void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
private:
Ui::OptionsDialog *ui;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 1e0391a35c..d51a5b06ff 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -163,6 +163,12 @@ void OptionsModel::Init(bool resetSettings)
addOverriddenOption("-lang");
language = settings.value("language").toString();
+
+ if (!settings.contains("UseEmbeddedMonospacedFont")) {
+ settings.setValue("UseEmbeddedMonospacedFont", "true");
+ }
+ m_use_embedded_monospaced_font = settings.value("UseEmbeddedMonospacedFont").toBool();
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
}
/** Helper function to copy contents from one QSettings to another.
@@ -326,6 +332,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return strThirdPartyTxUrls;
case Language:
return settings.value("language");
+ case UseEmbeddedMonospacedFont:
+ return m_use_embedded_monospaced_font;
case CoinControlFeatures:
return fCoinControlFeatures;
case Prune:
@@ -453,6 +461,11 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
setRestartRequired(true);
}
break;
+ case UseEmbeddedMonospacedFont:
+ m_use_embedded_monospaced_font = value.toBool();
+ settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+ break;
case CoinControlFeatures:
fCoinControlFeatures = value.toBool();
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index f7171951a1..4d012a9b8f 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -59,6 +59,7 @@ public:
DisplayUnit, // BitcoinUnits::Unit
ThirdPartyTxUrls, // QString
Language, // QString
+ UseEmbeddedMonospacedFont, // bool
CoinControlFeatures, // bool
ThreadsScriptVerif, // int
Prune, // bool
@@ -84,6 +85,7 @@ public:
bool getMinimizeOnClose() const { return fMinimizeOnClose; }
int getDisplayUnit() const { return nDisplayUnit; }
QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; }
+ bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; }
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
@@ -107,6 +109,7 @@ private:
QString language;
int nDisplayUnit;
QString strThirdPartyTxUrls;
+ bool m_use_embedded_monospaced_font;
bool fCoinControlFeatures;
/* settings that were overridden by command-line */
QString strOverriddenByCommandLine;
@@ -120,6 +123,7 @@ Q_SIGNALS:
void displayUnitChanged(int unit);
void coinControlFeaturesChanged(bool);
void showTrayIconChanged(bool);
+ void useEmbeddedMonospacedFontChanged(bool);
};
#endif // BITCOIN_QT_OPTIONSMODEL_H
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index bc542a0833..7f12b1d2b5 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -257,6 +257,9 @@ void OverviewPage::setClientModel(ClientModel *model)
// Show warning, for example if this is a prerelease version
connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts);
updateAlerts(model->getStatusBarWarnings());
+
+ connect(model->getOptionsModel(), &OptionsModel::useEmbeddedMonospacedFontChanged, this, &OverviewPage::setMonospacedFont);
+ setMonospacedFont(model->getOptionsModel()->getUseEmbeddedMonospacedFont());
}
}
@@ -321,3 +324,17 @@ void OverviewPage::showOutOfSyncWarning(bool fShow)
ui->labelWalletStatus->setVisible(fShow);
ui->labelTransactionsStatus->setVisible(fShow);
}
+
+void OverviewPage::setMonospacedFont(bool use_embedded_font)
+{
+ QFont f = GUIUtil::fixedPitchFont(use_embedded_font);
+ f.setWeight(QFont::Bold);
+ ui->labelBalance->setFont(f);
+ ui->labelUnconfirmed->setFont(f);
+ ui->labelImmature->setFont(f);
+ ui->labelTotal->setFont(f);
+ ui->labelWatchAvailable->setFont(f);
+ ui->labelWatchPending->setFont(f);
+ ui->labelWatchImmature->setFont(f);
+ ui->labelWatchTotal->setFont(f);
+}
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 578ef601fb..5158c81678 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -61,6 +61,7 @@ private Q_SLOTS:
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
void handleOutOfSyncWarningClicks();
+ void setMonospacedFont(bool use_embedded_font);
};
#endif // BITCOIN_QT_OVERVIEWPAGE_H
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index bad81d894c..3459bf4cf8 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -23,25 +23,25 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
- switch(column)
- {
+ switch (static_cast<PeerTableModel::ColumnIndex>(column)) {
case PeerTableModel::NetNodeId:
return pLeft->nodeid < pRight->nodeid;
case PeerTableModel::Address:
return pLeft->addrName.compare(pRight->addrName) < 0;
+ case PeerTableModel::ConnectionType:
+ return pLeft->m_conn_type < pRight->m_conn_type;
case PeerTableModel::Network:
return pLeft->m_network < pRight->m_network;
case PeerTableModel::Ping:
- return pLeft->m_min_ping_usec < pRight->m_min_ping_usec;
+ return pLeft->m_min_ping_time < pRight->m_min_ping_time;
case PeerTableModel::Sent:
return pLeft->nSendBytes < pRight->nSendBytes;
case PeerTableModel::Received:
return pLeft->nRecvBytes < pRight->nRecvBytes;
case PeerTableModel::Subversion:
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
- }
-
- return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
// private implementation
@@ -155,36 +155,44 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
+ const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole) {
- switch(index.column())
- {
+ switch (column) {
case NetNodeId:
return (qint64)rec->nodeStats.nodeid;
case Address:
// prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection
return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName);
+ case ConnectionType:
+ return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false);
case Network:
return GUIUtil::NetworkToQString(rec->nodeStats.m_network);
case Ping:
- return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec);
+ return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_time);
case Sent:
return GUIUtil::formatBytes(rec->nodeStats.nSendBytes);
case Received:
return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes);
case Subversion:
return QString::fromStdString(rec->nodeStats.cleanSubVer);
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
} else if (role == Qt::TextAlignmentRole) {
- switch (index.column()) {
- case Network:
- return QVariant(Qt::AlignCenter);
- case Ping:
- case Sent:
- case Received:
- return QVariant(Qt::AlignRight | Qt::AlignVCenter);
- default:
- return QVariant();
- }
+ switch (column) {
+ case NetNodeId:
+ case Address:
+ return {};
+ case ConnectionType:
+ case Network:
+ return QVariant(Qt::AlignCenter);
+ case Ping:
+ case Sent:
+ case Received:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+ case Subversion:
+ return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
} else if (role == StatsRole) {
switch (index.column()) {
case NetNodeId: return QVariant::fromValue(rec);
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 7bff239507..0823235ec0 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -59,12 +59,13 @@ public:
enum ColumnIndex {
NetNodeId = 0,
- Address = 1,
- Network = 2,
- Ping = 3,
- Sent = 4,
- Received = 5,
- Subversion = 6
+ Address,
+ ConnectionType,
+ Network,
+ Ping,
+ Sent,
+ Received,
+ Subversion
};
enum {
@@ -87,7 +88,7 @@ public Q_SLOTS:
private:
interfaces::Node& m_node;
- const QStringList columns{tr("Peer Id"), tr("Address"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")};
+ const QStringList columns{tr("Peer Id"), tr("Address"), tr("Type"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")};
std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index c6b80fd340..aab8d8e4af 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -23,7 +23,6 @@ static const struct {
/* Other: linux, unix, ... */
{"other", true, true, false}
};
-static const unsigned platform_styles_count = sizeof(platform_styles)/sizeof(*platform_styles);
namespace {
/* Local functions for colorizing single-color images */
@@ -121,15 +120,13 @@ QIcon PlatformStyle::TextColorIcon(const QIcon& icon) const
const PlatformStyle *PlatformStyle::instantiate(const QString &platformId)
{
- for (unsigned x=0; x<platform_styles_count; ++x)
- {
- if (platformId == platform_styles[x].platformId)
- {
+ for (const auto& platform_style : platform_styles) {
+ if (platformId == platform_style.platformId) {
return new PlatformStyle(
- platform_styles[x].platformId,
- platform_styles[x].imagesOnButtons,
- platform_styles[x].colorizeIcons,
- platform_styles[x].useExtraSpacing);
+ platform_style.platformId,
+ platform_style.imagesOnButtons,
+ platform_style.colorizeIcons,
+ platform_style.useExtraSpacing);
}
}
return nullptr;
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 55ab6046cf..17746b395b 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -141,11 +141,11 @@ void PSBTOperationsDialog::saveTransaction() {
filename_suggestion.append(".psbt");
QString filename = GUIUtil::getSaveFileName(this,
tr("Save Transaction Data"), filename_suggestion,
- tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter);
+ tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selected_filter);
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data());
+ std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
out << ssTx.str();
out.close();
showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index 490826cbbb..a71c8831e9 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -120,7 +120,9 @@ void QRImageWidget::saveImage()
{
if (!GUIUtil::HasPixmap(this))
return;
- QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr);
+ QString fn = GUIUtil::getSaveFileName(
+ this, tr("Save QR Code"), QString(),
+ tr("PNG Image", "Name of PNG file format") + QLatin1String(" (*.png)"), nullptr);
if (!fn.isEmpty())
{
exportImage().save(fn);
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 0aea920c86..61cb89d75a 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -19,12 +19,12 @@
#include <QCursor>
#include <QMessageBox>
#include <QScrollBar>
+#include <QSettings>
#include <QTextDocument>
ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::ReceiveCoinsDialog),
- columnResizingFixer(nullptr),
model(nullptr),
platformStyle(_platformStyle)
{
@@ -44,13 +44,15 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu actions
QAction *copyURIAction = new QAction(tr("Copy URI"), this);
- QAction *copyLabelAction = new QAction(tr("Copy label"), this);
- QAction *copyMessageAction = new QAction(tr("Copy message"), this);
- QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
+ QAction* copyAddressAction = new QAction(tr("Copy address"), this);
+ copyLabelAction = new QAction(tr("Copy label"), this);
+ copyMessageAction = new QAction(tr("Copy message"), this);
+ copyAmountAction = new QAction(tr("Copy amount"), this);
// context menu
contextMenu = new QMenu(this);
contextMenu->addAction(copyURIAction);
+ contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyMessageAction);
contextMenu->addAction(copyAmountAction);
@@ -58,11 +60,27 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu signals
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(copyURIAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyURI);
+ connect(copyAddressAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyAddress);
connect(copyLabelAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyLabel);
connect(copyMessageAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyMessage);
connect(copyAmountAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyAmount);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
+
+ QTableView* tableView = ui->recentRequestsView;
+ tableView->verticalHeader()->hide();
+ tableView->setAlternatingRowColors(true);
+ tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
+
+ QSettings settings;
+ if (!tableView->horizontalHeader()->restoreState(settings.value("RecentRequestsViewHeaderState").toByteArray())) {
+ tableView->setColumnWidth(RecentRequestsTableModel::Date, DATE_COLUMN_WIDTH);
+ tableView->setColumnWidth(RecentRequestsTableModel::Label, LABEL_COLUMN_WIDTH);
+ tableView->setColumnWidth(RecentRequestsTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
+ tableView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH);
+ tableView->horizontalHeader()->setStretchLastSection(true);
+ }
}
void ReceiveCoinsDialog::setModel(WalletModel *_model)
@@ -76,22 +94,12 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
updateDisplayUnit();
QTableView* tableView = ui->recentRequestsView;
-
- tableView->verticalHeader()->hide();
- tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
tableView->setModel(_model->getRecentRequestsTableModel());
- tableView->setAlternatingRowColors(true);
- tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
- tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
- tableView->setColumnWidth(RecentRequestsTableModel::Date, DATE_COLUMN_WIDTH);
- tableView->setColumnWidth(RecentRequestsTableModel::Label, LABEL_COLUMN_WIDTH);
- tableView->setColumnWidth(RecentRequestsTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
+ tableView->sortByColumn(RecentRequestsTableModel::Date, Qt::DescendingOrder);
connect(tableView->selectionModel(),
&QItemSelectionModel::selectionChanged, this,
&ReceiveCoinsDialog::recentRequestsView_selectionChanged);
- // 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->useBech32->setCheckState(Qt::Checked);
@@ -111,6 +119,8 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
ReceiveCoinsDialog::~ReceiveCoinsDialog()
{
+ QSettings settings;
+ settings.setValue("RecentRequestsViewHeaderState", ui->recentRequestsView->horizontalHeader()->saveState());
delete ui;
}
@@ -235,14 +245,6 @@ void ReceiveCoinsDialog::on_removeRequestButton_clicked()
model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent());
}
-// We override the virtual resizeEvent of the QWidget to adjust tables column
-// sizes as the tables width is proportional to the dialogs width.
-void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
-{
- QWidget::resizeEvent(event);
- columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
-}
-
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
@@ -268,9 +270,18 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column)
// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point)
{
- if (!selectedRow().isValid()) {
+ const QModelIndex sel = selectedRow();
+ if (!sel.isValid()) {
return;
}
+
+ // disable context menu actions when appropriate
+ const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel();
+ const RecentRequestEntry& req = submodel->entry(sel.row());
+ copyLabelAction->setDisabled(req.recipient.label.isEmpty());
+ copyMessageAction->setDisabled(req.recipient.message.isEmpty());
+ copyAmountAction->setDisabled(req.recipient.amount == 0);
+
contextMenu->exec(QCursor::pos());
}
@@ -287,6 +298,19 @@ void ReceiveCoinsDialog::copyURI()
GUIUtil::setClipboard(uri);
}
+// context menu action: copy address
+void ReceiveCoinsDialog::copyAddress()
+{
+ const QModelIndex sel = selectedRow();
+ if (!sel.isValid()) {
+ return;
+ }
+
+ const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel();
+ const QString address = submodel->entry(sel.row()).recipient.address;
+ GUIUtil::setClipboard(address);
+}
+
// context menu action: copy label
void ReceiveCoinsDialog::copyLabel()
{
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 9b89bd6a8b..fbbccc5a33 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -51,14 +51,15 @@ public Q_SLOTS:
private:
Ui::ReceiveCoinsDialog *ui;
- GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
WalletModel *model;
QMenu *contextMenu;
+ QAction* copyLabelAction;
+ QAction* copyMessageAction;
+ QAction* copyAmountAction;
const PlatformStyle *platformStyle;
QModelIndex selectedRow();
void copyColumnToClipboard(int column);
- virtual void resizeEvent(QResizeEvent *event) override;
private Q_SLOTS:
void on_receiveButton_clicked();
@@ -69,6 +70,7 @@ private Q_SLOTS:
void updateDisplayUnit();
void showMenu(const QPoint &point);
void copyURI();
+ void copyAddress();
void copyLabel();
void copyMessage();
void copyAmount();
diff --git a/src/qt/res/fonts/RobotoMono-Bold.ttf b/src/qt/res/fonts/RobotoMono-Bold.ttf
new file mode 100644
index 0000000000..900fce6848
--- /dev/null
+++ b/src/qt/res/fonts/RobotoMono-Bold.ttf
Binary files differ
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index a252685d2f..b258219894 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -462,7 +462,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
- tr("Inbound Full/Block Relay: initiated by peer"),
+ tr("Inbound: initiated by peer"),
tr("Outbound Full Relay: default"),
tr("Outbound Block Relay: does not relay transactions or addresses"),
tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options")
@@ -473,6 +473,11 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
+ const QString hb_list{"<ul><li>\""
+ + ts.to + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\""
+ + ts.from + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\""
+ + ts.no + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"};
+ ui->peerHighBandwidthLabel->setToolTip(ui->peerHighBandwidthLabel->toolTip().arg(hb_list));
ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME));
@@ -614,10 +619,10 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create peer table context menu actions
QAction* disconnectAction = new QAction(tr("&Disconnect"), this);
- QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
- QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this);
- QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this);
- QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this);
+ QAction* banAction1h = new QAction(ts.ban_for + " " + tr("1 &hour"), this);
+ QAction* banAction24h = new QAction(ts.ban_for + " " + tr("1 &day"), this);
+ QAction* banAction7d = new QAction(ts.ban_for + " " + tr("1 &week"), this);
+ QAction* banAction365d = new QAction(ts.ban_for + " " + tr("1 &year"), this);
// create peer table context menu
peersTableContextMenu = new QMenu(this);
@@ -1097,7 +1102,7 @@ void RPCConsole::updateDetailWidget()
{
const QList<QModelIndex> selected_peers = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
if (!clientModel || !clientModel->getPeerTableModel() || selected_peers.size() != 1) {
- ui->detailWidget->hide();
+ ui->peersTabRightPanel->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
return;
}
@@ -1109,21 +1114,29 @@ void RPCConsole::updateDetailWidget()
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
- ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never"));
- ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never"));
+ ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? ts.yes : ts.no);
+ QString bip152_hb_settings;
+ if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings = ts.to;
+ if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings.isEmpty() ? ts.from : QLatin1Char('/') + ts.from);
+ if (bip152_hb_settings.isEmpty()) bip152_hb_settings = ts.no;
+ ui->peerHighBandwidth->setText(bip152_hb_settings);
+ const int64_t time_now{GetSystemTimeInSeconds()};
+ ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.nTimeConnected));
+ ui->peerLastBlock->setText(TimeDurationField(time_now, stats->nodeStats.nLastBlockTime));
+ ui->peerLastTx->setText(TimeDurationField(time_now, stats->nodeStats.nLastTXTime));
+ ui->peerLastSend->setText(TimeDurationField(time_now, stats->nodeStats.nLastSend));
+ ui->peerLastRecv->setText(TimeDurationField(time_now, stats->nodeStats.nLastRecv));
ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
- ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
- ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_usec));
- ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_wait_usec));
- ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec));
+ ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
+ ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
- ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, stats->nodeStats.fRelayTxes));
+ ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true));
ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
if (stats->nodeStats.m_permissionFlags == PF_NONE) {
- ui->peerPermissions->setText(tr("N/A"));
+ ui->peerPermissions->setText(ts.na);
} else {
QStringList permissions;
for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) {
@@ -1131,27 +1144,28 @@ void RPCConsole::updateDetailWidget()
}
ui->peerPermissions->setText(permissions.join(" & "));
}
- ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A"));
+ ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : ts.na);
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
// Sync height is init to -1
- if (stats->nodeStateStats.nSyncHeight > -1)
+ if (stats->nodeStateStats.nSyncHeight > -1) {
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
- else
- ui->peerSyncHeight->setText(tr("Unknown"));
-
+ } else {
+ ui->peerSyncHeight->setText(ts.unknown);
+ }
// Common height is init to -1
- if (stats->nodeStateStats.nCommonHeight > -1)
+ if (stats->nodeStateStats.nCommonHeight > -1) {
ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight));
- else
- ui->peerCommonHeight->setText(tr("Unknown"));
-
+ } else {
+ ui->peerCommonHeight->setText(ts.unknown);
+ }
ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
+ ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait));
}
- ui->detailWidget->show();
+ ui->peersTabRightPanel->show();
}
void RPCConsole::resizeEvent(QResizeEvent *event)
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 5f308dc36d..b9806e40c9 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -136,6 +136,11 @@ Q_SIGNALS:
void cmdRequest(const QString &command, const WalletModel* wallet_model);
private:
+ struct TranslatedStrings {
+ const QString yes{tr("Yes")}, no{tr("No")}, to{tr("To")}, from{tr("From")},
+ ban_for{tr("Ban for")}, na{tr("N/A")}, unknown{tr("Unknown")};
+ } const ts;
+
void startExecutor();
void setTrafficGraphRange(int mins);
@@ -168,6 +173,11 @@ private:
/** Update UI with latest network info from model. */
void updateNetworkState();
+ /** Helper for the output of a time duration field. Inputs are UNIX epoch times. */
+ QString TimeDurationField(uint64_t time_now, uint64_t time_at_event) const {
+ return time_at_event ? GUIUtil::formatDurationStr(time_now - time_at_event) : tr("Never");
+ }
+
private Q_SLOTS:
void updateAlerts(const QString& warnings);
};
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 611f3584c3..95e1ce2210 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -266,7 +266,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
}
// prepare transaction for getting txFee earlier
- m_current_transaction = MakeUnique<WalletModelTransaction>(recipients);
+ m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
WalletModel::SendCoinsReturn prepareStatus;
updateCoinControlState(*m_coin_control);
@@ -430,11 +430,11 @@ void SendCoinsDialog::on_sendButton_clicked()
fileNameSuggestion.append(".psbt");
QString filename = GUIUtil::getSaveFileName(this,
tr("Save Transaction Data"), fileNameSuggestion,
- tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter);
+ tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selectedFilter);
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data());
+ std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
out << ssTx.str();
out.close();
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 35fcb2b0ca..a026069232 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -112,7 +112,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet, nullopt);
+ RemoveWallet(wallet, std::nullopt);
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 9ef5fe8fc7..a1baf6a402 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -54,6 +54,13 @@ int main(int argc, char* argv[])
NodeContext node_context;
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
+ gArgs.ForceSetArg("-listen", "0");
+ gArgs.ForceSetArg("-listenonion", "0");
+ gArgs.ForceSetArg("-discover", "0");
+ gArgs.ForceSetArg("-dnsseed", "0");
+ gArgs.ForceSetArg("-fixedseeds", "0");
+ gArgs.ForceSetArg("-upnp", "0");
+ gArgs.ForceSetArg("-natpmp", "0");
bool fInvalid = false;
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index d6d2d0e3df..1107c44dc9 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -167,7 +167,7 @@ void TestGUI(interfaces::Node& node)
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet, nullopt);
+ RemoveWallet(wallet, std::nullopt);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index e6d483364c..a7556eed04 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -526,27 +526,30 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return QVariant();
TransactionRecord *rec = static_cast<TransactionRecord*>(index.internalPointer());
- switch(role)
- {
+ const auto column = static_cast<ColumnIndex>(index.column());
+ switch (role) {
case RawDecorationRole:
- switch(index.column())
- {
+ switch (column) {
case Status:
return txStatusDecoration(rec);
case Watchonly:
return txWatchonlyDecoration(rec);
+ case Date: return {};
+ case Type: return {};
case ToAddress:
return txAddressDecoration(rec);
- }
- break;
+ case Amount: return {};
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::DecorationRole:
{
QIcon icon = qvariant_cast<QIcon>(index.data(RawDecorationRole));
return platformStyle->TextColorIcon(icon);
}
case Qt::DisplayRole:
- switch(index.column())
- {
+ switch (column) {
+ case Status: return {};
+ case Watchonly: return {};
case Date:
return formatTxDate(rec);
case Type:
@@ -555,12 +558,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return formatTxToAddress(rec, false);
case Amount:
return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS);
- }
- break;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::EditRole:
// Edit role is used for sorting, so return the unformatted values
- switch(index.column())
- {
+ switch (column) {
case Status:
return QString::fromStdString(rec->status.sortKey);
case Date:
@@ -573,8 +575,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
return formatTxToAddress(rec, true);
case Amount:
return qint64(rec->credit + rec->debit);
- }
- break;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
case Qt::ToolTipRole:
return formatTooltip(rec);
case Qt::TextAlignmentRole:
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 0cf6480b82..42e08c6af7 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -31,6 +31,7 @@
#include <QMenu>
#include <QPoint>
#include <QScrollBar>
+#include <QSettings>
#include <QTableView>
#include <QTimer>
#include <QUrl>
@@ -126,27 +127,39 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
vlayout->setContentsMargins(0,0,0,0);
vlayout->setSpacing(0);
- QTableView *view = new QTableView(this);
+ transactionView = new QTableView(this);
+ transactionView->setObjectName("transactionView");
vlayout->addLayout(hlayout);
vlayout->addWidget(createDateRangeWidget());
- vlayout->addWidget(view);
+ vlayout->addWidget(transactionView);
vlayout->setSpacing(0);
- int width = view->verticalScrollBar()->sizeHint().width();
+ int width = transactionView->verticalScrollBar()->sizeHint().width();
// Cover scroll bar width with spacing
if (platformStyle->getUseExtraSpacing()) {
hlayout->addSpacing(width+2);
} else {
hlayout->addSpacing(width);
}
- // Always show scroll bar
- view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- view->setTabKeyNavigation(false);
- view->setContextMenuPolicy(Qt::CustomContextMenu);
-
- view->installEventFilter(this);
-
- transactionView = view;
- transactionView->setObjectName("transactionView");
+ transactionView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ transactionView->setTabKeyNavigation(false);
+ transactionView->setContextMenuPolicy(Qt::CustomContextMenu);
+ transactionView->installEventFilter(this);
+ transactionView->setAlternatingRowColors(true);
+ transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ transactionView->setSortingEnabled(true);
+ transactionView->verticalHeader()->hide();
+
+ QSettings settings;
+ if (!transactionView->horizontalHeader()->restoreState(settings.value("TransactionViewHeaderState").toByteArray())) {
+ transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH);
+ transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH);
+ transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH);
+ transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH);
+ transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
+ transactionView->horizontalHeader()->setMinimumSectionSize(MINIMUM_COLUMN_WIDTH);
+ transactionView->horizontalHeader()->setStretchLastSection(true);
+ }
// Actions
abandonAction = new QAction(tr("Abandon transaction"), this);
@@ -158,7 +171,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
QAction *copyTxIDAction = new QAction(tr("Copy transaction ID"), this);
QAction *copyTxHexAction = new QAction(tr("Copy raw transaction"), this);
QAction *copyTxPlainText = new QAction(tr("Copy full transaction details"), this);
- QAction *editLabelAction = new QAction(tr("Edit label"), this);
+ QAction *editLabelAction = new QAction(tr("Edit address label"), this);
QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
contextMenu = new QMenu(this);
@@ -183,8 +196,8 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(search_widget, &QLineEdit::textChanged, prefix_typing_delay, static_cast<void (QTimer::*)()>(&QTimer::start));
connect(prefix_typing_delay, &QTimer::timeout, this, &TransactionView::changedSearch);
- connect(view, &QTableView::doubleClicked, this, &TransactionView::doubleClicked);
- connect(view, &QTableView::customContextMenuRequested, this, &TransactionView::contextualMenu);
+ connect(transactionView, &QTableView::doubleClicked, this, &TransactionView::doubleClicked);
+ connect(transactionView, &QTableView::customContextMenuRequested, this, &TransactionView::contextualMenu);
connect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
connect(abandonAction, &QAction::triggered, this, &TransactionView::abandonTx);
@@ -204,6 +217,12 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
});
}
+TransactionView::~TransactionView()
+{
+ QSettings settings;
+ settings.setValue("TransactionViewHeaderState", transactionView->horizontalHeader()->saveState());
+}
+
void TransactionView::setModel(WalletModel *_model)
{
this->model = _model;
@@ -214,25 +233,9 @@ void TransactionView::setModel(WalletModel *_model)
transactionProxyModel->setDynamicSortFilter(true);
transactionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
transactionProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
-
transactionProxyModel->setSortRole(Qt::EditRole);
-
- transactionView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
transactionView->setModel(transactionProxyModel);
- transactionView->setAlternatingRowColors(true);
- transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
- transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
- transactionView->horizontalHeader()->setSortIndicator(TransactionTableModel::Date, Qt::DescendingOrder);
- transactionView->setSortingEnabled(true);
- transactionView->verticalHeader()->hide();
-
- transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH);
- transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH);
- transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH);
- transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH);
- transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
-
- columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this);
+ transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder);
if (_model->getOptionsModel())
{
@@ -354,7 +357,7 @@ void TransactionView::exportClicked()
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Transaction History"), QString(),
- tr("Comma separated file (*.csv)"), nullptr);
+ tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
return;
@@ -623,14 +626,6 @@ void TransactionView::focusTransaction(const uint256& txid)
}
}
-// We override the virtual resizeEvent of the QWidget to adjust tables column
-// sizes as the tables width is proportional to the dialogs width.
-void TransactionView::resizeEvent(QResizeEvent* event)
-{
- QWidget::resizeEvent(event);
- columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress);
-}
-
// Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text
bool TransactionView::eventFilter(QObject *obj, QEvent *event)
{
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index b268823066..35ada4aa7a 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -35,6 +35,7 @@ class TransactionView : public QWidget
public:
explicit TransactionView(const PlatformStyle *platformStyle, QWidget *parent = nullptr);
+ ~TransactionView();
void setModel(WalletModel *model);
@@ -82,10 +83,6 @@ private:
QWidget *createDateRangeWidget();
- GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer{nullptr};
-
- virtual void resizeEvent(QResizeEvent* event) override;
-
bool eventFilter(QObject *obj, QEvent *event) override;
private Q_SLOTS:
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index b1e6b43e60..8612893683 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -273,7 +273,7 @@ void WalletView::backupWallet()
{
QString filename = GUIUtil::getSaveFileName(this,
tr("Backup Wallet"), QString(),
- tr("Wallet Data (*.dat)"), nullptr);
+ tr("Wallet Data", "Name of wallet data file format") + QLatin1String(" (*.dat)"), nullptr);
if (filename.isEmpty())
return;
diff --git a/src/random.cpp b/src/random.cpp
index af9504e0ce..9900825abb 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -38,7 +38,6 @@
#include <sys/random.h>
#endif
#ifdef HAVE_SYSCTL_ARND
-#include <util/strencodings.h> // for ARRAYLEN
#include <sys/sysctl.h>
#endif
@@ -333,7 +332,7 @@ void GetOSRand(unsigned char *ent32)
int have = 0;
do {
size_t len = NUM_OS_RANDOM_BYTES - have;
- if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) {
+ if (sysctl(name, std::size(name), ent32 + have, &len, nullptr, 0) != 0) {
RandFailure();
}
have += len;
diff --git a/src/rest.cpp b/src/rest.cpp
index f872f6e59d..71426a4dc4 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -19,7 +19,6 @@
#include <txmempool.h>
#include <util/check.h>
#include <util/ref.h>
-#include <util/strencodings.h>
#include <validation.h>
#include <version.h>
@@ -117,9 +116,10 @@ static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
param = strReq.substr(0, pos);
const std::string suff(strReq, pos + 1);
- for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
- if (suff == rf_names[i].name)
- return rf_names[i].rf;
+ for (const auto& rf_name : rf_names) {
+ if (suff == rf_name.name)
+ return rf_name.rf;
+ }
/* If no suffix is found, return original string. */
param = strReq;
@@ -129,12 +129,13 @@ static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
static std::string AvailableDataFormatsString()
{
std::string formats;
- for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
- if (strlen(rf_names[i].name) > 0) {
+ for (const auto& rf_name : rf_names) {
+ if (strlen(rf_name.name) > 0) {
formats.append(".");
- formats.append(rf_names[i].name);
+ formats.append(rf_name.name);
formats.append(", ");
}
+ }
if (formats.length() > 0)
return formats.substr(0, formats.length() - 2);
@@ -695,6 +696,7 @@ void InterruptREST()
void StopREST()
{
- for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
- UnregisterHTTPHandler(uri_prefixes[i].prefix, false);
+ for (const auto& up : uri_prefixes) {
+ UnregisterHTTPHandler(up.prefix, false);
+ }
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index a83b45ae1d..b90dc9bf42 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -17,8 +17,8 @@
#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
-#include <policy/fees.h>
#include <policy/feerate.h>
+#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -156,21 +156,11 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
- // Serialize passed information without accessing chain state of the active chain!
- AssertLockNotHeld(cs_main); // For performance reasons
+ UniValue result = blockheaderToJSON(tip, blockindex);
- UniValue result(UniValue::VOBJ);
- result.pushKV("hash", blockindex->GetBlockHash().GetHex());
- const CBlockIndex* pnext;
- int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
- result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
- result.pushKV("height", blockindex->nHeight);
- result.pushKV("version", block.nVersion);
- result.pushKV("versionHex", strprintf("%08x", block.nVersion));
- result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
UniValue txs(UniValue::VARR);
if (txDetails) {
CBlockUndo blockUndo;
@@ -189,18 +179,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
}
}
result.pushKV("tx", txs);
- result.pushKV("time", block.GetBlockTime());
- result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
- result.pushKV("nonce", (uint64_t)block.nNonce);
- result.pushKV("bits", strprintf("%08x", block.nBits));
- result.pushKV("difficulty", GetDifficulty(blockindex));
- result.pushKV("chainwork", blockindex->nChainWork.GetHex());
- result.pushKV("nTx", (uint64_t)blockindex->nTx);
- if (blockindex->pprev)
- result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
- if (pnext)
- result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
@@ -821,8 +800,8 @@ static RPCHelpMan getblockheader()
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
- {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
- {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"},
}},
RPCResult{"for verbose=false",
RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
@@ -929,8 +908,8 @@ static RPCHelpMan getblock()
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
- {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
- {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"},
}},
RPCResult{"for verbosity = 2",
RPCResult::Type::OBJ, "", "",
@@ -1036,7 +1015,7 @@ static RPCHelpMan pruneblockchain()
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
- PruneBlockFilesManual(height);
+ PruneBlockFilesManual(::ChainstateActive(), height);
const CBlockIndex* block = ::ChainActive().Tip();
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
@@ -1047,13 +1026,26 @@ static RPCHelpMan pruneblockchain()
};
}
+CoinStatsHashType ParseHashType(const std::string& hash_type_input)
+{
+ if (hash_type_input == "hash_serialized_2") {
+ return CoinStatsHashType::HASH_SERIALIZED;
+ } else if (hash_type_input == "muhash") {
+ return CoinStatsHashType::MUHASH;
+ } else if (hash_type_input == "none") {
+ return CoinStatsHashType::NONE;
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s is not a valid hash_type", hash_type_input));
+ }
+}
+
static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{
- {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'none'."},
+ {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1063,7 +1055,8 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
- {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
@@ -1078,11 +1071,11 @@ static RPCHelpMan gettxoutsetinfo()
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
- const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
+ const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
- CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
+ CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB());
NodeContext& node = EnsureNodeContext(request.context);
- if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) {
+ if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1091,6 +1084,9 @@ static RPCHelpMan gettxoutsetinfo()
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
}
+ if (hash_type == CoinStatsHashType::MUHASH) {
+ ret.pushKV("muhash", stats.hashSerialized.GetHex());
+ }
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
@@ -1104,30 +1100,29 @@ static RPCHelpMan gettxoutsetinfo()
static RPCHelpMan gettxout()
{
return RPCHelpMan{"gettxout",
- "\nReturns details about an unspent transaction output.\n",
- {
- {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
- {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
- {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
- {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
- {RPCResult::Type::OBJ, "scriptPubKey", "",
- {
- {RPCResult::Type::STR_HEX, "asm", ""},
- {RPCResult::Type::STR_HEX, "hex", ""},
- {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
- {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
- {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses",
- {{RPCResult::Type::STR, "address", "bitcoin address"}}},
- }},
- {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
- }},
- RPCExamples{
+ "\nReturns details about an unspent transaction output.\n",
+ {
+ {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
+ {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
+ {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
+ },
+ {
+ RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
+ RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "", {
+ {RPCResult::Type::STR_HEX, "asm", ""},
+ {RPCResult::Type::STR_HEX, "hex", ""},
+ {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
+ {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
+ {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", {{RPCResult::Type::STR, "address", "bitcoin address"}}},
+ }},
+ {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
+ }},
+ },
+ RPCExamples{
"\nGet unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
"\nView the details\n"
@@ -1204,7 +1199,7 @@ static RPCHelpMan verifychain()
LOCK(cs_main);
- return CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+ return CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
},
};
}
@@ -1236,7 +1231,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
+ const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
switch (thresholdState) {
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
@@ -1250,12 +1245,12 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
+ int64_t since_height = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache);
bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
+ BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1500,6 +1495,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("size", (int64_t)pool.size());
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
+ ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
@@ -1520,6 +1516,7 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::NUM, "size", "Current tx count"},
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
+ {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritizetransaction"},
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
@@ -1564,7 +1561,7 @@ static RPCHelpMan preciousblock()
}
BlockValidationState state;
- PreciousBlock(state, Params(), pblockindex);
+ ::ChainstateActive().PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1600,7 +1597,7 @@ static RPCHelpMan invalidateblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
- InvalidateBlock(state, Params(), pblockindex);
+ ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
::ChainstateActive().ActivateBestChain(state, Params());
@@ -1639,7 +1636,7 @@ static RPCHelpMan reconsiderblock()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- ResetBlockFailureFlags(pblockindex);
+ ::ChainstateActive().ResetBlockFailureFlags(pblockindex);
}
BlockValidationState state;
@@ -2139,59 +2136,63 @@ public:
static RPCHelpMan scantxoutset()
{
return RPCHelpMan{"scantxoutset",
- "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
- "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
- "Examples of output descriptors are:\n"
- " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
- " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
- " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
- " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
- "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
- "unhardened or hardened child keys.\n"
- "In the latter case, a range needs to be specified by below if different from 1000.\n"
- "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
+ "Examples of output descriptors are:\n"
+ " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
+ "unhardened or hardened child keys.\n"
+ "In the latter case, a range needs to be specified by below if different from 1000.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ {
+ {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
+ "\"start\" for starting a scan\n"
+ "\"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ "\"status\" for progress report (in %) of the current scan"},
+ {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
+ "Every scan object is either a string descriptor or an object:",
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
{
- {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
- " \"start\" for starting a scan\n"
- " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
- " \"status\" for progress report (in %) of the current scan"},
- {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
- " Every scan object is either a string descriptor or an object:",
- {
- {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
- {
- {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
- {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
- },
- },
- },
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
+ }},
+ },
"[scanobjects,...]"},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
+ },
+ {
+ RPCResult{"When action=='abort'", RPCResult::Type::BOOL, "", ""},
+ RPCResult{"When action=='status' and no scan is in progress", RPCResult::Type::NONE, "", ""},
+ RPCResult{"When action=='status' and scan is in progress", RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "progress", "The scan progress"},
+ }},
+ RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
+ {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
+ {RPCResult::Type::NUM, "height", "The current block height (index)"},
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::ARR, "unspents", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
- {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
- {RPCResult::Type::NUM, "height", "The current block height (index)"},
- {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
- {RPCResult::Type::ARR, "unspents", "",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
- {RPCResult::Type::NUM, "vout", "The vout value"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
- {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
- {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
- {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
- }},
- }},
- {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
+ {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
+ {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
}},
- RPCExamples{""},
+ }},
+ {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
+ }},
+ },
+ RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
@@ -2413,10 +2414,21 @@ static RPCHelpMan dumptxoutset()
FILE* file{fsbridge::fopen(temppath, "wb")};
CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
+ NodeContext& node = EnsureNodeContext(request.context);
+ UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile);
+ fs::rename(temppath, path);
+
+ result.pushKV("path", path.string());
+ return result;
+},
+ };
+}
+
+UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile)
+{
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats;
CBlockIndex* tip;
- NodeContext& node = EnsureNodeContext(request.context);
{
// We need to lock cs_main to ensure that the coinsdb isn't written to
@@ -2433,13 +2445,13 @@ static RPCHelpMan dumptxoutset()
//
LOCK(::cs_main);
- ::ChainstateActive().ForceFlushStateToDisk();
+ chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
+ if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
- pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
+ pcursor = std::unique_ptr<CCoinsViewCursor>(chainstate.CoinsDB().Cursor());
tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock);
CHECK_NONFATAL(tip);
}
@@ -2464,16 +2476,13 @@ static RPCHelpMan dumptxoutset()
}
afile.fclose();
- fs::rename(temppath, path);
UniValue result(UniValue::VOBJ);
result.pushKV("coins_written", stats.coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight);
- result.pushKV("path", path.string());
+
return result;
-},
- };
}
void RegisterBlockchainRPCCommands(CRPCTable &t)
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index e4ce80400e..d8cae4dd24 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -6,6 +6,7 @@
#define BITCOIN_RPC_BLOCKCHAIN_H
#include <amount.h>
+#include <streams.h>
#include <sync.h>
#include <stdint.h>
@@ -16,6 +17,7 @@ extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
class CBlockPolicyEstimator;
+class CChainState;
class CTxMemPool;
class ChainstateManager;
class UniValue;
@@ -57,4 +59,10 @@ CTxMemPool& EnsureMemPool(const util::Ref& context);
ChainstateManager& EnsureChainman(const util::Ref& context);
CBlockPolicyEstimator& EnsureFeeEstimator(const util::Ref& context);
+/**
+ * Helper to create UTXO snapshots given a chainstate and a file handle.
+ * @return a UniValue map containing metadata about the snapshot.
+ */
+UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile);
+
#endif
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index d1eb849b7e..2b593cd10b 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -183,6 +183,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createwallet", 4, "avoid_reuse"},
{ "createwallet", 5, "descriptors"},
{ "createwallet", 6, "load_on_startup"},
+ { "createwallet", 7, "external_signer"},
{ "loadwallet", 1, "load_on_startup"},
{ "unloadwallet", 1, "load_on_startup"},
{ "getnodeaddresses", 0, "count"},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 50987a735b..fd780ba782 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -150,7 +150,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -358,7 +358,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -369,7 +369,7 @@ static RPCHelpMan generateblock()
// Add transactions
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
- RegenerateCommitments(block);
+ RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
{
LOCK(cs_main);
@@ -505,83 +505,84 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
static RPCHelpMan getblocktemplate()
{
return RPCHelpMan{"getblocktemplate",
- "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
- "It returns data needed to construct a block to work on.\n"
- "For full specification, see BIPs 22, 23, 9, and 145:\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
+ "It returns data needed to construct a block to work on.\n"
+ "For full specification, see BIPs 22, 23, 9, and 145:\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ {
+ {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template",
+ {
+ {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
+ {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
{
- {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template",
- {
- {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
- {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
- {
- {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
- },
- },
- {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
- {
- {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
- {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
- },
- },
- },
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ }},
+ {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
+ {
+ {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
+ }},
+ },
"\"template_request\""},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
+ },
+ {
+ RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""},
+ RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"},
+ RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "version", "The preferred block version"},
+ {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
+ {
+ {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
+ }},
+ {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
+ {
+ {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
+ }},
+ {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
+ {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
+ {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
+ {
+ {RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM, "version", "The preferred block version"},
- {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
- {
- {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
- }},
- {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
- {
- {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
- }},
- {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
- {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
- {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
- {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
- {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
- {RPCResult::Type::ARR, "depends", "array of numbers",
- {
- {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
- }},
- {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"},
- {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"},
- {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
- }},
- }},
- {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
+ {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
+ {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
+ {RPCResult::Type::ARR, "depends", "array of numbers",
{
- {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
+ {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
}},
- {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
- {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
- {RPCResult::Type::STR, "target", "The hash target"},
- {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
- {
- {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
- }},
- {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
- {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
- {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
- {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
- {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::STR, "bits", "compressed target of next block"},
- {RPCResult::Type::NUM, "height", "The height of the next block"},
- {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}
+ {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"},
+ {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"},
+ {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
}},
- RPCExamples{
+ }},
+ {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {
+ {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
+ }},
+ {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
+ {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
+ {RPCResult::Type::STR, "target", "The hash target"},
+ {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
+ {
+ {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
+ }},
+ {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
+ {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
+ {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
+ {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
+ {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::STR, "bits", "compressed target of next block"},
+ {RPCResult::Type::NUM, "height", "The height of the next block"},
+ {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
+ }},
+ },
+ RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
@@ -659,7 +660,7 @@ static RPCHelpMan getblocktemplate()
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (!Params().IsTestChain()) {
- if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
+ if (node.connman->GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
}
@@ -747,7 +748,7 @@ static RPCHelpMan getblocktemplate()
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
@@ -940,14 +941,17 @@ static RPCHelpMan submitblock()
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
return RPCHelpMan{"submitblock",
- "\nAttempts to submit new block to network.\n"
- "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
- {
- {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
- {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
- },
- RPCResult{RPCResult::Type::NONE, "", "Returns JSON Null when valid, a string according to BIP22 otherwise"},
- RPCExamples{
+ "\nAttempts to submit new block to network.\n"
+ "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
+ {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
+ },
+ {
+ RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""},
+ RPCResult{"Otherwise", RPCResult::Type::STR, "", "According to BIP22"},
+ },
+ RPCExamples{
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index b75a7b8d26..143be1274e 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -305,11 +305,11 @@ static RPCHelpMan verifymessage()
switch (MessageVerify(strAddress, strSign, strMessage)) {
case MessageVerificationResult::ERR_INVALID_ADDRESS:
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
+ throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding");
case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
case MessageVerificationResult::ERR_NOT_SIGNED:
return false;
@@ -365,13 +365,13 @@ static RPCHelpMan signmessagewithprivkey()
static RPCHelpMan setmocktime()
{
return RPCHelpMan{"setmocktime",
- "\nSet the local time to given timestamp (-regtest only)\n",
- {
- {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
- " Pass 0 to go back to using the system time."},
- },
- RPCResult{RPCResult::Type::NONE, "", ""},
- RPCExamples{""},
+ "\nSet the local time to given timestamp (-regtest only)\n",
+ {
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
+ "Pass 0 to go back to using the system time."},
+ },
+ RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
if (!Params().IsMockableChain()) {
@@ -386,7 +386,10 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
- int64_t time = request.params[0].get_int64();
+ const int64_t time{request.params[0].get_int64()};
+ if (time < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime can not be negative: %s.", time));
+ }
SetMockTime(time);
if (request.context.Has<NodeContext>()) {
for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 47d77b341a..d4c1ab4b53 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -57,7 +57,7 @@ static RPCHelpMan getconnectioncount()
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+ return (int)node.connman->GetNodeCount(ConnectionDirection::Both);
},
};
}
@@ -77,13 +77,12 @@ static RPCHelpMan ping()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
+ if (!node.peerman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
// Request that each node send a ping during next message processing pass
- node.connman->ForEachNode([](CNode* pnode) {
- pnode->fPingQueued = true;
- });
+ node.peerman->SendPings();
return NullUniValue;
},
};
@@ -104,7 +103,7 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
{RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
{RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
- {RPCResult::Type::STR, "network", "Network (ipv4, ipv6, or onion) the peer connected through"},
+ {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
{RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
@@ -203,14 +202,14 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
obj.pushKV("timeoffset", stats.nTimeOffset);
- if (stats.m_ping_usec > 0) {
- obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6);
+ if (stats.m_last_ping_time > 0us) {
+ obj.pushKV("pingtime", CountSecondsDouble(stats.m_last_ping_time));
}
- if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
- obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
+ if (stats.m_min_ping_time < std::chrono::microseconds::max()) {
+ obj.pushKV("minping", CountSecondsDouble(stats.m_min_ping_time));
}
- if (stats.m_ping_wait_usec > 0) {
- obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6);
+ if (fStateStats && statestats.m_ping_wait > 0s) {
+ obj.pushKV("pingwait", CountSecondsDouble(statestats.m_ping_wait));
}
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
@@ -547,7 +546,7 @@ static UniValue GetNetworksInfo()
UniValue networks(UniValue::VARR);
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast<enum Network>(n);
- if (network == NET_UNROUTABLE || network == NET_I2P || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
@@ -587,7 +586,7 @@ static RPCHelpMan getnetworkinfo()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"},
+ {RPCResult::Type::STR, "name", "network (" + Join(GetNetworkNames(), ", ") + ")"},
{RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
{RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
{RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
@@ -631,9 +630,9 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
- obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
- obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
- obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(ConnectionDirection::Both));
+ obj.pushKV("connections_in", (int)node.connman->GetNodeCount(ConnectionDirection::In));
+ obj.pushKV("connections_out", (int)node.connman->GetNodeCount(ConnectionDirection::Out));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@@ -915,7 +914,7 @@ static RPCHelpMan addpeeraddress()
UniValue obj(UniValue::VOBJ);
std::string addr_string = request.params[0].get_str();
- uint16_t port = request.params[1].get_int();
+ uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
CNetAddr net_addr;
if (!LookupHost(addr_string, net_addr, false)) {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ac42404470..2f92a321f8 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -946,44 +946,35 @@ static RPCHelpMan testmempoolaccept()
result_0.pushKV("txid", tx->GetHash().GetHex());
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
- TxValidationState state;
- bool test_accept_res;
- CAmount fee{0};
- {
- LOCK(cs_main);
- test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
- }
-
- // Check that fee does not exceed maximum fee
- if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
- result_0.pushKV("allowed", false);
- result_0.pushKV("reject-reason", "max-fee-exceeded");
- result.push_back(std::move(result_0));
- return result;
- }
- result_0.pushKV("allowed", test_accept_res);
+ const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(::ChainstateActive(), mempool, std::move(tx),
+ false /* bypass_limits */, /* test_accept */ true));
// Only return the fee and vsize if the transaction would pass ATMP.
// These can be used to calculate the feerate.
- if (test_accept_res) {
- result_0.pushKV("vsize", virtual_size);
- UniValue fees(UniValue::VOBJ);
- fees.pushKV("base", ValueFromAmount(fee));
- result_0.pushKV("fees", fees);
+ if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ const CAmount fee = accept_result.m_base_fees.value();
+ // Check that fee does not exceed maximum fee
+ if (max_raw_tx_fee && fee > max_raw_tx_fee) {
+ result_0.pushKV("allowed", false);
+ result_0.pushKV("reject-reason", "max-fee-exceeded");
+ } else {
+ result_0.pushKV("allowed", true);
+ result_0.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_0.pushKV("fees", fees);
+ }
+ result.push_back(std::move(result_0));
} else {
- if (state.IsInvalid()) {
- if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
- result_0.pushKV("reject-reason", "missing-inputs");
- } else {
- result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
- }
+ result_0.pushKV("allowed", false);
+ const TxValidationState state = accept_result.m_state;
+ if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
+ result_0.pushKV("reject-reason", "missing-inputs");
} else {
result_0.pushKV("reject-reason", state.GetRejectReason());
}
+ result.push_back(std::move(result_0));
}
-
- result.push_back(std::move(result_0));
return result;
},
};
@@ -1837,13 +1828,13 @@ static RPCHelpMan analyzepsbt()
}
if (!inputs_result.empty()) result.pushKV("inputs", inputs_result);
- if (psbta.estimated_vsize != nullopt) {
+ if (psbta.estimated_vsize != std::nullopt) {
result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
}
- if (psbta.estimated_feerate != nullopt) {
+ if (psbta.estimated_feerate != std::nullopt) {
result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
}
- if (psbta.fee != nullopt) {
+ if (psbta.fee != std::nullopt) {
result.pushKV("fee", ValueFromAmount(*psbta.fee));
}
result.pushKV("next", PSBTRoleName(psbta.next));
diff --git a/src/rpc/request.h b/src/rpc/request.h
index de3a4ae840..27d06f3c92 100644
--- a/src/rpc/request.h
+++ b/src/rpc/request.h
@@ -34,19 +34,19 @@ public:
UniValue id;
std::string strMethod;
UniValue params;
- bool fHelp;
+ enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;
std::string URI;
std::string authUser;
std::string peerAddr;
const util::Ref& context;
- explicit JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {}
+ explicit JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), context(context) {}
//! Initializes request information from another request object and the
//! given context. The implementation should be updated if any members are
//! added or removed above.
JSONRPCRequest(const JSONRPCRequest& other, const util::Ref& context)
- : id(other.id), strMethod(other.strMethod), params(other.params), fHelp(other.fHelp), URI(other.URI),
+ : id(other.id), strMethod(other.strMethod), params(other.params), mode(other.mode), URI(other.URI),
authUser(other.authUser), peerAddr(other.peerAddr), context(context)
{
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 9a9b3713f3..39938f4eb9 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -88,7 +88,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
sort(vCommands.begin(), vCommands.end());
JSONRPCRequest jreq(helpreq);
- jreq.fHelp = true;
+ jreq.mode = JSONRPCRequest::GET_HELP;
jreq.params = UniValue();
for (const std::pair<std::string, const CRPCCommand*>& command : vCommands)
@@ -149,7 +149,7 @@ static RPCHelpMan help()
}
if (strCommand == "dump_all_command_conversions") {
// Used for testing only, undocumented
- return tableRPC.dumpArgMap();
+ return tableRPC.dumpArgMap(jsonRequest);
}
return tableRPC.help(strCommand, jsonRequest);
@@ -437,6 +437,16 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
return out;
}
+static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result)
+{
+ for (const auto& command : commands) {
+ if (ExecuteCommand(*command, request, result, &command == &commands.back())) {
+ return true;
+ }
+ }
+ return false;
+}
+
UniValue CRPCTable::execute(const JSONRPCRequest &request) const
{
// Return immediately if in warmup
@@ -450,10 +460,8 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
auto it = mapCommands.find(request.strMethod);
if (it != mapCommands.end()) {
UniValue result;
- for (const auto& command : it->second) {
- if (ExecuteCommand(*command, request, result, &command == &it->second.back())) {
- return result;
- }
+ if (ExecuteCommands(it->second, request, result)) {
+ return result;
}
}
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
@@ -484,13 +492,18 @@ std::vector<std::string> CRPCTable::listCommands() const
return commandList;
}
-UniValue CRPCTable::dumpArgMap() const
+UniValue CRPCTable::dumpArgMap(const JSONRPCRequest& args_request) const
{
+ JSONRPCRequest request(args_request);
+ request.mode = JSONRPCRequest::GET_ARGS;
+
UniValue ret{UniValue::VARR};
for (const auto& cmd : mapCommands) {
- for (const auto& c : cmd.second) {
- const auto help = RpcMethodFnType(c->unique_id)();
- help.AppendArgMap(ret);
+ UniValue result;
+ if (ExecuteCommands(cmd.second, request, result)) {
+ for (const auto& values : result.getValues()) {
+ ret.push_back(values);
+ }
}
}
return ret;
diff --git a/src/rpc/server.h b/src/rpc/server.h
index fe5a791e1e..03967020c2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -148,7 +148,7 @@ public:
/**
* Return all named arguments that need to be converted by the client from string to another JSON type
*/
- UniValue dumpArgMap() const;
+ UniValue dumpArgMap(const JSONRPCRequest& request) const;
/**
* Appends a CRPCCommand to the dispatch table.
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index bfdba5253c..44e58cb75f 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -113,23 +113,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type)
-{
- if (param.isNull()) {
- return default_type;
- } else {
- std::string hash_type_input = param.get_str();
-
- if (hash_type_input == "hash_serialized_2") {
- return CoinStatsHashType::HASH_SERIALIZED;
- } else if (hash_type_input == "none") {
- return CoinStatsHashType::NONE;
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%d is not a valid hash_type", hash_type_input));
- }
- }
-}
-
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
@@ -476,6 +459,21 @@ std::string RPCExamples::ToDescriptionString() const
return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
}
+UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request)
+{
+ if (request.mode == JSONRPCRequest::GET_ARGS) {
+ return GetArgMap();
+ }
+ /*
+ * Check if the given request is valid according to this command or if
+ * the user is asking for help information, and throw help when appropriate.
+ */
+ if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(ToString());
+ }
+ return m_fun(*this, request);
+}
+
bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
{
size_t num_required_args = 0;
@@ -549,8 +547,9 @@ std::string RPCHelpMan::ToString() const
return ret;
}
-void RPCHelpMan::AppendArgMap(UniValue& arr) const
+UniValue RPCHelpMan::GetArgMap() const
{
+ UniValue arr{UniValue::VARR};
for (int i{0}; i < int(m_args.size()); ++i) {
const auto& arg = m_args.at(i);
std::vector<std::string> arg_names;
@@ -565,6 +564,7 @@ void RPCHelpMan::AppendArgMap(UniValue& arr) const
arr.push_back(map);
}
}
+ return arr;
}
std::string RPCArg::GetFirstName() const
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 444a013ca1..94c2d2d626 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -77,8 +77,6 @@ extern uint256 ParseHashO(const UniValue& o, std::string strKey);
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type);
-
extern CAmount AmountFromValue(const UniValue& value);
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
@@ -335,26 +333,12 @@ public:
using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
+ UniValue HandleRequest(const JSONRPCRequest& request);
std::string ToString() const;
- /** Append the named args that need to be converted from string to another JSON type */
- void AppendArgMap(UniValue& arr) const;
- UniValue HandleRequest(const JSONRPCRequest& request)
- {
- Check(request);
- return m_fun(*this, request);
- }
+ /** Return the named args that need to be converted from string to another JSON type */
+ UniValue GetArgMap() const;
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
- /**
- * Check if the given request is valid according to this command or if
- * the user is asking for help information, and throw help when appropriate.
- */
- inline void Check(const JSONRPCRequest& request) const {
- if (request.fHelp || !IsValidNumArgs(request.params.size())) {
- throw std::runtime_error(ToString());
- }
- }
-
std::vector<std::string> GetArgNames() const;
const std::string m_name;
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index c5dceac848..b6939127e1 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -11,14 +11,12 @@
#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#if defined(_WIN32)
- #if defined(DLL_EXPORT)
- #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT)
- #define EXPORT_SYMBOL __declspec(dllexport)
- #else
- #define EXPORT_SYMBOL
- #endif
+ #if defined(HAVE_DLLEXPORT_ATTRIBUTE)
+ #define EXPORT_SYMBOL __declspec(dllexport)
+ #else
+ #define EXPORT_SYMBOL
#endif
- #elif defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY)
+ #elif defined(HAVE_DEFAULT_VISIBILITY_ATTRIBUTE)
#define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
#endif
#elif defined(MSC_VER) && !defined(STATIC_LIBBITCOINCONSENSUS)
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 9e4b8a9dd6..30399dca51 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -17,6 +17,7 @@
#include <util/vector.h>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -179,6 +180,9 @@ public:
/** Get the descriptor string form including private data (if available in arg). */
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
+ /** Get the descriptor string form with the xpub at the last hardened derivation */
+ virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const = 0;
+
/** Derive a private key, if private data is available in arg. */
virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
};
@@ -212,6 +216,21 @@ public:
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
+ bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
+ {
+ std::string sub;
+ if (!m_provider->ToNormalizedString(arg, sub, priv)) return false;
+ // If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider
+ // In that case, we need to strip out the leading square bracket and fingerprint from the substring,
+ // and append that to our own origin string.
+ if (sub[0] == '[') {
+ sub = sub.substr(9);
+ ret = "[" + OriginString() + std::move(sub);
+ } else {
+ ret = "[" + OriginString() + "]" + std::move(sub);
+ }
+ return true;
+ }
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
{
return m_provider->GetPrivKey(pos, arg, key);
@@ -243,6 +262,12 @@ public:
ret = EncodeSecret(key);
return true;
}
+ bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override
+ {
+ if (priv) return ToPrivateString(arg, ret);
+ ret = ToString();
+ return true;
+ }
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
{
return arg.GetKey(m_pubkey.GetID(), key);
@@ -386,6 +411,56 @@ public:
}
return true;
}
+ bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override
+ {
+ // For hardened derivation type, just return the typical string, nothing to normalize
+ if (m_derive == DeriveType::HARDENED) {
+ if (priv) return ToPrivateString(arg, out);
+ out = ToString();
+ return true;
+ }
+ // Step backwards to find the last hardened step in the path
+ int i = (int)m_path.size() - 1;
+ for (; i >= 0; --i) {
+ if (m_path.at(i) >> 31) {
+ break;
+ }
+ }
+ // Either no derivation or all unhardened derivation
+ if (i == -1) {
+ if (priv) return ToPrivateString(arg, out);
+ out = ToString();
+ return true;
+ }
+ // Derive the xpub at the last hardened step
+ CExtKey xprv;
+ if (!GetExtKey(arg, xprv)) return false;
+ KeyOriginInfo origin;
+ int k = 0;
+ for (; k <= i; ++k) {
+ // Derive
+ xprv.Derive(xprv, m_path.at(k));
+ // Add to the path
+ origin.path.push_back(m_path.at(k));
+ // First derivation element, get the fingerprint for origin
+ if (k == 0) {
+ std::copy(xprv.vchFingerprint, xprv.vchFingerprint + 4, origin.fingerprint);
+ }
+ }
+ // Build the remaining path
+ KeyPath end_path;
+ for (; k < (int)m_path.size(); ++k) {
+ end_path.push_back(m_path.at(k));
+ }
+ // Build the string
+ std::string origin_str = HexStr(origin.fingerprint) + FormatHDKeypath(origin.path);
+ out = "[" + origin_str + "]" + (priv ? EncodeExtKey(xprv) : EncodeExtPubKey(xprv.Neuter())) + FormatHDKeypath(end_path);
+ if (IsRange()) {
+ out += "/*";
+ assert(m_derive == DeriveType::UNHARDENED);
+ }
+ return true;
+ }
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
{
CExtKey extkey;
@@ -449,7 +524,7 @@ public:
return false;
}
- bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv) const
+ bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const
{
std::string extra = ToStringExtra();
size_t pos = extra.size() > 0 ? 1 : 0;
@@ -457,7 +532,9 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pos++) ret += ",";
std::string tmp;
- if (priv) {
+ if (normalized) {
+ if (!pubkey->ToNormalizedString(*arg, tmp, priv)) return false;
+ } else if (priv) {
if (!pubkey->ToPrivateString(*arg, tmp)) return false;
} else {
tmp = pubkey->ToString();
@@ -467,7 +544,7 @@ public:
if (m_subdescriptor_arg) {
if (pos++) ret += ",";
std::string tmp;
- if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv)) return false;
+ if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv, normalized)) return false;
ret += std::move(tmp);
}
out = std::move(ret) + ")";
@@ -477,13 +554,20 @@ public:
std::string ToString() const final
{
std::string ret;
- ToStringHelper(nullptr, ret, false);
+ ToStringHelper(nullptr, ret, false, false);
return AddChecksum(ret);
}
bool ToPrivateString(const SigningProvider& arg, std::string& out) const final
{
- bool ret = ToStringHelper(&arg, out, true);
+ bool ret = ToStringHelper(&arg, out, true, false);
+ out = AddChecksum(out);
+ return ret;
+ }
+
+ bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override final
+ {
+ bool ret = ToStringHelper(&arg, out, priv, true);
out = AddChecksum(out);
return ret;
}
@@ -549,7 +633,7 @@ public:
}
}
- Optional<OutputType> GetOutputType() const override { return nullopt; }
+ std::optional<OutputType> GetOutputType() const override { return std::nullopt; }
};
/** A parsed addr(A) descriptor. */
@@ -563,7 +647,7 @@ public:
AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
- Optional<OutputType> GetOutputType() const override
+ std::optional<OutputType> GetOutputType() const override
{
switch (m_destination.index()) {
case 1 /* PKHash */:
@@ -572,7 +656,7 @@ public:
case 4 /* WitnessV0KeyHash */:
case 5 /* WitnessUnknown */: return OutputType::BECH32;
case 0 /* CNoDestination */:
- default: return nullopt;
+ default: return std::nullopt;
}
}
bool IsSingleType() const final { return true; }
@@ -589,7 +673,7 @@ public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
- Optional<OutputType> GetOutputType() const override
+ std::optional<OutputType> GetOutputType() const override
{
CTxDestination dest;
ExtractDestination(m_script, dest);
@@ -600,7 +684,7 @@ public:
case 4 /* WitnessV0KeyHash */:
case 5 /* WitnessUnknown */: return OutputType::BECH32;
case 0 /* CNoDestination */:
- default: return nullopt;
+ default: return std::nullopt;
}
}
bool IsSingleType() const final { return true; }
@@ -628,7 +712,7 @@ protected:
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {}
- Optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
bool IsSingleType() const final { return true; }
};
@@ -644,7 +728,7 @@ protected:
}
public:
WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {}
- Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
bool IsSingleType() const final { return true; }
};
@@ -700,7 +784,7 @@ protected:
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
- Optional<OutputType> GetOutputType() const override
+ std::optional<OutputType> GetOutputType() const override
{
assert(m_subdescriptor_arg);
if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
@@ -716,7 +800,7 @@ protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
public:
WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
- Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
bool IsSingleType() const final { return true; }
};
@@ -770,7 +854,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
CPubKey pubkey(data);
if (pubkey.IsFullyValid()) {
if (permit_uncompressed || pubkey.IsCompressed()) {
- return MakeUnique<ConstPubkeyProvider>(key_exp_index, pubkey);
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey);
} else {
error = "Uncompressed keys are not allowed";
return nullptr;
@@ -784,7 +868,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
if (permit_uncompressed || key.IsCompressed()) {
CPubKey pubkey = key.GetPubKey();
out.keys.emplace(pubkey.GetID(), key);
- return MakeUnique<ConstPubkeyProvider>(key_exp_index, pubkey);
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey);
} else {
error = "Uncompressed keys are not allowed";
return nullptr;
@@ -811,7 +895,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
}
- return MakeUnique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type);
+ return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type);
}
/** Parse a public key including origin information (if enabled). */
@@ -848,7 +932,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c
if (!ParseKeyPath(slash_split, info.path, error)) return nullptr;
auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], permit_uncompressed, out, error);
if (!provider) return nullptr;
- return MakeUnique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider));
+ return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider));
}
/** Parse a script in a particular context. */
@@ -861,17 +945,17 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
- return MakeUnique<PKDescriptor>(std::move(pubkey));
+ return std::make_unique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
- return MakeUnique<PKHDescriptor>(std::move(pubkey));
+ return std::make_unique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, true, out, error);
if (!pubkey) return nullptr;
- return MakeUnique<ComboDescriptor>(std::move(pubkey));
+ return std::make_unique<ComboDescriptor>(std::move(pubkey));
} else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) {
error = "Cannot have combo in non-top level";
return nullptr;
@@ -919,12 +1003,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
return nullptr;
}
}
- return MakeUnique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
+ return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
}
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, false, out, error);
if (!pubkey) return nullptr;
- return MakeUnique<WPKHDescriptor>(std::move(pubkey));
+ return std::make_unique<WPKHDescriptor>(std::move(pubkey));
} else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) {
error = "Cannot have wpkh within wsh";
return nullptr;
@@ -932,7 +1016,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<SHDescriptor>(std::move(desc));
+ return std::make_unique<SHDescriptor>(std::move(desc));
} else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) {
error = "Cannot have sh in non-top level";
return nullptr;
@@ -940,7 +1024,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error);
if (!desc || expr.size()) return nullptr;
- return MakeUnique<WSHDescriptor>(std::move(desc));
+ return std::make_unique<WSHDescriptor>(std::move(desc));
} else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) {
error = "Cannot have wsh within wsh";
return nullptr;
@@ -951,7 +1035,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
error = "Address is not valid";
return nullptr;
}
- return MakeUnique<AddressDescriptor>(std::move(dest));
+ return std::make_unique<AddressDescriptor>(std::move(dest));
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
@@ -960,7 +1044,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
return nullptr;
}
auto bytes = ParseHex(str);
- return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
+ return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
}
if (ctx == ParseScriptContext::P2SH) {
error = "A function is needed within P2SH";
@@ -975,10 +1059,10 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
{
- std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(0, pubkey);
+ std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey);
KeyOriginInfo info;
if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
- return MakeUnique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
+ return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
}
return key_provider;
}
@@ -991,7 +1075,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (txntype == TxoutType::PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
- return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
+ return std::make_unique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TxoutType::PUBKEYHASH) {
@@ -999,7 +1083,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
- return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ return std::make_unique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TxoutType::WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
@@ -1007,7 +1091,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
- return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
+ return std::make_unique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TxoutType::MULTISIG) {
@@ -1016,7 +1100,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CPubKey pubkey(data[i].begin(), data[i].end());
providers.push_back(InferPubkey(pubkey, ctx, provider));
}
- return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
+ return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers));
}
if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) {
uint160 hash(data[0]);
@@ -1024,7 +1108,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
- if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
+ if (sub) return std::make_unique<SHDescriptor>(std::move(sub));
}
}
if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
@@ -1033,18 +1117,18 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
- if (sub) return MakeUnique<WSHDescriptor>(std::move(sub));
+ if (sub) return std::make_unique<WSHDescriptor>(std::move(sub));
}
}
CTxDestination dest;
if (ExtractDestination(script, dest)) {
if (GetScriptForDestination(dest) == script) {
- return MakeUnique<AddressDescriptor>(std::move(dest));
+ return std::make_unique<AddressDescriptor>(std::move(dest));
}
}
- return MakeUnique<RawDescriptor>(script);
+ return std::make_unique<RawDescriptor>(script);
}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 17b43e7c81..332ae2f230 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -5,12 +5,12 @@
#ifndef BITCOIN_SCRIPT_DESCRIPTOR_H
#define BITCOIN_SCRIPT_DESCRIPTOR_H
-#include <optional.h>
#include <outputtype.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
+#include <optional>
#include <vector>
using ExtPubKeyMap = std::unordered_map<uint32_t, CExtPubKey>;
@@ -93,6 +93,9 @@ struct Descriptor {
/** Convert the descriptor to a private string. This fails if the provided provider does not have the relevant private keys. */
virtual bool ToPrivateString(const SigningProvider& provider, std::string& out) const = 0;
+ /** Convert the descriptor to a normalized string. Normalized descriptors have the xpub at the last hardened step. This fails if the provided provider does not have the private keys to derive that xpub. */
+ virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, bool priv) const = 0;
+
/** Expand a descriptor at a specified position.
*
* @param[in] pos The position at which to expand the descriptor. If IsRange() is false, this is ignored.
@@ -121,7 +124,7 @@ struct Descriptor {
virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
/** @return The OutputType of the scriptPubKey(s) produced by this descriptor. Or nullopt if indeterminate (multiple or none) */
- virtual Optional<OutputType> GetOutputType() const = 0;
+ virtual std::optional<OutputType> GetOutputType() const = 0;
};
/** Parse a `descriptor` string. Included private keys are put in `out`.
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index ecac3b9e7e..20a4ce48b0 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1834,7 +1834,7 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
{
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
- //! The inner pubkey (x-only, so no Y coordinate parity).
+ //! The internal pubkey (x-only, so no Y coordinate parity).
const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
//! The output pubkey (taken from the scriptPubKey).
const XOnlyPubKey q{uint256(program)};
@@ -1852,9 +1852,9 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
- // Compute the tweak from the Merkle root and the inner pubkey.
+ // Compute the tweak from the Merkle root and the internal pubkey.
k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
- // Verify that the output pubkey matches the tweaked inner pubkey, after correcting for parity.
+ // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
return q.CheckPayToContract(p, k, control[0] & 1);
}
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index cf47d37e70..c6d898a25a 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -12,8 +12,10 @@
#include <cuckoocache.h>
-#include <boost/thread/lock_types.hpp>
-#include <boost/thread/shared_mutex.hpp>
+#include <algorithm>
+#include <mutex>
+#include <shared_mutex>
+#include <vector>
namespace {
/**
@@ -29,7 +31,7 @@ private:
CSHA256 m_salted_hasher_schnorr;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
- boost::shared_mutex cs_sigcache;
+ std::shared_mutex cs_sigcache;
public:
CSignatureCache()
@@ -64,13 +66,13 @@ public:
bool
Get(const uint256& entry, const bool erase)
{
- boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
+ std::shared_lock<std::shared_mutex> lock(cs_sigcache);
return setValid.contains(entry, erase);
}
void Set(const uint256& entry)
{
- boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
+ std::unique_lock<std::shared_mutex> lock(cs_sigcache);
setValid.insert(entry);
}
uint32_t setup_bytes(size_t n)
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 8afbe9ebed..dba5ce621a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -106,8 +106,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
std::vector<valtype> vSolutions;
whichTypeRet = Solver(scriptPubKey, vSolutions);
- switch (whichTypeRet)
- {
+ switch (whichTypeRet) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
@@ -173,10 +172,8 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
// Could not find witnessScript, add to missing
sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
-
- default:
- return false;
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
static CScript PushAll(const std::vector<valtype>& values)
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 7967c01858..4d882cd1f1 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -45,8 +45,7 @@ WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
std::string GetTxnOutputType(TxoutType t)
{
- switch (t)
- {
+ switch (t) {
case TxoutType::NONSTANDARD: return "nonstandard";
case TxoutType::PUBKEY: return "pubkey";
case TxoutType::PUBKEYHASH: return "pubkeyhash";
@@ -182,7 +181,8 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
std::vector<valtype> vSolutions;
TxoutType whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TxoutType::PUBKEY) {
+ switch (whichType) {
+ case TxoutType::PUBKEY: {
CPubKey pubKey(vSolutions[0]);
if (!pubKey.IsValid())
return false;
@@ -190,26 +190,28 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = PKHash(pubKey);
return true;
}
- else if (whichType == TxoutType::PUBKEYHASH)
- {
+ case TxoutType::PUBKEYHASH: {
addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
- else if (whichType == TxoutType::SCRIPTHASH)
- {
+ case TxoutType::SCRIPTHASH: {
addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
- } else if (whichType == TxoutType::WITNESS_V0_KEYHASH) {
+ }
+ case TxoutType::WITNESS_V0_KEYHASH: {
WitnessV0KeyHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
+ }
+ case TxoutType::WITNESS_V0_SCRIPTHASH: {
WitnessV0ScriptHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TxoutType::WITNESS_UNKNOWN || whichType == TxoutType::WITNESS_V1_TAPROOT) {
+ }
+ case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT: {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -217,8 +219,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = unk;
return true;
}
- // Multisig txns have more than one address...
- return false;
+ case TxoutType::MULTISIG:
+ // Multisig txns have more than one address...
+ case TxoutType::NULL_DATA:
+ case TxoutType::NONSTANDARD:
+ return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
diff --git a/src/shutdown.cpp b/src/shutdown.cpp
index df5f996022..2fc195e2d1 100644
--- a/src/shutdown.cpp
+++ b/src/shutdown.cpp
@@ -5,16 +5,15 @@
#include <shutdown.h>
+#include <logging.h>
+#include <util/tokenpipe.h>
+
#include <config/bitcoin-config.h>
#include <assert.h>
#include <atomic>
#ifdef WIN32
#include <condition_variable>
-#else
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
#endif
static std::atomic<bool> fRequestShutdown(false);
@@ -24,25 +23,18 @@ std::mutex g_shutdown_mutex;
std::condition_variable g_shutdown_cv;
#else
/** On UNIX-like operating systems use the self-pipe trick.
- * Index 0 will be the read end of the pipe, index 1 the write end.
*/
-static int g_shutdown_pipe[2] = {-1, -1};
+static TokenPipeEnd g_shutdown_r;
+static TokenPipeEnd g_shutdown_w;
#endif
bool InitShutdownState()
{
#ifndef WIN32
-#if HAVE_O_CLOEXEC
- // If we can, make sure that the file descriptors are closed on exec()
- // to prevent interference.
- if (pipe2(g_shutdown_pipe, O_CLOEXEC) != 0) {
- return false;
- }
-#else
- if (pipe(g_shutdown_pipe) != 0) {
- return false;
- }
-#endif
+ std::optional<TokenPipe> pipe = TokenPipe::Make();
+ if (!pipe) return false;
+ g_shutdown_r = pipe->TakeReadEnd();
+ g_shutdown_w = pipe->TakeWriteEnd();
#endif
return true;
}
@@ -59,17 +51,10 @@ void StartShutdown()
// case of a reentrant signal.
if (!fRequestShutdown.exchange(true)) {
// Write an arbitrary byte to the write end of the shutdown pipe.
- const char token = 'x';
- while (true) {
- int result = write(g_shutdown_pipe[1], &token, 1);
- if (result < 0) {
- // Failure. It's possible that the write was interrupted by another signal.
- // Other errors are unexpected here.
- assert(errno == EINTR);
- } else {
- assert(result == 1);
- break;
- }
+ int res = g_shutdown_w.TokenWrite('x');
+ if (res != 0) {
+ LogPrintf("Sending shutdown token failed\n");
+ assert(0);
}
}
#endif
@@ -96,17 +81,10 @@ void WaitForShutdown()
std::unique_lock<std::mutex> lk(g_shutdown_mutex);
g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); });
#else
- char token;
- while (true) {
- int result = read(g_shutdown_pipe[0], &token, 1);
- if (result < 0) {
- // Failure. Check if the read was interrupted by a signal.
- // Other errors are unexpected here.
- assert(errno == EINTR);
- } else {
- assert(result == 1);
- break;
- }
+ int res = g_shutdown_r.TokenRead();
+ if (res != 'x') {
+ LogPrintf("Reading shutdown token failed\n");
+ assert(0);
}
#endif
}
diff --git a/src/signet.cpp b/src/signet.cpp
index e68f031aa4..fb41e43752 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -65,7 +65,7 @@ static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CB
return ComputeMerkleRoot(std::move(leaves));
}
-Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge)
+std::optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge)
{
CMutableTransaction tx_to_spend;
tx_to_spend.nVersion = 0;
@@ -83,12 +83,12 @@ Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challe
// responses from block coinbase tx
// find and delete signet signature
- if (block.vtx.empty()) return nullopt; // no coinbase tx in block; invalid
+ if (block.vtx.empty()) return std::nullopt; // no coinbase tx in block; invalid
CMutableTransaction modified_cb(*block.vtx.at(0));
const int cidx = GetWitnessCommitmentIndex(block);
if (cidx == NO_WITNESS_COMMITMENT) {
- return nullopt; // require a witness commitment
+ return std::nullopt; // require a witness commitment
}
CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey;
@@ -101,9 +101,9 @@ Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challe
VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0);
v >> tx_spending.vin[0].scriptSig;
v >> tx_spending.vin[0].scriptWitness.stack;
- if (!v.empty()) return nullopt; // extraneous data encountered
+ if (!v.empty()) return std::nullopt; // extraneous data encountered
} catch (const std::exception&) {
- return nullopt; // parsing error
+ return std::nullopt; // parsing error
}
}
uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block);
@@ -129,7 +129,7 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
}
const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end());
- const Optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge);
+ const std::optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge);
if (!signet_txs) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n");
diff --git a/src/signet.h b/src/signet.h
index 23563a83c4..f876488c0a 100644
--- a/src/signet.h
+++ b/src/signet.h
@@ -9,7 +9,7 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <optional.h>
+#include <optional>
/**
* Extract signature and check whether a block has a valid solution
@@ -28,7 +28,7 @@ class SignetTxs {
SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign} { }
public:
- static Optional<SignetTxs> Create(const CBlock& block, const CScript& challenge);
+ static std::optional<SignetTxs> Create(const CBlock& block, const CScript& challenge);
const CTransaction m_to_spend;
const CTransaction m_to_sign;
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 37ff8a9afe..d438537606 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -100,7 +100,7 @@ static CNetAddr ResolveIP(const std::string& ip)
return addr;
}
-static CService ResolveService(const std::string& ip, const int port = 0)
+static CService ResolveService(const std::string& ip, uint16_t port = 0)
{
CService serv;
BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port));
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index d33d668a04..b523173a45 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util/memory.h>
#include <util/system.h>
#include <test/util/setup_common.h>
@@ -163,7 +162,7 @@ private:
BOOST_AUTO_TEST_CASE(lockedpool_tests_mock)
{
// Test over three virtual arenas, of which one will succeed being locked
- std::unique_ptr<LockedPageAllocator> x = MakeUnique<TestLockedPageAllocator>(3, 1);
+ std::unique_ptr<LockedPageAllocator> x = std::make_unique<TestLockedPageAllocator>(3, 1);
LockedPool pool(std::move(x));
BOOST_CHECK(pool.stats().total == 0);
BOOST_CHECK(pool.stats().locked == 0);
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 3f7c5e99ee..3b44564ddb 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -17,7 +17,7 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"};
static const std::string vstrOutNoPadding[] = {"","my","mzxq","mzxw6","mzxw6yq","mzxw6ytb","mzxw6ytboi"};
- for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
+ for (unsigned int i=0; i<std::size(vstrIn); i++)
{
std::string strEnc = EncodeBase32(vstrIn[i]);
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index bb8d102bd0..714fccffaa 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
{
static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
static const std::string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"};
- for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
+ for (unsigned int i=0; i<std::size(vstrIn); i++)
{
std::string strEnc = EncodeBase64(vstrIn[i]);
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index a2098f4f56..2651e46430 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -10,7 +10,7 @@
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
-BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
+BOOST_AUTO_TEST_CASE(bech32_testvectors_valid)
{
static const std::string CASES[] = {
"A12UEL5L",
@@ -22,15 +22,35 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
"?1ezyfcl",
};
for (const std::string& str : CASES) {
- auto ret = bech32::Decode(str);
- BOOST_CHECK(!ret.first.empty());
- std::string recode = bech32::Encode(ret.first, ret.second);
+ const auto dec = bech32::Decode(str);
+ BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32);
+ std::string recode = bech32::Encode(bech32::Encoding::BECH32, dec.hrp, dec.data);
BOOST_CHECK(!recode.empty());
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
}
}
-BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
+BOOST_AUTO_TEST_CASE(bech32m_testvectors_valid)
+{
+ static const std::string CASES[] = {
+ "A1LQFN3A",
+ "a1lqfn3a",
+ "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
+ "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
+ "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
+ "split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
+ "?1v759aa"
+ };
+ for (const std::string& str : CASES) {
+ const auto dec = bech32::Decode(str);
+ BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32M);
+ std::string recode = bech32::Encode(bech32::Encoding::BECH32M, dec.hrp, dec.data);
+ BOOST_CHECK(!recode.empty());
+ BOOST_CHECK(CaseInsensitiveEqual(str, recode));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
{
static const std::string CASES[] = {
" 1nwldj5",
@@ -49,8 +69,32 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
"A12uEL5L",
};
for (const std::string& str : CASES) {
- auto ret = bech32::Decode(str);
- BOOST_CHECK(ret.first.empty());
+ const auto dec = bech32::Decode(str);
+ BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(bech32m_testvectors_invalid)
+{
+ static const std::string CASES[] = {
+ " 1xj0phk",
+ "\x7f""1g6xzxy",
+ "\x80""1vctc34",
+ "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4",
+ "qyrz8wqd2c9m",
+ "1qyrz8wqd2c9m",
+ "y1b0jsk6g",
+ "lt1igcx5c0",
+ "in1muywd",
+ "mm1crxm3i",
+ "au1s5cgom",
+ "M1VUXWEZ",
+ "16plkw9",
+ "1p2gdwpf"
+ };
+ for (const std::string& str : CASES) {
+ const auto dec = bech32::Decode(str);
+ BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
}
}
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 633a95ce96..04da10715f 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 21921375b3..64c6d7f634 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -5,7 +5,6 @@
#include <checkqueue.h>
#include <sync.h>
#include <test/util/setup_common.h>
-#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
@@ -146,7 +145,7 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;
*/
static void Correct_Queue_range(std::vector<size_t> range)
{
- auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE);
+ auto small_queue = std::make_unique<Correct_Queue>(QUEUE_BATCH_SIZE);
small_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
// Make vChecks here to save on malloc (this test can be slow...)
std::vector<FakeCheckCheckCompletion> vChecks;
@@ -206,7 +205,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random)
/** Test that failing checks are caught */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
{
- auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
+ auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE);
fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (size_t i = 0; i < 1001; ++i) {
@@ -234,7 +233,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
// future blocks, ie, the bad state is cleared.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
{
- auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
+ auto fail_queue = std::make_unique<Failing_Queue>(QUEUE_BATCH_SIZE);
fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (auto times = 0; times < 10; ++times) {
@@ -258,7 +257,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
// more than once as well
BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
{
- auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE);
+ auto queue = std::make_unique<Unique_Queue>(QUEUE_BATCH_SIZE);
queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
size_t COUNT = 100000;
@@ -293,7 +292,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
// time could leave the data hanging across a sequence of blocks.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
{
- auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE);
+ auto queue = std::make_unique<Memory_Queue>(QUEUE_BATCH_SIZE);
queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
for (size_t i = 0; i < 1000; ++i) {
size_t total = i;
@@ -320,7 +319,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
{
- auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE);
+ auto queue = std::make_unique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE);
bool fails = false;
queue->StartWorkerThreads(SCRIPT_CHECK_THREADS);
std::thread t0([&]() {
@@ -360,7 +359,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
/** Test that CCheckQueueControl is threadsafe */
BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
{
- auto queue = MakeUnique<Standard_Queue>(QUEUE_BATCH_SIZE);
+ auto queue = std::make_unique<Standard_Queue>(QUEUE_BATCH_SIZE);
{
std::vector<std::thread> tg;
std::atomic<int> nThreads {0};
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index 75c7e47e64..35b66cfc53 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -1,15 +1,18 @@
// Copyright (c) 2012-2020 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 <boost/test/unit_test.hpp>
-#include <boost/thread/lock_types.hpp>
-#include <boost/thread/shared_mutex.hpp>
#include <cuckoocache.h>
-#include <deque>
#include <random.h>
#include <script/sigcache.h>
#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <deque>
+#include <mutex>
+#include <shared_mutex>
#include <thread>
+#include <vector>
/** Test Suite for CuckooCache
*
@@ -201,11 +204,11 @@ static void test_cache_erase_parallel(size_t megabytes)
* "future proofed".
*/
std::vector<uint256> hashes_insert_copy = hashes;
- boost::shared_mutex mtx;
+ std::shared_mutex mtx;
{
/** Grab lock to make sure we release inserts */
- boost::unique_lock<boost::shared_mutex> l(mtx);
+ std::unique_lock<std::shared_mutex> l(mtx);
/** Insert the first half */
for (uint32_t i = 0; i < (n_insert / 2); ++i)
set.insert(hashes_insert_copy[i]);
@@ -219,7 +222,7 @@ static void test_cache_erase_parallel(size_t megabytes)
/** Each thread is emplaced with x copy-by-value
*/
threads.emplace_back([&, x] {
- boost::shared_lock<boost::shared_mutex> l(mtx);
+ std::shared_lock<std::shared_mutex> l(mtx);
size_t ntodo = (n_insert/4)/3;
size_t start = ntodo*x;
size_t end = ntodo*(x+1);
@@ -234,7 +237,7 @@ static void test_cache_erase_parallel(size_t megabytes)
for (std::thread& t : threads)
t.join();
/** Grab lock to make sure we observe erases */
- boost::unique_lock<boost::shared_mutex> l(mtx);
+ std::unique_lock<std::shared_mutex> l(mtx);
/** Insert the second half */
for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
set.insert(hashes_insert_copy[i]);
diff --git a/src/test/data/key_io_invalid.json b/src/test/data/key_io_invalid.json
index 9b52943ac6..abe07dad24 100644
--- a/src/test/data/key_io_invalid.json
+++ b/src/test/data/key_io_invalid.json
@@ -6,177 +6,207 @@
"x"
],
[
- "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"
+ "2v7k5Bb8Lr1MMgTgW6HAf5YHXi6BzpPjHpQ4srD4RSwHYpzXKiXmLAgiLhkXvp3JF5v7nq45EWr"
],
[
- "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"
+ "RAZzCGtMbiUgMiiyrZySrSpdfnQReFXA3r"
],
[
- "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"
+ "NYamy7tcPQTzoU5iyQojD3sqhiz7zxkvn8"
],
[
- "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"
+ "geaFG555Ex5nyRf7JjW6Pj2GwZA8KYxtJJLbr1eZhVW75STbYBZeRszy3wg4pkKdF4ez9J4wQiz"
],
[
- "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"
+ "2Cxmid3c2XQ2zvQ8SA1ha2TKqvqbJS9XFmXRsCneBS3Po7Qqb65z5zNdsoF9AfieXFcpoVPmkmfa"
],
[
- "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"
+ "gaJ7UVge2njVg9tFTetJrtHgruMm7aQDiSAxfHrVEgzK8N2ooagDVmDkdph434xzc4K96Gjyxcs"
],
[
- "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"
+ "5JN5BEVQPZ3tAiatz1RGXkrJuE3EC6bervMaPb38wTNgEuZCeqp"
],
[
- "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"
+ "3TnFbyUtBRS5rE1KTW81qLVspjJNaB3uu6uuvLjxhZo2DB6PCGh"
],
[
- "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"
+ "7UgSZGaMaTc4d2mdEgcGBFiMeS6eMsithGUqvBsKTQdGzD7XQDbMEYo3gojdbXEPbUdFF3CQoK72f"
],
[
- "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"
+ "9261wfqQqruNDnBDhbbb4tN9oKA1KpRFHeoYeufyJApVGixyAG4V"
],
[
- "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"
+ "cS824CTUh18scFmYuqt6BgxuRhdR4dEEnCHs3fzBbcyQgbfasHbw"
],
[
- "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"
+ "tc1q0ywf7wkz6t580n3yemd3ucfw8jxn93tpc6wskt"
],
[
- "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"
+ "bt1pxeeuh96wpm5c6u3kavts2qgwlv6y8um7u7ga6ltlwrhrv7w9vers8lgt3k"
],
[
- "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"
+ "tb130lvl2lyugsk2tf3zhwcjjv39dmwt2tt7ytqaexy8edwcuwks6p5scll5kz"
],
[
- "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"
+ "bcrt1rhsveeudk"
],
[
- "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"
+ "bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p"
],
[
- "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"
+ "tb1qjqnfsuatr54e957xzg9sqk7yqcry9lns"
],
[
- "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"
+ "bcrt1q8p08mv8echkf3es027u4cdswxlylm3th76ls8v6y4zy4vwsavngpr4e4td"
],
[
- "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"
+ "BC1QNC2H66VLWTWTW52DP0FYUSNU3QQG5VT4V"
],
[
- "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"
+ "tb1qgk665m2auw09rc7pqyf7aulcuhmatz9xqtr5mxew7zuysacaascqs9v0vn"
],
[
- "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"
+ "bcrt17CAPP7"
],
[
- "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"
+ "bc1qxmf2d6aerjzam3rur0zufqxqnyqfts5u302s7x"
],
[
- "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"
+ "tb1qn8x5dnzpexq7nnvrvnhwr9c3wkakpcyu9wwsjzq9pstkwg0t6qhs4l3rv6"
],
[
- "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"
+ "BCRT1Q397G2RNVYRL5LK07CE8NCKHVKP8Z4SC9U0MVH9"
],
[
- "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"
+ "bc1pgxwyajq0gdn389f69uwn2fw9q0z5c9s063j5dgkdd23ajaud4hpsercr9h"
],
[
- "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"
+ "tb1z6mnmp5k542l6yk4ul0mp4rq3yvz44lfm"
],
[
- "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"
+ "bcrt17capp7"
],
[
- "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"
+ "2D2bqvKseKHdoKjCNvjVULUgmxHu9hjKGwDbPRjTRH59tsHNLeyKwq3vyVBbo9LByY9wiapqjwFY"
],
[
- "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"
+ "2SSjAim4wZpeQRe5zTj1qqS6Li9ttJDaZ3ze"
],
[
- "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"
+ "mi9H6MjLwXxy9kxe1x4ToxyLRBsmcZxgVi"
],
[
- "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"
+ "VciXoxEitcn88jy197J9n9cpJ1pZahzU3SyWUiHqLgcfjttLEEJz"
],
[
- "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"
+ "KppmwADGoExPT9Eq5hjRWpWFDbzJyfzHFgsfxBiDHNpVBgWPRNuy"
],
[
- "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"
+ "TN7EQXMxKffzvHo54yHHu9R4ks9f5gWBW3MMVf5k72zAqrgVK9ys"
],
[
- "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"
+ "92dbrMEYzP5dD5UhQ6maNkCQ4GLG42BM4Gc6XKZzSSMSfosfkkcB"
],
[
- "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"
+ "J7VQxPxyzuWEkRstQWpCz2AgysEz1APgnWCEQrFvkN3umAnCrhQF"
],
[
- "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"
+ "tc1qymllj6c96v5qj2504y27ldtner6eh8ldx38t83"
],
[
- "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"
+ "bt1flep4g"
],
[
- "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"
+ "tb13c553hwygcgj48qwmr9f8q0hgdcfklyaye5sxzcpcjnmxv4z506xs90tchn"
],
[
- "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"
+ "bcrt1tyddyu"
],
[
- "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"
+ "bc10qssq2mknjqf0glwe2f3587wc4jysvs3f8s6chysae6hcl6fxzdm4wxyyscrl5k9f5qmnf05a"
],
[
- "EsYbG4tWWWY45G31nox838qNdzksbPySWc"
+ "tb1q425lmgvxdgtyl2m6xuu2pc354y4fvgg8"
],
[
- "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"
+ "bcrt1q9wp8e5d2u3u4g0pll0cy7smeeuqezdun9xl439n3p2gg4fvgfvk3hu52hj"
],
[
- "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"
+ "bc1qrz5acazpue8vl4zsaxn8fxtmeuqmyjkq3"
],
[
- "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"
+ "tb1qkeuglpgmnex9tv3fr7htzfrh3rwrk23r52rx9halxzmv9fr85lwq0fwhmp"
],
[
- "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"
+ "bcrt1qd0t2wrhl7s57z99rsyaekpq0dyjcQRSSmz80r4"
],
[
- "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"
+ "BC1QXLFDUCGX90T3E53PQCNKJ2PK25MSF3VLPMVY6T"
],
[
- "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"
+ "tb1qmycg4zszgnk34vaurx3cu8wpvteg9h40yq6cp52gt26gjel03t3su3x3xu"
],
[
- "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
+ "bcrt1q9hy58r4fnuxqzdqndpmq9pptc9nt2dw3rczf5e"
],
[
- "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
+ "BC1PA7682NAY6JQSLUWAJYTC0ERWTMW7A4RPWLNTUS32LCXWLHVKKKTQ2UL8CG"
],
[
- "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
+ "tb1z850dpxnwz2fzae5h2myatj4yvu6rq5xq"
],
[
- "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
+ "bcrt1sp525pzjsmpqvcrawjreww36e9keg876skjvpwt"
],
[
- "bc1rw5uspcuh"
+ "xcAvW5jurCpzSpLxBKEhCewCgwwuGhqJnC"
],
[
- "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
+ "2Cvv8yp9kXbQt8EKh6Yma95yJ1uwYF9YKXuVhGJyu3dHGVsb2AVpTC62TFACZZ3KDNrALxR2CVNs"
],
[
- "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
+ "niUuL46hCuEVvkAzZKHvD746qbmLmzip9Pv3F6UZV14JxzEXBnTkVxCT4URapChJG6qAEgsZs6G"
],
[
- "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
+ "2UHHgGfiipzvB8Eumnmvq6SowvrMJimjT3NwwG1839XEiUfwtpSdkUrseNsQuagXv21ce7aZu6yo"
],
[
- "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
+ "8u9djKu4u6o3bsgeR4BKNnLK3akpo64FYzDAmA9239wKeshgF97"
],
[
- "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
+ "TC1QPAARXSLVMXHVRR0474LZXQYZWLGFZYPSFVL9E4"
],
[
- "bc1gmk9yu"
+ "bt1pakek0n2267t9yaksxaczgr2syhv9y3xkx0wnsdwchfa6xkmjtvuqg3kgyr"
+ ],
+ [
+ "tb13h83rtwq62udrhwpn87uely7cyxcjrj0azz6a4r3n9s87x5uj98ys6ufp83"
+ ],
+ [
+ "bcrt1rk5vw5qf2"
+ ],
+ [
+ "bc10d3rmtg62h747en5j6fju5g5qyvsransrkty6ghh96pu647wumctejlsngh9pf26cysrys2x2"
+ ],
+ [
+ "tb1qajuy2cdwqgmrzc7la85al5cwcq374tsp"
+ ],
+ [
+ "bcrt1q3udxvj6x20chqh723mn064mzz65yr56ef00xk8czvu3jnx04ydapzk02s5"
+ ],
+ [
+ "bc1qule2szwzyaq4qy0s3aa4mauucyqt6fewe"
+ ],
+ [
+ "tb1ql0qny5vg9gh5tyzke6dw36px5ulkrp24x53x0pl2t5lpwrtejw3s2seej2"
+ ],
+ [
+ "bcrt17CAPP7"
+ ],
+ [
+ "bc1qtvm6davyf725wfedc2d5mrgfewqgcrce8gjrpl"
+ ],
+ [
+ "tb1q5acjgtqrrw3an0dzavxxxzlex8k7aukjzjk9v2u4rmfdqxjphcyq7ge97e"
]
]
diff --git a/src/test/data/key_io_valid.json b/src/test/data/key_io_valid.json
index 8418a6002d..5dee44c04b 100644
--- a/src/test/data/key_io_valid.json
+++ b/src/test/data/key_io_valid.json
@@ -1,533 +1,610 @@
[
[
- "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
- "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",
+ "1BShJZ8A5q53oJJfMJoEF1gfZCWdZqZwwD",
+ "76a914728d4cc27d19707b0197cfcd7c412d43287864b588ac",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "main",
+ "isPrivkey": false
}
],
[
- "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",
- "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",
+ "3L1YkZjdeNSqaZcNKZFXQfyokx3zVYm7r6",
+ "a914c8f37c3cc21561296ad81f4bec6b5de10ebc185187",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "main",
+ "isPrivkey": false
}
],
[
- "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
- "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
+ "mhJuoGLgnJC8gdBgBzEigsoyG4omQXejPT",
+ "76a91413a92d1998e081354d36c13ce0c9dc04b865d40a88ac",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false
}
],
[
- "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",
- "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",
+ "2N5VpzKEuYvZJbmg6eUNGnfrrD1ir92FWGu",
+ "a91486648cc2faaf05660e72c04c7a837bcc3e986f1787",
{
- "isPrivkey": false,
- "chain": "regtest"
+ "chain": "test",
+ "isPrivkey": false
}
],
[
- "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",
- "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",
+ "mtQueCtmAnP3E4aBHXCiFNEQAuPaLMuQNy",
+ "76a9148d74ecd86c845baf9c6d4484d2d00e731b79e34788ac",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "signet",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "2NEvWRTHjh89gV52fkperFtwzoFWQiQmiCh",
+ "a914edc895152c67ccff0ba620bcc373b789ec68266f87",
+ {
+ "chain": "signet",
+ "isPrivkey": false
}
],
[
- "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",
- "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",
+ "mngdx94qJFhSf7A7SAEgQSC9fQJuapujJp",
+ "76a9144e9dba545455a80ce94c343d1cac9dec62cbf22288ac",
{
+ "chain": "regtest",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "2NBzRN3pV56k3JUvSHifaHyzjGHv7ZS9FZZ",
+ "a914cd9da5642451273e5b6d088854cc1fad4a8d442187",
+ {
+ "chain": "regtest",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "5KcrFZvJ2p4dM6QVUPu53cKXcCfozA1PJLHm1mNAxkDYhgThLu4",
+ "ed6c796e2f62377410766214f55aa81ac9a6590ad7ed57c509c983bf648409ac",
+ {
+ "chain": "main",
"isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "isPrivkey": true
}
],
[
- "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",
- "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",
+ "L195WBrf2G3nCnun4CLxrb8XKk9LbCqH43THh4n4QrL5SzRzpq9j",
+ "74f76c106e38d20514a99a86e4fe3bb28319e7dd2ad21dbc170cbb516a5358fa",
{
+ "chain": "main",
"isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "isPrivkey": true
}
],
[
- "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
- "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
+ "92z6HnMQR4tWqjfVA3UaUN5EuUMgoVMdCa5rZFYZfmgyD7wxYCw",
+ "b8511e1d74549e305517d48a1d394d1be2cfa5d0f3c0d83f9f450316ffa01276",
{
+ "chain": "test",
"isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "isPrivkey": true
}
],
[
- "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",
- "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",
+ "cTPnaF52x4w4Tq6afPxRHux3wbYb86thS7S45A7r3oZc1AHTQ6Qm",
+ "ad68c48d337181da125de9061933ececcdf7d917631af7d34f7e38082bff9a11",
{
+ "chain": "test",
+ "isCompressed": true,
+ "isPrivkey": true
+ }
+ ],
+ [
+ "924U35yFcYkxe2JXGmuhSRVaShGyhRDZx1ysPmw1sAHuszGMoxq",
+ "3e8dfaf78d4f02b11d0b645648a4f3080d71d0d068979c47f7255c9a29eee01d",
+ {
+ "chain": "signet",
"isCompressed": false,
- "isPrivkey": true,
- "chain": "regtest"
+ "isPrivkey": true
}
],
[
- "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
- "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
+ "cRy1qCf2LUesGPQagTkYwk2V3PyN2KCPKgxeg6k6KoJPzH7nrVjw",
+ "82d4187690d6b59bcffda27dae52f2ecb87313cfc0904e0b674a27d906a65fde",
{
+ "chain": "signet",
"isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "isPrivkey": true
}
],
[
- "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",
- "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",
+ "932NTcHK35Apf2C3K9Zv1ZdeZEmB1x7ZT2Ju3SjoEY6pUgUpT7H",
+ "bd7dba24df9e003e145ae9b4862776413a0bb6fa5b4e42753397f2d9536e58a9",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "regtest"
+ "chain": "regtest",
+ "isCompressed": false,
+ "isPrivkey": true
}
],
[
- "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",
- "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",
+ "cNa75orYQ2oos52zCnMaS5PG6XbNZKc5LpGxTHacrxwWeX4WAK3E",
+ "1d87e3c58b08766fea03598380ec8d59f8c88d5392bf683ab1088bd4caf073ee",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "regtest",
+ "isCompressed": true,
+ "isPrivkey": true
}
],
[
- "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",
- "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",
+ "bc1q5cuatynjmk4szh40mmunszfzh7zrc5xm9w8ccy",
+ "0014a639d59272ddab015eafdef9380922bf843c50db",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",
- "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",
+ "bc1qkw7lz3ahms6e0ajv27mzh7g62tchjpmve4afc29u7w49tddydy2syv0087",
+ "0020b3bdf147b7dc3597f64c57b62bf91a52f179076ccd7a9c28bcf3aa55b5a46915",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",
- "a914c579342c2c4c9220205e2cdc285617040c924a0a87",
+ "bc1p5rgvqejqh9dh37t9g94dd9cm8vtqns7dndgj423egwggsggcdzmsspvr7j",
+ "5120a0d0c06640b95b78f965416ad6971b3b1609c3cd9b512aaa39439088211868b7",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",
- "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",
+ "bc1zr4pq63udck",
+ "52021d42",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "main",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",
- "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",
+ "tb1q74fxwnvhsue0l8wremgq66xzvn48jlc5zthsvz",
+ "0014f552674d978732ff9dc3ced00d68c264ea797f14",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",
- "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",
+ "tb1qpt7cqgq8ukv92dcraun9c3n0s3aswrt62vtv8nqmkfpa2tjfghesv9ln74",
+ "00200afd802007e598553703ef265c466f847b070d7a5316c3cc1bb243d52e4945f3",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",
- "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",
+ "tb1ph9v3e8nxct57hknlkhkz75p5pnxnkn05cw8ewpxu6tek56g29xgqydzfu7",
+ "5120b9591c9e66c2e9ebda7fb5ec2f50340ccd3b4df4c38f9704dcd2f36a690a2990",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",
- "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",
+ "tb1ray6e8gxfx49ers6c4c70l3c8lsxtcmlx",
+ "5310e93593a0c9354b91c358ae3cffc707fc",
{
+ "chain": "test",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",
- "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",
+ "tb1q0sqzfp3zj42u0perxr6jahhu4y03uw4dypk6sc",
+ "00147c002486229555c7872330f52edefca91f1e3aad",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",
- "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",
+ "tb1q9jv4qnawnuevqaeadn47gkq05ev78m4qg3zqejykdr9u0cm7yutq6gu5dj",
+ "00202c99504fae9f32c0773d6cebe4580fa659e3eea044440cc89668cbc7e37e2716",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",
- "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",
+ "tb1pxqf7d825wjtcftj7uep8w24jq3tz8vudfaqj20rns8ahqya56gcs92eqtu",
+ "51203013e69d54749784ae5ee642772ab2045623b38d4f41253c7381fb7013b4d231",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",
- "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",
+ "tb1rsrzkyvu2rt0dcgexajtazlw5nft4j7494ay396q6auw9375wxsrsgag884",
+ "532080c562338a1adedc2326ec97d17dd49a57597aa5af4912e81aef1c58fa8e3407",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "signet",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",
- "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",
+ "bcrt1qwf52dt9y2sv0f7fwkcpmtfjf74d4np2saeljt6",
+ "00147268a6aca45418f4f92eb603b5a649f55b598550",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "regtest",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",
- "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",
+ "bcrt1q0lma84unycxl4n96etffthqlf7y5axyp4fxf64kmhymvw8l6pwfs39futd",
+ "00207ff7d3d793260dfaccbacad295dc1f4f894e9881aa4c9d56dbb936c71ffa0b93",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "regtest",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",
- "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",
+ "bcrt1p3xat2ryucc2v0adrktqnavfzttvezrr27ngltsa2726p2ehvxz4se722v2",
+ "512089bab50c9cc614c7f5a3b2c13eb1225ad9910c6af4d1f5c3aaf2b41566ec30ab",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "regtest",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",
- "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",
+ "bcrt1saflydw6e26xhp29euhy5jke5jjqyywk3wvtc9ulgw9dvxyuqy9hdnxthyw755c7ldavy7u",
+ "6028ea7e46bb59568d70a8b9e5c9495b349480423ad1731782f3e8715ac31380216ed9997723bd4a63df",
{
+ "chain": "regtest",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",
- "a914188f91a931947eddd7432d6e614387e32b24470987",
+ "16y3Q1XVRZqMR9T1XL1FkvNtD2E1bXBuYa",
+ "76a9144171ec673aeb9fcf42af6094a3c82207e3b9a78188ac",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "main",
+ "isPrivkey": false
}
],
[
- "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",
- "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",
+ "3CmZZnAiHVQgiAKSakf864oJMxN2BP1eLC",
+ "a914798575fc1041b9440c4e63c28e57e597d00b7e4387",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "main",
+ "isPrivkey": false
}
],
[
- "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",
- "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",
+ "mtCB3SoBo7EYUv8j54kUubGY4x3aJPY8nk",
+ "76a9148b0c5f9ee714e0d1d24642ad63d9d5f398d9b56588ac",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false
}
],
[
- "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",
- "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",
+ "2N5ymzzKpx6EdUR4UdMZ7t9hcuwqtpHwgw5",
+ "a9148badb3c3b5c0d39f906f7618e0018b7eae4baf7387",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "test",
+ "isPrivkey": false
}
],
[
- "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",
- "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",
+ "myXnpYbub28zgiJupDdZSWZtDbjcyfJVby",
+ "76a914c59ac57661b57daadd7c0caf7318c14f54c6c0fa88ac",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "signet",
+ "isPrivkey": false
}
],
[
- "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
- "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
+ "2MtLg8jS5jSXm9evMzTtvpLjy26dBmjFEoT",
+ "a9140c0007e89cea625d3bf9543baa5a470bb7e5b67287",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "signet",
+ "isPrivkey": false
}
],
[
- "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",
- "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",
+ "mzCyqdf2UNGdpgkD9NBgLcxdwXRg1i9buY",
+ "76a914cd04311bdd1ef9c5c24e41930e032aade82a863a88ac",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "regtest"
+ "chain": "regtest",
+ "isPrivkey": false
}
],
[
- "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",
- "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",
+ "2N3zGiwFku2vQjYnAqXv5Qu2ztfYRhh7tbF",
+ "a91475d56d75c88e704d6c72fbe84ac1505abf736b4087",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "regtest",
+ "isPrivkey": false
}
],
[
- "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",
- "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",
+ "5JUHCgyxNSHg64wwju72eNsG6ajqo4Z2fHHw9iLDLfh69rSiL7w",
+ "5644d06d88855dacf3192a31df8f4acd8e4c155c52a86d2c1fa48303f5cff053",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "main",
+ "isCompressed": false,
+ "isPrivkey": true
}
],
[
- "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",
- "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",
+ "L2kZaexG69VSriMe9T2m1jkS86iPe3xNbjcdfakRC1PHe7ay78Ji",
+ "a50ee94aefcabf5a5d7c85be5b3844dee03c5604861dbfc77fe388c91e5a30f8",
{
- "isPrivkey": false,
- "chain": "main"
+ "chain": "main",
+ "isCompressed": true,
+ "isPrivkey": true
}
],
[
- "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",
- "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",
+ "927JwT1ViCr5TD2ZX8CsMNhg17dXmou5xu4y2KiH54zD7i34UJq",
+ "4502a54c0026b0150281d41f40860d1e23870c63cdc32645bbed688f2ee41f64",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "test",
+ "isCompressed": false,
+ "isPrivkey": true
}
],
[
- "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",
- "a914e930e1834a4d234702773951d627cce82fbb5d2e87",
+ "cTpGGNPVy2Eagawohbr4aGtRJzpLnjxGsGYh9DUcBM45f3KdKGF6",
+ "ba005a0cb39587aab00bd54c848b59e8adaed47403228567ddc739c2a344ff59",
{
- "isPrivkey": false,
- "chain": "test"
+ "chain": "test",
+ "isCompressed": true,
+ "isPrivkey": true
}
],
[
- "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",
- "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",
+ "932PLCLA19yPNqV67qwHBSGjxi82LVzWBF7josL9ab4Q1kxgPGF",
+ "bd8677e076eb39770bf7e9f9e8d3f2cf257effab9b4c220fd3439ccfc208c984",
{
+ "chain": "signet",
"isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "isPrivkey": true
}
],
[
- "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",
- "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",
+ "cViUpEy8URSsLjUvxwL7cEuNgCVqM7oKBzd1ZPbA4khcQsQJuj1j",
+ "f2b36ade8393e29dc71e52cb75ba1109ba210203cd7d0a5ae881ad6846516203",
{
+ "chain": "signet",
"isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "isPrivkey": true
}
],
[
- "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",
- "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",
+ "92jddDjJCVDmJtgvBHQ9i58PMash8kwsYhRdNo22ea2MYPXdCBE",
+ "977bf8686f1bcad28f86c4c14afbd33215746bd19203647bf7ff9c6fddc9cc87",
{
+ "chain": "regtest",
"isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "isPrivkey": true
}
],
[
- "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",
- "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",
+ "cVwAuMoUqRo399X7vXzuzQyPEvZJMXM8c82zHzRkFCxPCSGx8A6y",
+ "f93acbbce02b8cb9ddca3fad495441e324cc01eb640b0a7b4c9f0e31644c822a",
{
+ "chain": "regtest",
"isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "isPrivkey": true
}
],
[
- "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",
- "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",
+ "bc1qz377zwe5awr68dnggengqx9vrjt05k98q3sw2n",
+ "0014147de13b34eb87a3b66846668018ac1c96fa58a7",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",
- "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",
+ "bc1qkmhskpdzg8kdkfywhu09kswwn9qan9vnkrf6mk40jvnr06s6sz5ssf82ya",
+ "0020b6ef0b05a241ecdb248ebf1e5b41ce9941d99593b0d3addaaf932637ea1a80a9",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",
- "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",
+ "bc1ps8cndas60cntk8x79sg9f5e5jz7x050z8agyugln2ukkks23rryqpejzkc",
+ "512081f136f61a7e26bb1cde2c1054d33490bc67d1e23f504e23f3572d6b415118c8",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",
- "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",
+ "bc1zn4tsczge9l",
+ "52029d57",
{
+ "chain": "main",
"isPrivkey": false,
- "chain": "test"
+ "tryCaseFlip": true
}
],
[
- "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",
- "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",
+ "tb1q6xw0wwd9n9d7ge87dryz4vm5vtahzhvz6yett3",
+ "0014d19cf739a5995be464fe68c82ab37462fb715d82",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",
- "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",
+ "tb1qwn9zq9fu5uk35ykpgsc7rz4uawy4yh0r5m5er26768h5ur50su3qj6evun",
+ "002074ca20153ca72d1a12c14431e18abceb89525de3a6e991ab5ed1ef4e0e8f8722",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "main"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",
- "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",
+ "tb1pmcdc5d8gr92rtemfsnhpeqanvs0nr82upn5dktxluz9n0qcv34lqxke0wq",
+ "5120de1b8a34e8195435e76984ee1c83b3641f319d5c0ce8db2cdfe08b37830c8d7e",
{
- "isCompressed": false,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",
- "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",
+ "tb1rgxjvtfzp0xczz6dlzqv8d5cmuykk4qkk",
+ "531041a4c5a44179b02169bf101876d31be1",
{
- "isCompressed": true,
- "isPrivkey": true,
- "chain": "test"
+ "chain": "test",
+ "isPrivkey": false,
+ "tryCaseFlip": true
}
],
[
- "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",
- "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",
+ "tb1qa9dlyt6fydestul4y4wh72yshh044w32np8etk",
+ "0014e95bf22f49237305f3f5255d7f2890bddf5aba2a",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",
- "a9145ece0cadddc415b1980f001785947120acdb36fc87",
+ "tb1qu4p26n0033720xm0rjgkds5ehdwf039k2fgv75um5krrvfhrrj7qckl9r2",
+ "0020e542ad4def8c7ca79b6f1c9166c299bb5c97c4b65250cf539ba5863626e31cbc",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "main"
+ "tryCaseFlip": true
}
],
[
- "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
- "0014751e76e8199196d454941c45d1b3a323f1433bd6",
+ "tb1pjyukm4n4flwd0ey3lrl06c9kalr60ggmlkcxq2rhhxmy4lvkmkpqexdzqy",
+ "512091396dd6754fdcd7e491f8fefd60b6efc7a7a11bfdb0602877b9b64afd96dd82",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "main",
"tryCaseFlip": true
}
],
[
- "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",
- "0014751e76e8199196d454941c45d1b3a323f1433bd6",
+ "tb1r4k75s5syvewsvxufdc3xfhf4tw4u30alw39xny3dnxrl6hau7systymfdv",
+ "5320adbd485204665d061b896e2264dd355babc8bfbf744a69922d9987fd5fbcf409",
{
+ "chain": "signet",
"isPrivkey": false,
- "chain": "regtest",
"tryCaseFlip": true
}
],
[
- "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
- "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
+ "bcrt1qnk3tdwwj47ppc4pqmxkjdusegedn9ru5gvccwa",
+ "00149da2b6b9d2af821c5420d9ad26f219465b328f94",
{
+ "chain": "regtest",
"isPrivkey": false,
- "chain": "test",
"tryCaseFlip": true
}
],
[
- "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
- "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
+ "bcrt1qz7prfshfkwsxuk72pt6mzr6uumq4qllxe4vmwqt89tat48d362yqlykk6a",
+ "0020178234c2e9b3a06e5bca0af5b10f5ce6c1507fe6cd59b701672afaba9db1d288",
{
+ "chain": "regtest",
"isPrivkey": false,
- "chain": "main",
"tryCaseFlip": true
}
],
[
- "bc1sw50qa3jx3s",
- "6002751e",
+ "bcrt1pumee3wj80xvyr7wjmj7zsk26x5pn095aegy862yhx6f2j9sgc9hq6cj4cm",
+ "5120e6f398ba47799841f9d2dcbc28595a350337969dca087d28973692a91608c16e",
{
+ "chain": "regtest",
"isPrivkey": false,
- "chain": "main",
"tryCaseFlip": true
}
],
[
- "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
- "5210751e76e8199196d454941c45d1b3a323",
+ "bcrt1szqz8hj64d2hhc6nt65v09jxal66pgff2xpcp9kj648qkk8kjzxelsts4dktd799g47uase",
+ "602810047bcb556aaf7c6a6bd518f2c8ddfeb414252a307012da5aa9c16b1ed211b3f82e156d96df14a8",
{
+ "chain": "regtest",
"isPrivkey": false,
- "chain": "main",
"tryCaseFlip": true
}
],
[
- "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
- "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
+ "12agZTajtRE3STSchwWNWnrm467zzTQ916",
+ "76a9141156e00f70061e5faba8b71593a8c7554b47090c88ac",
+ {
+ "chain": "main",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "3NXqB6iZiPYbKruNT3d9xNBTmtb73xMvvf",
+ "a914e49decc9e5d97e0547d3642f3a4795b13ae62bca87",
+ {
+ "chain": "main",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "mjgt4BoCYxjzWvJFoh68x7cj5GeaKDYhyx",
+ "76a9142dc11fc7b8072f733f690ffb0591c00f4062295c88ac",
{
- "isPrivkey": false,
"chain": "test",
- "tryCaseFlip": true
+ "isPrivkey": false
}
],
[
- "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",
- "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",
+ "2NCT6FdQ5MxorHgnFxLeHyGwTGRdkHcrJDH",
+ "a914d2a8ec992b0894a0d9391ca5d9c45c388c41be7e87",
{
- "isPrivkey": false,
- "chain": "regtest",
- "tryCaseFlip": true
+ "chain": "test",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "mpomiA7wqDnMcxaNLC23eBuXAb4U6H4ZqW",
+ "76a91465e75e340415ed297c58d6a14d3c17ceeaa17bbd88ac",
+ {
+ "chain": "signet",
+ "isPrivkey": false
+ }
+ ],
+ [
+ "2N1pGAA5uatbU2PKvMA9BnJmHcK6yHfMiZa",
+ "a9145e008b6cc232164570befc23d216060bf4ea793b87",
+ {
+ "chain": "signet",
+ "isPrivkey": false
}
]
]
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index 3b1db449b2..c394356798 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -3,27 +3,28 @@
["They are in the form"],
["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"],
["serializedTransaction, verifyFlags]"],
+["Use BADTX for verifyFlags if it is expected to fail CheckTransaction()"],
["Objects that are only a single string (like this one) are ignored"],
["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"],
[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]],
-"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "P2SH"],
+"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "NONE"],
["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"],
["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"],
["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"],
["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"],
["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"],
["but with the signature duplicated in the scriptPubKey with a different hashtype suffix"],
["See FindAndDelete, which will only remove if the signature, including the hash type, matches"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a81"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"],
["An invalid P2SH Transaction"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
@@ -32,46 +33,46 @@
["Tests for CheckTransaction()"],
["No outputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "BADTX"],
["Negative output"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", "BADTX"],
["MAX_MONEY + 1 output"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "BADTX"],
["MAX_MONEY output + 1 output"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "BADTX"],
["Duplicate inputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]],
-"01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", "P2SH"],
+"01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", "BADTX"],
["Coinbase of size 1"],
["Note the input is just required to make the tester happy"],
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
-"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "BADTX"],
["Coinbase of size 101"],
["Note the input is just required to make the tester happy"],
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
-"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "BADTX"],
["Null txin, but without being a coinbase (because there are two inputs)"],
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"],
["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015100000000", "P2SH"],
+"01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015100000000", "BADTX"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"],
["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
-"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff010000000000000000015100000000", "P2SH"],
+"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff010000000000000000015100000000", "BADTX"],
["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
- "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "P2SH"],
+ "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "NONE"],
["CHECKMULTISIG with incorrect signature order"],
["Note the input is just required to make the tester happy"],
@@ -82,7 +83,7 @@
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
["It is an OP_CHECKMULTISIG with the dummy value missing"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NONE"],
["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"],
@@ -90,104 +91,104 @@
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"],
["As above, but using an OP_1"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"],
["As above, but using an OP_1NEGATE"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"],
["As above, but with the dummy byte missing"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NONE"],
["Empty stack when we try to run CHECKSIG"],
[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]],
-"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"],
+"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "NONE"],
["Inverted versions of tx_valid CODESEPARATOR IF block tests"],
["CODESEPARATOR in an unexecuted IF block does not change what is hashed"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
-"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "P2SH"],
+"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "NONE"],
["As above, with the IF block executed"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
-"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "P2SH"],
+"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "NONE"],
["CHECKLOCKTIMEVERIFY tests"],
["By-height locks, with argument just beyond tx nLockTime"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "CHECKLOCKTIMEVERIFY"],
["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "CHECKLOCKTIMEVERIFY"],
["Argument missing"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Argument negative with by-blockheight nLockTime=0"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Argument negative with by-blocktime nLockTime=500,000,000"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "CHECKLOCKTIMEVERIFY"],
["Input locked"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "NONE"],
["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"] ,
["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]],
-"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Argument/tx height/time mismatch, both versions"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "NONE"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "CHECKLOCKTIMEVERIFY"],
["Argument 2^32 with nLockTime=2^32-1"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000000001 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "CHECKLOCKTIMEVERIFY"],
["Same, but with nLockTime=2^31-1"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "CHECKLOCKTIMEVERIFY"],
["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]],
@@ -195,51 +196,51 @@
["A transaction with a non-standard DER signature."],
[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
-"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"],
+"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "DERSIG"],
["CHECKSEQUENCEVERIFY tests"],
["By-height locks, with argument just beyond txin.nSequence"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["By-time locks, with argument just beyond txin.nSequence (but within numerical boundaries)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument missing"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument negative with by-blockheight txin.nSequence=0"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument negative with by-blocktime txin.nSequence=CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument/tx height/time mismatch, both versions"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Failure due to failing CHECKSEQUENCEVERIFY in scriptSig"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Failure due to failing CHECKSEQUENCEVERIFY in redeemScript"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]],
@@ -247,9 +248,9 @@
["Failure due to insufficient tx.nVersion (<2)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Unknown witness program version (with DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
@@ -273,7 +274,7 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff000100000000000000000000000000000000000000000000000000000000000001000000000100000000010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff000100000000000000000000000000000000000000000000000000000000000001000000000100000000010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
["Witness with SigHash All|AnyoneCanPay (third output value changed)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
@@ -287,7 +288,7 @@
["Witness with unknown version which push false on the stack should be invalid (even without DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x02 0x0000", 2000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015101010100000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015101010100000000", "NONE"],
["Witness program should leave clean stack"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x2f04a3aa051f1f60d695f6c44c0c3d383973dfd446ace8962664a76bb10e31a8", 2000]],
@@ -304,11 +305,11 @@
["Non witness Single|AnyoneCanPay hash input's position (permutation)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1001]],
-"010000000200010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff0001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff02e9030000000000000151e803000000000000015100000000", "P2SH,WITNESS"],
+"010000000200010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff0001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff02e9030000000000000151e803000000000000015100000000", "NONE"],
-["P2WSH with a redeem representing a witness scriptPubKey should fail"],
+["P2WSH with a redeem representing a witness scriptPubKey should fail due to too many stack items"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x34b6c399093e06cf9f0f7f660a1abcfe78fcf7b576f43993208edd9518a0ae9b", 1000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0001045102010100000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015101045102010100000000", "P2SH,WITNESS"],
["33 bytes push should be considered a witness scriptPubKey"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x21 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff", 1000]],
@@ -324,7 +325,7 @@
["by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE)"],
["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
-"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH"],
["BIP143: wrong sighash (with FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9d7ed6e161f0e255c10bbfcca0128a9e2035c2c8da58899c54d22d3a31afdef4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
@@ -335,7 +336,7 @@
["Should pass by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE) under OP_CHECKMULTISIG"],
["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
-"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH"],
["BIP143: wrong sighash (with FindAndDelete) = 17c50ec2181ecdfdc85ca081174b248199ba81fff730794d4f69b8ec031f2dce"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
@@ -344,39 +345,39 @@
["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"],
[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
- "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
- "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]],
- "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
- "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["CODESEPARATOR in an unexecuted IF block is still invalid"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
- "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["CODESEPARATOR in an executed IF block is invalid"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
- "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["Using CHECKSIG with signatures in scriptSigs will trigger FindAndDelete, which is invalid"],
[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
- "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["OP_CODESEPARATOR in scriptSig is invalid"],
[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
- "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["Again, FindAndDelete() in scriptSig"],
[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
- "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
- "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"],
+ "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["FindAndDelete() in redeemScript"],
[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
@@ -386,7 +387,7 @@
["FindAndDelete() in bare CHECKMULTISIG"],
[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
- "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH,CONST_SCRIPTCODE"],
+ "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "CONST_SCRIPTCODE"],
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 11634c90f0..2727af5abd 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -2,7 +2,7 @@
["The following are deserialized transactions which are valid."],
["They are in the form"],
["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"],
-["serializedTransaction, verifyFlags]"],
+["serializedTransaction, excluded verifyFlags]"],
["Objects that are only a single string (like this one) are ignored"],
["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
@@ -10,413 +10,413 @@
["See http://r6.ca/blog/20111119T211504Z.html"],
["It is also the first OP_CHECKMULTISIG transaction in standard form"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC"],
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"],
["The dummy byte is fine however, so the NULLDUMMY flag should be happy"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC"],
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"],
["As above, but using an OP_1"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"],
["As above, but using an OP_1NEGATE"],
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
-"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
+"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"],
["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"],
["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"],
[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]],
-"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", "P2SH"],
+"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", "LOW_S,STRICTENC"],
["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"],
["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "CLEANSTACK,CONST_SCRIPTCODE"],
["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"],
["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation. In a signature, it contains an ASN1 integer which isn't strict-DER conformant due to being negative, which doesn't make sense in a signature. Before BIP66 activated, it was a valid signature. After it activated, it's not valid any more."],
[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"],
["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]],
-"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"],
+"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "DERSIG,LOW_S,STRICTENC"],
["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"],
["It results in signing the constant 1, instead of something generated based on the transaction,"],
["when the input doing the signing has an index greater than the maximum output index"],
[[["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"], ["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"]],
-"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"],
+"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "CLEANSTACK"],
["The following tests SIGHASH_SINGLE|SIGHASHANYONECANPAY inputs"],
[[["437a1002eb125dec0f93f635763e0ae45f28ff8e81d82945753d0107611cd390", 1, "DUP HASH160 0x14 0x383fb81cb0a3fc724b5e08cf8bbd404336d711f6 EQUALVERIFY CHECKSIG"],
["2d48d32ccad087bcda0ac5b31555bd58d1d2568184cbc8e752dd2be2684af03f", 1, "DUP HASH160 0x14 0x275ec2a233e5b23d43fa19e7bf9beb0cb3996117 EQUALVERIFY CHECKSIG"],
["c76168ef1a272a4f176e55e73157ecfce040cfad16a5272f6296eb7089dca846", 1, "DUP HASH160 0x14 0x34fea2c5a75414fd945273ae2d029ce1f28dafcf EQUALVERIFY CHECKSIG"]],
-"010000000390d31c6107013d754529d8818eff285fe40a3e7635f6930fec5d12eb02107a43010000006b483045022100f40815ae3c81a0dd851cc8d376d6fd226c88416671346a9033468cca2cdcc6c202204f764623903e6c4bed1b734b75d82c40f1725e4471a55ad4f51218f86130ac038321033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff3ff04a68e22bdd52e7c8cb848156d2d158bd5515b3c50adabc87d0ca2cd3482d010000006a4730440220598d263c107004008e9e26baa1e770be30fd31ee55ded1898f7c00da05a75977022045536bead322ca246779698b9c3df3003377090f41afeca7fb2ce9e328ec4af2832102b738b531def73020bd637f32935924cc88549c8206976226d968edd3a42fc2d7ffffffff46a8dc8970eb96622f27a516adcf40e0fcec5731e7556e174f2a271aef6861c7010000006b483045022100c5b90a777a9fdc90c208dbef7290d1fc1be651f47151ee4ccff646872a454cf90220640cfbc4550446968fbbe9d12528f3adf7d87b31541569c59e790db8a220482583210391332546e22bbe8fe3af54addfad6f8b83d05fa4f5e047593d4c07ae938795beffffffff028036be26000000001976a914ddfb29efad43a667465ac59ff14dc6442a1adfca88ac3d5cba01000000001976a914b64dde7a505a13ca986c40e86e984a8dc81368b688ac00000000", "P2SH"],
+"010000000390d31c6107013d754529d8818eff285fe40a3e7635f6930fec5d12eb02107a43010000006b483045022100f40815ae3c81a0dd851cc8d376d6fd226c88416671346a9033468cca2cdcc6c202204f764623903e6c4bed1b734b75d82c40f1725e4471a55ad4f51218f86130ac038321033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff3ff04a68e22bdd52e7c8cb848156d2d158bd5515b3c50adabc87d0ca2cd3482d010000006a4730440220598d263c107004008e9e26baa1e770be30fd31ee55ded1898f7c00da05a75977022045536bead322ca246779698b9c3df3003377090f41afeca7fb2ce9e328ec4af2832102b738b531def73020bd637f32935924cc88549c8206976226d968edd3a42fc2d7ffffffff46a8dc8970eb96622f27a516adcf40e0fcec5731e7556e174f2a271aef6861c7010000006b483045022100c5b90a777a9fdc90c208dbef7290d1fc1be651f47151ee4ccff646872a454cf90220640cfbc4550446968fbbe9d12528f3adf7d87b31541569c59e790db8a220482583210391332546e22bbe8fe3af54addfad6f8b83d05fa4f5e047593d4c07ae938795beffffffff028036be26000000001976a914ddfb29efad43a667465ac59ff14dc6442a1adfca88ac3d5cba01000000001976a914b64dde7a505a13ca986c40e86e984a8dc81368b688ac00000000", "NONE"],
["An invalid P2SH Transaction"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "NONE"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH,CLEANSTACK,WITNESS"],
["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", "LOW_S"],
["Tests for CheckTransaction()"],
["MAX_MONEY output"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", "LOW_S"],
["MAX_MONEY output + 0 output"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", "P2SH"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", "LOW_S"],
["Coinbase of size 2"],
["Note the input is just required to make the tester happy"],
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
-"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", "CLEANSTACK"],
["Coinbase of size 100"],
["Note the input is just required to make the tester happy"],
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
-"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"],
+"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "CLEANSTACK"],
["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
- "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"],
+ "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "NONE"],
["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
- "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"],
+ "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "LOW_S"],
["afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae which has several SIGHASH_SINGLE signatures"],
[[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"],
["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"],
["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]],
- "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", "P2SH"],
+ "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", "LOW_S"],
["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"],
[[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]],
- "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", "P2SH"],
+ "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", "NONE"],
["Correct signature order"],
["Note the input is just required to make the tester happy"],
[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]],
-"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"],
+"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "LOW_S"],
["cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984, which is a fairly strange transaction which relies on OP_CHECKSIG returning 0 when checking a completely invalid sig of length 0"],
[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]],
-"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "P2SH"],
+"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "CLEANSTACK"],
["Empty pubkey"],
[[["229257c295e7f555421c1bfec8538dd30a4b5c37c1c8810bbe83cafa7811652c", 0, "0x00 CHECKSIG NOT"]],
-"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", "P2SH"],
+"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", "STRICTENC,NULLFAIL"],
["Empty signature"],
[[["9ca93cfd8e3806b9d9e2ba1cf64e3cc6946ee0119670b1796a09928d14ea25f7", 0, "0x21 0x028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02 CHECKSIG NOT"]],
-"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", "P2SH"],
+"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", "NONE"],
[[["444e00ed7840d41f20ecd9c11d3f91982326c731a02f3c05748414a4fa9e59be", 0, "1 0x00 0x21 0x02136b04758b0b6e363e7a6fbe83aaf527a153db2b060d36cc29f7f8309ba6e458 2 CHECKMULTISIG"]],
-"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", "P2SH"],
+"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", "NONE"],
[[["e16abbe80bf30c080f63830c8dbf669deaef08957446e95940227d8c5e6db612", 0, "1 0x21 0x03905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9f 0x00 2 CHECKMULTISIG"]],
-"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", "P2SH"],
+"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", "STRICTENC"],
[[["ebbcf4bfce13292bd791d6a65a2a858d59adbf737e387e40370d4e64cc70efb0", 0, "2 0x21 0x033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194 0x21 0x03a88b326f8767f4f192ce252afe33c94d25ab1d24f27f159b3cb3aa691ffe1423 2 CHECKMULTISIG NOT"]],
-"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", "P2SH"],
+"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", "NULLFAIL"],
[[["ba4cd7ae2ad4d4d13ebfc8ab1d93a63e4a6563f25089a18bf0fc68f282aa88c1", 0, "2 0x21 0x037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1 0x21 0x02edc823cd634f2c4033d94f5755207cb6b60c4b1f1f056ad7471c47de5f2e4d50 2 CHECKMULTISIG NOT"]],
-"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", "P2SH"],
+"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", "NULLFAIL"],
["OP_CODESEPARATOR tests"],
["Test that SignatureHash() removes OP_CODESEPARATOR with FindAndDelete()"],
[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
-"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"],
[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
-"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"],
["Hashed data starts at the CODESEPARATOR"],
[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]],
-"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"],
["But only if execution has reached it"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
-"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH"],
+"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"],
["CODESEPARATOR in an unexecuted IF block does not change what is hashed"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
-"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH"],
+"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"],
["As above, with the IF block executed"],
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
-"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH"],
+"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"],
["CHECKSIG is legal in scriptSigs"],
[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
-"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
+"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"],
["Same semantics for OP_CODESEPARATOR"],
[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
-"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"],
["Signatures are removed from the script they are in by FindAndDelete() in the CHECKSIG code; even multiple instances of one signature can be removed."],
[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
-"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,CLEANSTACK"],
["That also includes ahead of the opcode being executed."],
[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
-"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
+"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"],
["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."],
["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"],
[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]],
-"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH"],
+"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "CONST_SCRIPTCODE,LOW_S"],
["Same idea, but with bare CHECKMULTISIG"],
[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
-"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"],
+"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "CONST_SCRIPTCODE,LOW_S"],
["CHECKLOCKTIMEVERIFY tests"],
["By-height locks, with argument == 0 and == tx nLockTime"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "NONE"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "CLEANSTACK"],
["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY"]],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "NONE"],
["Any non-maxint nSequence is fine"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "CLEANSTACK"],
["The argument can be calculated rather than created directly by a PUSHDATA"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD CHECKLOCKTIMEVERIFY 1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD CHECKLOCKTIMEVERIFY"]],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "NONE"],
["Perhaps even by an ADD producing a 5-byte result that is out of bounds for other opcodes"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD CHECKLOCKTIMEVERIFY 1"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD CHECKLOCKTIMEVERIFY"]],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "NONE"],
["5 byte non-minimally-encoded arguments are valid"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 CHECKLOCKTIMEVERIFY 1"]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK,MINIMALDATA"],
["Valid CHECKLOCKTIMEVERIFY in scriptSig"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "SIGPUSHONLY,CLEANSTACK"],
["Valid CHECKLOCKTIMEVERIFY in redeemScript"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]],
-"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "NONE"],
["A transaction with a non-standard DER signature."],
[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
-"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH"],
+"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "DERSIG,LOW_S,STRICTENC"],
["CHECKSEQUENCEVERIFY tests"],
["By-height locks, with argument == 0 and == txin.nSequence"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "CLEANSTACK"],
["By-time locks, with argument == 0 and == txin.nSequence"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"],
["Upper sequence with upper sequence is fine"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"],
["Argument 2^31 with various nSequence"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"],
["Argument 2^32-1 with various nSequence"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"],
["Argument 3<<31 with various nSequence"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"],
["5 byte non-minimally-encoded operandss are valid"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "MINIMALDATA,CLEANSTACK"],
["The argument can be calculated rather than created directly by a PUSHDATA"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194303 1ADD CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 1SUB CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194303 1ADD CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 1SUB CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "NONE"],
["An ADD producing a 5-byte result that sets CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 65536 CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 4259840 ADD CHECKSEQUENCEVERIFY 1"]],
-"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 65536 ADD CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "NONE"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 4259840 ADD CHECKSEQUENCEVERIFY"]],
+"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"],
["Valid CHECKSEQUENCEVERIFY in scriptSig"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
-"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000", "SIGPUSHONLY,CLEANSTACK"],
["Valid CHECKSEQUENCEVERIFY in redeemScript"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]],
-"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"],
+"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000", "NONE"],
["Valid P2WPKH (Private key of segwit tests is L5AQtV2HDm4xGsseLokK2VAT2EtYKcTm3c7HwqnJBFt9LdaQULsM)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"],
["Valid P2WSH"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db", 1000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "NONE"],
["Valid P2SH(P2WPKH)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xfe9c7dacc9fcfbf7e3b7d5ad06aa2b28c5a7b7e3 EQUAL", 1000]],
-"01000000000101000100000000000000000000000000000000000000000000000000000000000000000000171600144c9c3dfac4207d5d8cb89df5722cb3d712385e3fffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
+"01000000000101000100000000000000000000000000000000000000000000000000000000000000000000171600144c9c3dfac4207d5d8cb89df5722cb3d712385e3fffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"],
["Valid P2SH(P2WSH)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x2135ab4f0981830311e35600eebc7376dce3a914 EQUAL", 1000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "NONE"],
["Witness with SigHash Single|AnyoneCanPay"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
-"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000", "P2SH,WITNESS"],
+"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000", "NONE"],
["Witness with SigHash Single|AnyoneCanPay (same signature as previous)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash Single"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff0484030000000000000151d0070000000000000151540b0000000000000151c800000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff0484030000000000000151d0070000000000000151540b0000000000000151c800000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash Single (same signature as previous)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash None|AnyoneCanPay"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
-"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash None|AnyoneCanPay (same signature as previous)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash None"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash None (same signature as previous)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash None (same signature, only sequences changed)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash All|AnyoneCanPay"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100],
["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]],
-"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Witness with SigHash All|AnyoneCanPay (same signature as previous)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Unknown witness program version (without DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x60 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000],
["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]],
-"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
["Witness with a push of 520 bytes"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x33198a9bfef674ebddb9ffaa52928017b8472791e54c609cb95f278ac6b1e349", 1000]],
-"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000", "P2SH,WITNESS"],
+"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000", "NONE"],
["Transaction mixing all SigHash, segwit and normal inputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000],
@@ -431,67 +431,67 @@
["0000000000000000000000000000000000000000000000000000000000000100", 9, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1009],
["0000000000000000000000000000000000000000000000000000000000000100", 10, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1010],
["0000000000000000000000000000000000000000000000000000000000000100", 11, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1011]],
-"0100000000010c00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff0001000000000000000000000000000000000000000000000000000000000000020000006a473044022026c2e65b33fcd03b2a3b0f25030f0244bd23cc45ae4dec0f48ae62255b1998a00220463aa3982b718d593a6b9e0044513fd67a5009c2fdccc59992cffc2b167889f4012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000030000006a4730440220008bd8382911218dcb4c9f2e75bf5c5c3635f2f2df49b36994fde85b0be21a1a02205a539ef10fb4c778b522c1be852352ea06c67ab74200977c722b0bc68972575a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000040000006b483045022100d9436c32ff065127d71e1a20e319e4fe0a103ba0272743dbd8580be4659ab5d302203fd62571ee1fe790b182d078ecfd092a509eac112bea558d122974ef9cc012c7012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000050000006a47304402200e2c149b114ec546015c13b2b464bbcb0cdc5872e6775787527af6cbc4830b6c02207e9396c6979fb15a9a2b96ca08a633866eaf20dc0ff3c03e512c1d5a1654f148012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000060000006b483045022100b20e70d897dc15420bccb5e0d3e208d27bdd676af109abbd3f88dbdb7721e6d6022005836e663173fbdfe069f54cde3c2decd3d0ea84378092a5d9d85ec8642e8a41012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff00010000000000000000000000000000000000000000000000000000000000000700000000ffffffff00010000000000000000000000000000000000000000000000000000000000000800000000ffffffff00010000000000000000000000000000000000000000000000000000000000000900000000ffffffff00010000000000000000000000000000000000000000000000000000000000000a00000000ffffffff00010000000000000000000000000000000000000000000000000000000000000b0000006a47304402206639c6e05e3b9d2675a7f3876286bdf7584fe2bbd15e0ce52dd4e02c0092cdc60220757d60b0a61fc95ada79d23746744c72bac1545a75ff6c2c7cdb6ae04e7e9592012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0ce8030000000000000151e9030000000000000151ea030000000000000151eb030000000000000151ec030000000000000151ed030000000000000151ee030000000000000151ef030000000000000151f0030000000000000151f1030000000000000151f2030000000000000151f30300000000000001510248304502210082219a54f61bf126bfc3fa068c6e33831222d1d7138c6faa9d33ca87fd4202d6022063f9902519624254d7c2c8ea7ba2d66ae975e4e229ae38043973ec707d5d4a83012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022017fb58502475848c1b09f162cb1688d0920ff7f142bed0ef904da2ccc88b168f02201798afa61850c65e77889cbcd648a5703b487895517c88f85cdd18b021ee246a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000000247304402202830b7926e488da75782c81a54cd281720890d1af064629ebf2e31bf9f5435f30220089afaa8b455bbeb7d9b9c3fe1ed37d07685ade8455c76472cda424d93e4074a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022026326fcdae9207b596c2b05921dbac11d81040c4d40378513670f19d9f4af893022034ecd7a282c0163b89aaa62c22ec202cef4736c58cd251649bad0d8139bcbf55012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71024730440220214978daeb2f38cd426ee6e2f44131a33d6b191af1c216247f1dd7d74c16d84a02205fdc05529b0bc0c430b4d5987264d9d075351c4f4484c16e91662e90a72aab24012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402204a6e9f199dc9672cf2ff8094aaa784363be1eb62b679f7ff2df361124f1dca3302205eeb11f70fab5355c9c8ad1a0700ea355d315e334822fa182227e9815308ee8f012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"],
+"0100000000010c00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff0001000000000000000000000000000000000000000000000000000000000000020000006a473044022026c2e65b33fcd03b2a3b0f25030f0244bd23cc45ae4dec0f48ae62255b1998a00220463aa3982b718d593a6b9e0044513fd67a5009c2fdccc59992cffc2b167889f4012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000030000006a4730440220008bd8382911218dcb4c9f2e75bf5c5c3635f2f2df49b36994fde85b0be21a1a02205a539ef10fb4c778b522c1be852352ea06c67ab74200977c722b0bc68972575a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000040000006b483045022100d9436c32ff065127d71e1a20e319e4fe0a103ba0272743dbd8580be4659ab5d302203fd62571ee1fe790b182d078ecfd092a509eac112bea558d122974ef9cc012c7012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000050000006a47304402200e2c149b114ec546015c13b2b464bbcb0cdc5872e6775787527af6cbc4830b6c02207e9396c6979fb15a9a2b96ca08a633866eaf20dc0ff3c03e512c1d5a1654f148012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000060000006b483045022100b20e70d897dc15420bccb5e0d3e208d27bdd676af109abbd3f88dbdb7721e6d6022005836e663173fbdfe069f54cde3c2decd3d0ea84378092a5d9d85ec8642e8a41012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff00010000000000000000000000000000000000000000000000000000000000000700000000ffffffff00010000000000000000000000000000000000000000000000000000000000000800000000ffffffff00010000000000000000000000000000000000000000000000000000000000000900000000ffffffff00010000000000000000000000000000000000000000000000000000000000000a00000000ffffffff00010000000000000000000000000000000000000000000000000000000000000b0000006a47304402206639c6e05e3b9d2675a7f3876286bdf7584fe2bbd15e0ce52dd4e02c0092cdc60220757d60b0a61fc95ada79d23746744c72bac1545a75ff6c2c7cdb6ae04e7e9592012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0ce8030000000000000151e9030000000000000151ea030000000000000151eb030000000000000151ec030000000000000151ed030000000000000151ee030000000000000151ef030000000000000151f0030000000000000151f1030000000000000151f2030000000000000151f30300000000000001510248304502210082219a54f61bf126bfc3fa068c6e33831222d1d7138c6faa9d33ca87fd4202d6022063f9902519624254d7c2c8ea7ba2d66ae975e4e229ae38043973ec707d5d4a83012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022017fb58502475848c1b09f162cb1688d0920ff7f142bed0ef904da2ccc88b168f02201798afa61850c65e77889cbcd648a5703b487895517c88f85cdd18b021ee246a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000000247304402202830b7926e488da75782c81a54cd281720890d1af064629ebf2e31bf9f5435f30220089afaa8b455bbeb7d9b9c3fe1ed37d07685ade8455c76472cda424d93e4074a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022026326fcdae9207b596c2b05921dbac11d81040c4d40378513670f19d9f4af893022034ecd7a282c0163b89aaa62c22ec202cef4736c58cd251649bad0d8139bcbf55012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71024730440220214978daeb2f38cd426ee6e2f44131a33d6b191af1c216247f1dd7d74c16d84a02205fdc05529b0bc0c430b4d5987264d9d075351c4f4484c16e91662e90a72aab24012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402204a6e9f199dc9672cf2ff8094aaa784363be1eb62b679f7ff2df361124f1dca3302205eeb11f70fab5355c9c8ad1a0700ea355d315e334822fa182227e9815308ee8f012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"],
["Unknown version witness program with empty witness"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
["Witness SIGHASH_SINGLE with output out of bound"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x20 0x4d6c2a32c87821d68fc016fca70797abdb80df6cd84651d40a9300c6bad79e62", 1000]],
-"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff01d00700000000000001510003483045022100e078de4e96a0e05dcdc0a414124dd8475782b5f3f0ed3f607919e9a5eeeb22bf02201de309b3a3109adb3de8074b3610d4cf454c49b61247a2779a0bcbf31c889333032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc711976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac00000000", "P2SH,WITNESS"],
+"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff01d00700000000000001510003483045022100e078de4e96a0e05dcdc0a414124dd8475782b5f3f0ed3f607919e9a5eeeb22bf02201de309b3a3109adb3de8074b3610d4cf454c49b61247a2779a0bcbf31c889333032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc711976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac00000000", "NONE"],
["1 byte push should not be considered a witness scriptPubKey"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x01 0x01", 1000]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"],
["41 bytes push should not be considered a witness scriptPubKey"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x29 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff0000000000000000", 1000]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "CLEANSTACK"],
["The witness version must use OP_1 to OP_16 only"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x01 0x10 0x02 0x0001", 1000]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"],
["The witness program push must be canonical"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x4c02 0x0001", 1000]],
-"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"],
["Witness Single|AnyoneCanPay does not hash input's position"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001]],
-"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
+"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"],
["Witness Single|AnyoneCanPay does not hash input's position (permutation)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001],
["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]],
-"0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"],
+"0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"],
["Non witness Single|AnyoneCanPay hash input's position"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1000],
["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1001]],
-"01000000020001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff00010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff02e8030000000000000151e903000000000000015100000000", "P2SH,WITNESS"],
+"01000000020001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff00010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff02e8030000000000000151e903000000000000015100000000", "NONE"],
["BIP143 examples: details and private keys are available in BIP143"],
["BIP143 example: P2WSH with OP_CODESEPARATOR and out-of-range SIGHASH_SINGLE."],
[[["6eb316926b1c5d567cd6f5e6a84fec606fc53d7b474526d1fff3948020c93dfe", 0, "0x21 0x036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8 CHECKSIG", 156250000],
["f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", 0, "0x00 0x20 0x5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", 4900000000]],
-"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
+"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "NONE"],
["BIP143 example: P2WSH with unexecuted OP_CODESEPARATOR and SINGLE|ANYONECANPAY"],
[[["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215],
["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]],
-"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
+"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "NONE"],
["BIP143 example: Same as the previous example with input-output pairs swapped"],
[[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215],
["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]],
-"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
+"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "NONE"],
["BIP143 example: P2SH-P2WSH 6-of-6 multisig signed with 6 different SIGHASH types"],
[[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]],
-"0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "P2SH,WITNESS"],
+"0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "NONE"],
["FindAndDelete tests"],
["This is a test of FindAndDelete. The first tx is a spend of normal P2SH and the second tx is a spend of bare P2WSH."],
@@ -501,24 +501,24 @@
["This is to show that FindAndDelete is applied only to non-segwit scripts"],
["Non-segwit: correct sighash (with FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
-"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "CONST_SCRIPTCODE,LOW_S"],
["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
-"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "LOW_S"],
["This is multisig version of the FindAndDelete tests"],
["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
["Non-segwit: correct sighash (with FindAndDelete) = 1d50f00ba4db2917b903b0ec5002e017343bb38876398c9510570f5dce099295"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
-"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "CONST_SCRIPTCODE,LOW_S"],
["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"],
[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
-"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS,CONST_SCRIPTCODE"],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "LOW_S"],
["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."],
[[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]],
-"0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "P2SH,WITNESS"],
+"0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "NONE"],
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 3d802cbeb3..a6080ad3dd 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -5,7 +5,6 @@
#include <dbwrapper.h>
#include <test/util/setup_common.h>
#include <uint256.h>
-#include <util/memory.h>
#include <memory>
@@ -207,7 +206,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
char key = 'k';
uint256 in = InsecureRand256();
uint256 res;
@@ -248,7 +247,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
- std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false);
+ std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
char key = 'k';
uint256 in = InsecureRand256();
uint256 res;
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index cf6009d591..e75982bc6f 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,7 +14,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
-#include <util/memory.h>
+#include <txorphanage.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
@@ -43,18 +43,6 @@ struct CConnmanTest : public CConnman {
}
};
-// Tests these internal-to-net_processing.cpp methods:
-extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
-extern void EraseOrphansFor(NodeId peer);
-extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
-
-struct COrphanTx {
- CTransactionRef tx;
- NodeId fromPeer;
- int64_t nTimeExpire;
-};
-extern std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -79,13 +67,13 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
const CChainParams& chainparams = Params();
- auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
+ auto connman = std::make_unique<CConnman>(0x1337, 0x1337);
auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler,
*m_node.chainman, *m_node.mempool, false);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY);
+ CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
@@ -136,7 +124,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
- vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY));
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
CNode &node = *vNodes.back();
node.SetCommonVersion(PROTOCOL_VERSION);
@@ -149,7 +137,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const CChainParams& chainparams = Params();
- auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
+ auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337);
auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler,
*m_node.chainman, *m_node.mempool, false);
@@ -222,14 +210,14 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
- auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
+ auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto connman = std::make_unique<CConnman>(0x1337, 0x1337);
auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler,
*m_node.chainman, *m_node.mempool, false);
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode1(id++, NODE_NETWORK, INVALID_SOCKET, addr1, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.fSuccessfullyConnected = true;
@@ -242,7 +230,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
- CNode dummyNode2(id++, NODE_NETWORK, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode2(id++, NODE_NETWORK, INVALID_SOCKET, addr2, /* nKeyedNetGroupIn */ 1, /* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.fSuccessfullyConnected = true;
@@ -269,8 +257,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
const CChainParams& chainparams = Params();
- auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
+ auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto connman = std::make_unique<CConnman>(0x1337, 0x1337);
auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler,
*m_node.chainman, *m_node.mempool, false);
@@ -279,7 +267,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, 4, 4, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 4, /* nLocalHostNonceIn */ 4, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode);
dummyNode.fSuccessfullyConnected = true;
@@ -295,15 +283,23 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
peerLogic->FinalizeNode(dummyNode, dummy);
}
-static CTransactionRef RandomOrphan()
+class TxOrphanageTest : public TxOrphanage
{
- std::map<uint256, COrphanTx>::iterator it;
- LOCK2(cs_main, g_cs_orphans);
- it = mapOrphanTransactions.lower_bound(InsecureRand256());
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- return it->second.tx;
-}
+public:
+ inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+ {
+ return m_orphans.size();
+ }
+
+ CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
+ {
+ std::map<uint256, OrphanTx>::iterator it;
+ it = m_orphans.lower_bound(InsecureRand256());
+ if (it == m_orphans.end())
+ it = m_orphans.begin();
+ return it->second.tx;
+ }
+};
static void MakeNewKeyWithFastRandomContext(CKey& key)
{
@@ -323,11 +319,14 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
// signature's R and S values have leading zeros.
g_insecure_rand_ctx = FastRandomContext(ArithToUint256(arith_uint256(33)));
+ TxOrphanageTest orphanage;
CKey key;
MakeNewKeyWithFastRandomContext(key);
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
+ LOCK(g_cs_orphans);
+
// 50 orphan transactions:
for (int i = 0; i < 50; i++)
{
@@ -340,13 +339,13 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
- AddOrphanTx(MakeTransactionRef(tx), i);
+ orphanage.AddTx(MakeTransactionRef(tx), i);
}
// ... and 50 that depend on other orphans:
for (int i = 0; i < 50; i++)
{
- CTransactionRef txPrev = RandomOrphan();
+ CTransactionRef txPrev = orphanage.RandomOrphan();
CMutableTransaction tx;
tx.vin.resize(1);
@@ -357,13 +356,13 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL));
- AddOrphanTx(MakeTransactionRef(tx), i);
+ orphanage.AddTx(MakeTransactionRef(tx), i);
}
// This really-big orphan should be ignored:
for (int i = 0; i < 10; i++)
{
- CTransactionRef txPrev = RandomOrphan();
+ CTransactionRef txPrev = orphanage.RandomOrphan();
CMutableTransaction tx;
tx.vout.resize(1);
@@ -381,25 +380,24 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
for (unsigned int j = 1; j < tx.vin.size(); j++)
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
- BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i));
+ BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
}
- LOCK2(cs_main, g_cs_orphans);
// Test EraseOrphansFor:
for (NodeId i = 0; i < 3; i++)
{
- size_t sizeBefore = mapOrphanTransactions.size();
- EraseOrphansFor(i);
- BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore);
+ size_t sizeBefore = orphanage.CountOrphans();
+ orphanage.EraseForPeer(i);
+ BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
}
// Test LimitOrphanTxSize() function:
- LimitOrphanTxSize(40);
- BOOST_CHECK(mapOrphanTransactions.size() <= 40);
- LimitOrphanTxSize(10);
- BOOST_CHECK(mapOrphanTransactions.size() <= 10);
- LimitOrphanTxSize(0);
- BOOST_CHECK(mapOrphanTransactions.empty());
+ orphanage.LimitOrphans(40);
+ BOOST_CHECK(orphanage.CountOrphans() <= 40);
+ orphanage.LimitOrphans(10);
+ BOOST_CHECK(orphanage.CountOrphans() <= 10);
+ orphanage.LimitOrphans(0);
+ BOOST_CHECK(orphanage.CountOrphans() == 0);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 20132d5782..aecf955fee 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -10,6 +10,7 @@
#include <boost/test/unit_test.hpp>
+#include <optional>
#include <string>
#include <vector>
@@ -65,7 +66,7 @@ std::string UseHInsteadOfApostrophe(const std::string& desc)
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
-void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const Optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
+void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, 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;
@@ -112,6 +113,17 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
BOOST_CHECK(EqualDescriptor(prv, prv1));
BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));
+ // Check that private can produce the normalized descriptors
+ std::string norm1;
+ BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, false));
+ BOOST_CHECK(EqualDescriptor(norm1, norm_pub));
+ BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, false));
+ BOOST_CHECK(EqualDescriptor(norm1, norm_pub));
+ BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, true));
+ BOOST_CHECK(EqualDescriptor(norm1, norm_prv));
+ BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, true));
+ BOOST_CHECK(EqualDescriptor(norm1, norm_prv));
+
// Check whether IsRange on both returns the expected result
BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0);
BOOST_CHECK_EQUAL(parse_priv->IsRange(), (flags & RANGE) != 0);
@@ -251,29 +263,29 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
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 Optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
+void Check(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, 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, type, paths);
+ DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, 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, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
+ DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, 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, type, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, 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, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
+ DoCheck(prv, pub, norm_prv, norm_pub, flags, scripts, type, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
}
}
@@ -284,50 +296,51 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(descriptor_test)
{
// Basic single-key compressed
- Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, nullopt);
- Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, nullopt);
- Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}});
- Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
- Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
+ Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt);
+ Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt);
+ Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}});
+ Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32);
+ Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT);
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"}}, nullopt);
- Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, nullopt);
- Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY);
+ Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt);
+ Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt);
+ Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY);
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"}}, OutputType::LEGACY);
- Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY);
- Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}}, OutputType::BECH32);
- Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
- Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
- Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
+ Check("sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141857af51a5e516552b3086430fd8ce55f7c1a52487"}}, OutputType::LEGACY);
+ Check("sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a9141a31ad23bf49c247dd531a623c2ef57da3c400c587"}}, OutputType::LEGACY);
+ Check("wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa"}}, OutputType::BECH32);
+ Check("wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b"}}, OutputType::BECH32);
+ Check("sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a91472d0c5a3bfad8c3e7bd5303a72b94240e80b6f1787"}}, OutputType::P2SH_SEGWIT);
+ Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT);
// Versions with BIP32 derivations
- Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, nullopt);
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, nullopt, {{0}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}});
- Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{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 | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{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"}}, nullopt, {{0}, {1}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt);
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}});
+ Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{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/*'))", "sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
+ Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}});
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
+ Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([01234567/10/20/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}});
// Multisig constructions
- Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
- Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
- Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt);
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
- Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
- 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 | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{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"}}, OutputType::P2SH_SEGWIT);
+ Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt);
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}});
+ 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/*'))", "wsh(multi(2,[bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{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)))", "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"}}, OutputType::P2SH_SEGWIT);
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
@@ -350,8 +363,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
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"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
- Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}});
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
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index ec487aa3ff..e52cd5230c 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -5,6 +5,7 @@
#include <fs.h>
#include <test/util/setup_common.h>
#include <util/system.h>
+#include <util/getuniquepath.h>
#include <boost/test/unit_test.hpp>
@@ -69,6 +70,21 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, ""));
BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, {}));
}
+ {
+ fs::path p1 = GetUniquePath(tmpfolder);
+ fs::path p2 = GetUniquePath(tmpfolder);
+ fs::path p3 = GetUniquePath(tmpfolder);
+
+ // Ensure that the parent path is always the same.
+ BOOST_CHECK_EQUAL(tmpfolder, p1.parent_path());
+ BOOST_CHECK_EQUAL(tmpfolder, p2.parent_path());
+ BOOST_CHECK_EQUAL(tmpfolder, p3.parent_path());
+
+ // Ensure that generated paths are actually different.
+ BOOST_CHECK(p1 != p2);
+ BOOST_CHECK(p2 != p3);
+ BOOST_CHECK(p1 != p3);
+ }
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file
diff --git a/src/test/fuzz/FuzzedDataProvider.h b/src/test/fuzz/FuzzedDataProvider.h
index 744a9d78ce..6cbfc39bc2 100644
--- a/src/test/fuzz/FuzzedDataProvider.h
+++ b/src/test/fuzz/FuzzedDataProvider.h
@@ -20,6 +20,7 @@
#include <cstdint>
#include <cstring>
#include <initializer_list>
+#include <limits>
#include <string>
#include <type_traits>
#include <utility>
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 1ea6b3d01d..b55f1c72b1 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp
index 8b7822dc16..8ca4de3919 100644
--- a/src/test/fuzz/asmap_direct.cpp
+++ b/src/test/fuzz/asmap_direct.cpp
@@ -2,8 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/fuzz/fuzz.h>
+#include <netaddress.h>
#include <util/asmap.h>
+#include <test/fuzz/fuzz.h>
#include <cstdint>
#include <optional>
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
index 9ecd172e19..479342e4be 100644
--- a/src/test/fuzz/autofile.cpp
+++ b/src/test/fuzz/autofile.cpp
@@ -1,8 +1,7 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 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 <optional.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index e0715f3e29..8bf484722c 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -8,6 +8,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <cstdint>
@@ -26,7 +27,7 @@ int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept
void initialize_banman()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(banman, initialize_banman)
diff --git a/src/test/fuzz/bech32.cpp b/src/test/fuzz/bech32.cpp
index 95cd4b413f..ad3bf73af4 100644
--- a/src/test/fuzz/bech32.cpp
+++ b/src/test/fuzz/bech32.cpp
@@ -16,28 +16,28 @@
FUZZ_TARGET(bech32)
{
const std::string random_string(buffer.begin(), buffer.end());
- const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string);
- if (r1.first.empty()) {
- assert(r1.second.empty());
+ const auto r1 = bech32::Decode(random_string);
+ if (r1.hrp.empty()) {
+ assert(r1.encoding == bech32::Encoding::INVALID);
+ assert(r1.data.empty());
} else {
- const std::string& hrp = r1.first;
- const std::vector<uint8_t>& data = r1.second;
- const std::string reencoded = bech32::Encode(hrp, data);
+ assert(r1.encoding != bech32::Encoding::INVALID);
+ const std::string reencoded = bech32::Encode(r1.encoding, r1.hrp, r1.data);
assert(CaseInsensitiveEqual(random_string, reencoded));
}
std::vector<unsigned char> input;
ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
- const std::string encoded = bech32::Encode("bc", input);
- assert(!encoded.empty());
- const std::pair<std::string, std::vector<uint8_t>> r2 = bech32::Decode(encoded);
- if (r2.first.empty()) {
- assert(r2.second.empty());
- } else {
- const std::string& hrp = r2.first;
- const std::vector<uint8_t>& data = r2.second;
- assert(hrp == "bc");
- assert(data == input);
+ if (input.size() + 3 + 6 <= 90) {
+ // If it's possible to encode input in Bech32(m) without exceeding the 90-character limit:
+ for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
+ const std::string encoded = bech32::Encode(encoding, "bc", input);
+ assert(!encoded.empty());
+ const auto r2 = bech32::Decode(encoded);
+ assert(r2.encoding == encoding);
+ assert(r2.hrp == "bc");
+ assert(r2.data == input);
+ }
}
}
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index d43c182644..c5bb8744a4 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index 3a1b2dbbe7..ed72260d10 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -1,8 +1,7 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 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 <optional.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 8ece94d771..d951bda20f 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,6 +16,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <cstdint>
@@ -36,7 +37,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -263,7 +264,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats;
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 71b4b00116..ae77a45e44 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,6 +10,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <util/translation.h>
#include <cstdint>
@@ -17,7 +18,7 @@
void initialize_connman()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(connman, initialize_connman)
@@ -94,7 +95,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
(void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
},
[&] {
- (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL}));
+ (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({ConnectionDirection::None, ConnectionDirection::In, ConnectionDirection::Out, ConnectionDirection::Both}));
},
[&] {
connman.MarkAddressGood(random_address);
@@ -104,7 +105,9 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
},
[&] {
// Limit now to int32_t to avoid signed integer overflow
- (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>());
+ (void)connman.PoissonNextSendInbound(
+ std::chrono::microseconds{fuzzed_data_provider.ConsumeIntegral<int32_t>()},
+ std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<int>()});
},
[&] {
CSerializedNetMsg serialized_net_msg;
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index c2bb3a1a4e..eeeac18968 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -1,10 +1,9 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 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 <crypto/hmac_sha256.h>
#include <crypto/hmac_sha512.h>
-#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -36,7 +35,6 @@ FUZZ_TARGET(crypto)
CSHA512 sha512;
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
- MuHash3072 muhash;
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
@@ -63,12 +61,6 @@ FUZZ_TARGET(crypto)
(void)Hash(data);
(void)Hash160(data);
(void)sha512.Size();
-
- if (fuzzed_data_provider.ConsumeBool()) {
- muhash *= MuHash3072(data);
- } else {
- muhash /= MuHash3072(data);
- }
},
[&] {
(void)hash160.Reset();
@@ -78,7 +70,6 @@ FUZZ_TARGET(crypto)
(void)sha256.Reset();
(void)sha3.Reset();
(void)sha512.Reset();
- muhash = MuHash3072();
},
[&] {
CallOneOf(
@@ -122,10 +113,6 @@ FUZZ_TARGET(crypto)
[&] {
data.resize(SHA3_256::OUTPUT_SIZE);
sha3.Finalize(data);
- },
- [&] {
- uint256 out;
- muhash.Finalize(out);
});
});
}
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index bb8dd4594f..8adfa92420 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
index 0e1c44cded..bb4ef22158 100644
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index f3b6e6af04..473caec6ff 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,13 +7,14 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <vector>
void initialize_data_stream_addr_man()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp
index 0d1921f285..ffe4855662 100644
--- a/src/test/fuzz/descriptor_parse.cpp
+++ b/src/test/fuzz/descriptor_parse.cpp
@@ -6,7 +6,6 @@
#include <pubkey.h>
#include <script/descriptor.h>
#include <test/fuzz/fuzz.h>
-#include <util/memory.h>
void initialize_descriptor_parse()
{
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 74dec6475e..1290c78712 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,6 @@
#include <net.h>
#include <netbase.h>
#include <node/utxo_snapshot.h>
-#include <optional.h>
#include <primitives/block.h>
#include <protocol.h>
#include <psbt.h>
@@ -26,12 +25,11 @@
#include <version.h>
#include <exception>
+#include <optional>
#include <stdexcept>
#include <stdint.h>
#include <unistd.h>
-#include <vector>
-
#include <test/fuzz/fuzz.h>
void initialize_deserialize()
@@ -71,7 +69,7 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, const Optional<int> protocol_version = nullopt)
+void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt)
{
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
if (protocol_version) {
diff --git a/src/test/fuzz/eval_script.cpp b/src/test/fuzz/eval_script.cpp
index 635288fc36..77ed798923 100644
--- a/src/test/fuzz/eval_script.cpp
+++ b/src/test/fuzz/eval_script.cpp
@@ -6,7 +6,6 @@
#include <script/interpreter.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
-#include <util/memory.h>
#include <limits>
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index fd87667755..1fab46ff13 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,15 +13,15 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>>& FuzzTargets()
+std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets()
{
- static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>> g_fuzz_targets;
+ static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets;
return g_fuzz_targets;
}
-void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init)
+void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden)
{
- const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init));
+ const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init), hidden);
Assert(it_ins.second);
}
@@ -31,6 +31,7 @@ void initialize()
{
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
+ if (std::get<2>(t.second)) continue;
std::cout << t.first << std::endl;
}
Assert(false);
@@ -43,7 +44,7 @@ void initialize()
std::get<1>(it->second)();
}
-#if defined(PROVIDE_MAIN_FUNCTION)
+#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
uint8_t buffer[1024];
@@ -59,8 +60,7 @@ static bool read_stdin(std::vector<uint8_t>& data)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
static const auto& test_one_input = *Assert(g_test_one_input);
- const std::vector<uint8_t> input(data, data + size);
- test_one_input(input);
+ test_one_input({data, size});
return 0;
}
@@ -71,8 +71,8 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
return 0;
}
-#if defined(PROVIDE_MAIN_FUNCTION)
-__attribute__((weak)) int main(int argc, char** argv)
+#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
+int main(int argc, char** argv)
{
initialize();
static const auto& test_one_input = *Assert(g_test_one_input);
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 52841e069a..2bad77bdc1 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -1,33 +1,40 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-2021 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_TEST_FUZZ_FUZZ_H
#define BITCOIN_TEST_FUZZ_FUZZ_H
+#include <span.h>
+
#include <cstdint>
#include <functional>
#include <string_view>
-#include <vector>
-using TypeTestOneInput = std::function<void(const std::vector<uint8_t>&)>;
+using FuzzBufferType = Span<const uint8_t>;
+
+using TypeTestOneInput = std::function<void(FuzzBufferType)>;
using TypeInitialize = std::function<void()>;
+using TypeHidden = bool;
-void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init);
+void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden);
-inline void FuzzFrameworkEmptyFun() {}
+inline void FuzzFrameworkEmptyInitFun() {}
#define FUZZ_TARGET(name) \
- FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyFun)
-
-#define FUZZ_TARGET_INIT(name, init_fun) \
- void name##_fuzz_target(const std::vector<uint8_t>&); \
- struct name##_Before_Main { \
- name##_Before_Main() \
- { \
- FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun); \
- } \
- } const static g_##name##_before_main; \
- void name##_fuzz_target(const std::vector<uint8_t>& buffer)
+ FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyInitFun)
+
+#define FUZZ_TARGET_INIT(name, init_fun) \
+ FUZZ_TARGET_INIT_HIDDEN(name, init_fun, false)
+
+#define FUZZ_TARGET_INIT_HIDDEN(name, init_fun, hidden) \
+ void name##_fuzz_target(FuzzBufferType); \
+ struct name##_Before_Main { \
+ name##_Before_Main() \
+ { \
+ FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun, hidden); \
+ } \
+ } const static g_##name##_before_main; \
+ void name##_fuzz_target(FuzzBufferType buffer)
#endif // BITCOIN_TEST_FUZZ_FUZZ_H
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index ac83d91ea0..5bc99ddcb9 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -84,8 +84,7 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
(void)DecompressAmount(u64);
(void)FormatISO8601Date(i64);
(void)FormatISO8601DateTime(i64);
- // FormatMoney(i) not defined when i == std::numeric_limits<int64_t>::min()
- if (i64 != std::numeric_limits<int64_t>::min()) {
+ {
int64_t parsed_money;
if (ParseMoney(FormatMoney(i64), parsed_money)) {
assert(parsed_money == i64);
@@ -132,8 +131,7 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
(void)SipHashUint256Extra(u64, u64, u256, u32);
(void)ToLower(ch);
(void)ToUpper(ch);
- // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
- if (i64 != std::numeric_limits<int64_t>::min()) {
+ {
int64_t parsed_money;
if (ParseMoney(ValueFromAmount(i64).getValStr(), parsed_money)) {
assert(parsed_money == i64);
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
index aa8f826e4a..32077b1fe2 100644
--- a/src/test/fuzz/key.cpp
+++ b/src/test/fuzz/key.cpp
@@ -17,7 +17,6 @@
#include <script/standard.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
-#include <util/memory.h>
#include <util/strencodings.h>
#include <cassert>
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index fa4024fc38..908e9a1c83 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index 95597bf082..dbd0c76d42 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,7 @@
void initialize_load_external_block_file()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp
index 23e0baa564..1eefd4c521 100644
--- a/src/test/fuzz/merkleblock.cpp
+++ b/src/test/fuzz/merkleblock.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index 8f843ca773..4ea9511870 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -41,6 +41,11 @@ FUZZ_TARGET(muhash)
muhash.Finalize(out2);
assert(out == out2);
+ MuHash3072 muhash3;
+ muhash3 *= muhash;
+ uint256 out3;
+ muhash3.Finalize(out3);
+ assert(out == out3);
// Test that removing all added elements brings the object back to it's initial state
muhash /= muhash;
@@ -50,4 +55,9 @@ FUZZ_TARGET(muhash)
muhash2.Finalize(out2);
assert(out == out2);
+
+ muhash3.Remove(data);
+ muhash3.Remove(data2);
+ muhash3.Finalize(out3);
+ assert(out == out3);
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 21dca4eb05..272f6415a9 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,7 +7,6 @@
#include <net.h>
#include <net_permissions.h>
#include <netaddress.h>
-#include <optional.h>
#include <protocol.h>
#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -17,12 +16,13 @@
#include <test/util/setup_common.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
void initialize_net()
{
- static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::MAIN);
+ static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN);
}
FUZZ_TARGET_INIT(net, initialize_net)
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index 6e9bb47ff6..f9d8129ca9 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,6 @@
#include <cassert>
#include <cstdint>
-#include <netinet/in.h>
#include <vector>
FUZZ_TARGET(netaddress)
diff --git a/src/test/fuzz/netbase_dns_lookup.cpp b/src/test/fuzz/netbase_dns_lookup.cpp
new file mode 100644
index 0000000000..cf2fa33744
--- /dev/null
+++ b/src/test/fuzz/netbase_dns_lookup.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2021 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 <netaddress.h>
+#include <netbase.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+FUZZ_TARGET(netbase_dns_lookup)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512);
+ const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
+ const bool allow_lookup = fuzzed_data_provider.ConsumeBool();
+ const uint16_t default_port = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
+
+ auto fuzzed_dns_lookup_function = [&](const std::string&, bool) {
+ std::vector<CNetAddr> resolved_addresses;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ resolved_addresses.push_back(ConsumeNetAddr(fuzzed_data_provider));
+ }
+ return resolved_addresses;
+ };
+
+ {
+ std::vector<CNetAddr> resolved_addresses;
+ if (LookupHost(name, resolved_addresses, max_results, allow_lookup, fuzzed_dns_lookup_function)) {
+ for (const CNetAddr& resolved_address : resolved_addresses) {
+ assert(!resolved_address.IsInternal());
+ }
+ }
+ assert(resolved_addresses.size() <= max_results || max_results == 0);
+ }
+ {
+ CNetAddr resolved_address;
+ if (LookupHost(name, resolved_address, allow_lookup, fuzzed_dns_lookup_function)) {
+ assert(!resolved_address.IsInternal());
+ }
+ }
+ {
+ std::vector<CService> resolved_services;
+ if (Lookup(name, resolved_services, default_port, allow_lookup, max_results, fuzzed_dns_lookup_function)) {
+ for (const CNetAddr& resolved_service : resolved_services) {
+ assert(!resolved_service.IsInternal());
+ }
+ }
+ assert(resolved_services.size() <= max_results || max_results == 0);
+ }
+ {
+ CService resolved_service;
+ if (Lookup(name, resolved_service, default_port, allow_lookup, fuzzed_dns_lookup_function)) {
+ assert(!resolved_service.IsInternal());
+ }
+ }
+ {
+ CService resolved_service = LookupNumeric(name, default_port, fuzzed_dns_lookup_function);
+ assert(!resolved_service.IsInternal());
+ }
+ {
+ CSubNet resolved_subnet;
+ if (LookupSubNet(name, resolved_subnet, fuzzed_dns_lookup_function)) {
+ assert(resolved_subnet.IsValid());
+ }
+ }
+}
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index aaebe83c0a..603d520cf5 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <net.h>
-#include <optional.h>
#include <protocol.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -21,23 +20,23 @@ FUZZ_TARGET(node_eviction)
std::vector<NodeEvictionCandidate> eviction_candidates;
while (fuzzed_data_provider.ConsumeBool()) {
eviction_candidates.push_back({
- fuzzed_data_provider.ConsumeIntegral<NodeId>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeIntegral<int64_t>(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
- fuzzed_data_provider.ConsumeBool(),
- fuzzed_data_provider.ConsumeBool(),
+ /* id */ fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ /* nTimeConnected */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* m_min_ping_time */ std::chrono::microseconds{fuzzed_data_provider.ConsumeIntegral<int64_t>()},
+ /* nLastBlockTime */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* nLastTXTime */ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ /* fRelevantServices */ fuzzed_data_provider.ConsumeBool(),
+ /* fRelayTxes */ fuzzed_data_provider.ConsumeBool(),
+ /* fBloomFilter */ fuzzed_data_provider.ConsumeBool(),
+ /* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ /* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
+ /* m_is_local */ fuzzed_data_provider.ConsumeBool(),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
// indeterminate state after the SelectNodeToEvict(&&) call.
const std::vector<NodeEvictionCandidate> eviction_candidates_copy = eviction_candidates;
- const Optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates));
+ const std::optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates));
if (node_to_evict) {
assert(std::any_of(eviction_candidates_copy.begin(), eviction_candidates_copy.end(), [&node_to_evict](const NodeEvictionCandidate& eviction_candidate) { return *node_to_evict == eviction_candidate.id; }));
}
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
index 163f1b839e..3a1fdaad8f 100644
--- a/src/test/fuzz/p2p_transport_deserializer.cpp
+++ b/src/test/fuzz/p2p_transport_deserializer.cpp
@@ -10,6 +10,7 @@
#include <cassert>
#include <cstdint>
#include <limits>
+#include <optional>
#include <vector>
void initialize_p2p_transport_deserializer()
@@ -30,7 +31,7 @@ FUZZ_TARGET_INIT(p2p_transport_deserializer, initialize_p2p_transport_deserializ
if (deserializer.Complete()) {
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
uint32_t out_err_raw_size{0};
- Optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)};
+ std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)};
if (result) {
assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE);
assert(result->m_raw_message_size <= buffer.size());
diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp
index ddd2bcfba3..1ad5fb6a05 100644
--- a/src/test/fuzz/parse_numbers.cpp
+++ b/src/test/fuzz/parse_numbers.cpp
@@ -18,6 +18,9 @@ FUZZ_TARGET(parse_numbers)
double d;
(void)ParseDouble(random_string, &d);
+ uint8_t u8;
+ (void)ParseUInt8(random_string, &u8);
+
int32_t i32;
(void)ParseInt32(random_string, &i32);
(void)atoi(random_string);
diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp
index afe382ba21..3fffaac8d0 100644
--- a/src/test/fuzz/parse_univalue.cpp
+++ b/src/test/fuzz/parse_univalue.cpp
@@ -7,7 +7,6 @@
#include <rpc/client.h>
#include <rpc/util.h>
#include <test/fuzz/fuzz.h>
-#include <util/memory.h>
#include <limits>
#include <string>
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index fff893fb3f..116b7a71d9 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <txmempool.h>
#include <cstdint>
@@ -16,7 +17,7 @@
void initialize_policy_estimator()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator)
diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp
index 73242870a0..9021d95954 100644
--- a/src/test/fuzz/policy_estimator_io.cpp
+++ b/src/test/fuzz/policy_estimator_io.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,13 +6,14 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <vector>
void initialize_policy_estimator_io()
{
- static const auto testing_setup = MakeFuzzingContext<>();
+ static const auto testing_setup = MakeNoLogFileContext<>();
}
FUZZ_TARGET_INIT(policy_estimator_io, initialize_policy_estimator_io)
diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp
index c4348495bf..53726ca893 100644
--- a/src/test/fuzz/pow.cpp
+++ b/src/test/fuzz/pow.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index e7cc0f5297..04fc6da9b1 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -18,7 +18,7 @@
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
-#include <util/memory.h>
+#include <txorphanage.h>
#include <validationinterface.h>
#include <version.h>
@@ -30,15 +30,33 @@
#include <iostream>
#include <memory>
#include <string>
-#include <vector>
namespace {
const TestingSetup* g_setup;
} // namespace
+size_t& GetNumMsgTypes()
+{
+ static size_t g_num_msg_types{0};
+ return g_num_msg_types;
+}
+#define FUZZ_TARGET_MSG(msg_type) \
+ struct msg_type##_Count_Before_Main { \
+ msg_type##_Count_Before_Main() \
+ { \
+ ++GetNumMsgTypes(); \
+ } \
+ } const static g_##msg_type##_count_before_main; \
+ FUZZ_TARGET_INIT(process_message_##msg_type, initialize_process_message) \
+ { \
+ fuzz_target(buffer, #msg_type); \
+ }
+
void initialize_process_message()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ Assert(GetNumMsgTypes() == getAllNetMessageTypes().size()); // If this fails, add or remove the message type below
+
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
@@ -46,7 +64,7 @@ void initialize_process_message()
SyncWithValidationInterfaceQueue();
}
-void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO_MESSAGE_TYPE)
+void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -61,7 +79,7 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO
}
CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release();
- const bool successfully_connected{true};
+ const bool successfully_connected{fuzzed_data_provider.ConsumeBool()};
p2p_node.fSuccessfullyConnected = successfully_connected;
connman.AddTestNode(p2p_node);
g_setup->m_node.peerman->InitializeNode(&p2p_node);
@@ -87,27 +105,37 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO
}
FUZZ_TARGET_INIT(process_message, initialize_process_message) { fuzz_target(buffer, ""); }
-FUZZ_TARGET_INIT(process_message_addr, initialize_process_message) { fuzz_target(buffer, "addr"); }
-FUZZ_TARGET_INIT(process_message_block, initialize_process_message) { fuzz_target(buffer, "block"); }
-FUZZ_TARGET_INIT(process_message_blocktxn, initialize_process_message) { fuzz_target(buffer, "blocktxn"); }
-FUZZ_TARGET_INIT(process_message_cmpctblock, initialize_process_message) { fuzz_target(buffer, "cmpctblock"); }
-FUZZ_TARGET_INIT(process_message_feefilter, initialize_process_message) { fuzz_target(buffer, "feefilter"); }
-FUZZ_TARGET_INIT(process_message_filteradd, initialize_process_message) { fuzz_target(buffer, "filteradd"); }
-FUZZ_TARGET_INIT(process_message_filterclear, initialize_process_message) { fuzz_target(buffer, "filterclear"); }
-FUZZ_TARGET_INIT(process_message_filterload, initialize_process_message) { fuzz_target(buffer, "filterload"); }
-FUZZ_TARGET_INIT(process_message_getaddr, initialize_process_message) { fuzz_target(buffer, "getaddr"); }
-FUZZ_TARGET_INIT(process_message_getblocks, initialize_process_message) { fuzz_target(buffer, "getblocks"); }
-FUZZ_TARGET_INIT(process_message_getblocktxn, initialize_process_message) { fuzz_target(buffer, "getblocktxn"); }
-FUZZ_TARGET_INIT(process_message_getdata, initialize_process_message) { fuzz_target(buffer, "getdata"); }
-FUZZ_TARGET_INIT(process_message_getheaders, initialize_process_message) { fuzz_target(buffer, "getheaders"); }
-FUZZ_TARGET_INIT(process_message_headers, initialize_process_message) { fuzz_target(buffer, "headers"); }
-FUZZ_TARGET_INIT(process_message_inv, initialize_process_message) { fuzz_target(buffer, "inv"); }
-FUZZ_TARGET_INIT(process_message_mempool, initialize_process_message) { fuzz_target(buffer, "mempool"); }
-FUZZ_TARGET_INIT(process_message_notfound, initialize_process_message) { fuzz_target(buffer, "notfound"); }
-FUZZ_TARGET_INIT(process_message_ping, initialize_process_message) { fuzz_target(buffer, "ping"); }
-FUZZ_TARGET_INIT(process_message_pong, initialize_process_message) { fuzz_target(buffer, "pong"); }
-FUZZ_TARGET_INIT(process_message_sendcmpct, initialize_process_message) { fuzz_target(buffer, "sendcmpct"); }
-FUZZ_TARGET_INIT(process_message_sendheaders, initialize_process_message) { fuzz_target(buffer, "sendheaders"); }
-FUZZ_TARGET_INIT(process_message_tx, initialize_process_message) { fuzz_target(buffer, "tx"); }
-FUZZ_TARGET_INIT(process_message_verack, initialize_process_message) { fuzz_target(buffer, "verack"); }
-FUZZ_TARGET_INIT(process_message_version, initialize_process_message) { fuzz_target(buffer, "version"); }
+FUZZ_TARGET_MSG(addr);
+FUZZ_TARGET_MSG(addrv2);
+FUZZ_TARGET_MSG(block);
+FUZZ_TARGET_MSG(blocktxn);
+FUZZ_TARGET_MSG(cfcheckpt);
+FUZZ_TARGET_MSG(cfheaders);
+FUZZ_TARGET_MSG(cfilter);
+FUZZ_TARGET_MSG(cmpctblock);
+FUZZ_TARGET_MSG(feefilter);
+FUZZ_TARGET_MSG(filteradd);
+FUZZ_TARGET_MSG(filterclear);
+FUZZ_TARGET_MSG(filterload);
+FUZZ_TARGET_MSG(getaddr);
+FUZZ_TARGET_MSG(getblocks);
+FUZZ_TARGET_MSG(getblocktxn);
+FUZZ_TARGET_MSG(getcfcheckpt);
+FUZZ_TARGET_MSG(getcfheaders);
+FUZZ_TARGET_MSG(getcfilters);
+FUZZ_TARGET_MSG(getdata);
+FUZZ_TARGET_MSG(getheaders);
+FUZZ_TARGET_MSG(headers);
+FUZZ_TARGET_MSG(inv);
+FUZZ_TARGET_MSG(mempool);
+FUZZ_TARGET_MSG(merkleblock);
+FUZZ_TARGET_MSG(notfound);
+FUZZ_TARGET_MSG(ping);
+FUZZ_TARGET_MSG(pong);
+FUZZ_TARGET_MSG(sendaddrv2);
+FUZZ_TARGET_MSG(sendcmpct);
+FUZZ_TARGET_MSG(sendheaders);
+FUZZ_TARGET_MSG(tx);
+FUZZ_TARGET_MSG(verack);
+FUZZ_TARGET_MSG(version);
+FUZZ_TARGET_MSG(wtxidrelay);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 810f0aac92..ee402dba38 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,7 +13,7 @@
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
-#include <util/memory.h>
+#include <txorphanage.h>
#include <validation.h>
#include <validationinterface.h>
@@ -23,7 +23,7 @@ const TestingSetup* g_setup;
void initialize_process_messages()
{
- static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
@@ -46,7 +46,7 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
CNode& p2p_node = *peers.back();
- const bool successfully_connected{true};
+ const bool successfully_connected{fuzzed_data_provider.ConsumeBool()};
p2p_node.fSuccessfullyConnected = successfully_connected;
p2p_node.fPauseSend = false;
g_setup->m_node.peerman->InitializeNode(&p2p_node);
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index 0b4588c4ce..d1cc6f9c7e 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -5,15 +5,14 @@
#include <test/fuzz/fuzz.h>
#include <node/psbt.h>
-#include <optional.h>
#include <psbt.h>
#include <pubkey.h>
#include <script/script.h>
#include <streams.h>
-#include <util/memory.h>
#include <version.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
@@ -40,7 +39,7 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt)
(void)psbt.IsNull();
- Optional<CMutableTransaction> tx = psbt.tx;
+ std::optional<CMutableTransaction> tx = psbt.tx;
if (tx) {
const CMutableTransaction& mtx = *tx;
const PartiallySignedTransaction psbt_from_tx{mtx};
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 2a08b45aa3..07059cce76 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 7fadf36f98..8219a04e49 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2020 The Bitcoin Core developers
+// Copyright (c) 2019-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -20,7 +20,6 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <univalue.h>
-#include <util/memory.h>
#include <algorithm>
#include <cassert>
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index 2091ad5d91..5f07acbcc7 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -28,12 +28,12 @@
//
// (normal build)
// $ mkdir dump
-// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
+// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot.py --dumptests; done
// $ ...
//
-// (fuzz test build)
+// (libFuzzer build)
// $ mkdir dump-min
-// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
+// $ FUZZ=script_assets_test_minimizer ./src/test/fuzz/fuzz -merge=1 -use_value_profile=1 dump-min/ dump/
// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
namespace {
@@ -190,7 +190,7 @@ ECCVerifyHandle handle;
} // namespace
-FUZZ_TARGET(script_assets_test_minimizer)
+FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, FuzzFrameworkEmptyInitFun, /* hidden */ true)
{
if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
const std::string str((const char*)buffer.data(), buffer.size() - 2);
diff --git a/src/test/fuzz/script_descriptor_cache.cpp b/src/test/fuzz/script_descriptor_cache.cpp
index 1c62c018e7..6ce13d5679 100644
--- a/src/test/fuzz/script_descriptor_cache.cpp
+++ b/src/test/fuzz/script_descriptor_cache.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <optional.h>
#include <pubkey.h>
#include <script/descriptor.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -10,6 +9,7 @@
#include <test/fuzz/util.h>
#include <cstdint>
+#include <optional>
#include <string>
#include <vector>
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index ce8915ca2c..561230707c 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -5,7 +5,6 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <streams.h>
-#include <util/memory.h>
#include <version.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
index bdbfe817ff..eb1c808a88 100644
--- a/src/test/fuzz/script_ops.cpp
+++ b/src/test/fuzz/script_ops.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
index bc4867839c..62ed50d13f 100644
--- a/src/test/fuzz/scriptnum_ops.cpp
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index 3e7b72805e..7b57c5dfd8 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -6,7 +6,6 @@
#include <script/interpreter.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
-#include <util/memory.h>
#include <cstdint>
#include <limits>
@@ -15,7 +14,7 @@
void initialize_signature_checker()
{
- static const auto verify_handle = MakeUnique<ECCVerifyHandle>();
+ static const auto verify_handle = std::make_unique<ECCVerifyHandle>();
}
namespace {
diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp
index 83effec064..303dcf13e3 100644
--- a/src/test/fuzz/signet.cpp
+++ b/src/test/fuzz/signet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,6 +10,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <cstdint>
#include <optional>
@@ -17,7 +18,7 @@
void initialize_signet()
{
- static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::SIGNET);
+ static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::SIGNET);
}
FUZZ_TARGET_INIT(signet, initialize_signet)
diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp
new file mode 100644
index 0000000000..c3a6eed089
--- /dev/null
+++ b/src/test/fuzz/socks5.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) 2020-2021 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 <netaddress.h>
+#include <netbase.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace {
+int default_socks5_recv_timeout;
+};
+
+extern int g_socks5_recv_timeout;
+
+void initialize_socks5()
+{
+ static const auto testing_setup = MakeNoLogFileContext<const BasicTestingSetup>();
+ default_socks5_recv_timeout = g_socks5_recv_timeout;
+}
+
+FUZZ_TARGET_INIT(socks5, initialize_socks5)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ ProxyCredentials proxy_credentials;
+ proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512);
+ proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512);
+ InterruptSocks5(fuzzed_data_provider.ConsumeBool());
+ // Set FUZZED_SOCKET_FAKE_LATENCY=1 to exercise recv timeout code paths. This
+ // will slow down fuzzing.
+ g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1 : default_socks5_recv_timeout;
+ FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider);
+ // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within
+ // a few seconds of fuzzing.
+ (void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512),
+ fuzzed_data_provider.ConsumeIntegral<uint16_t>(),
+ fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr,
+ fuzzed_sock);
+}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 282a2cd8ca..286375f7ae 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -1,10 +1,11 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 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 <blockfilter.h>
#include <clientversion.h>
#include <logging.h>
+#include <netaddress.h>
#include <netbase.h>
#include <outputtype.h>
#include <rpc/client.h>
@@ -67,6 +68,7 @@ FUZZ_TARGET(string)
}
OutputType output_type;
(void)ParseOutputType(random_string_1, output_type);
+ (void)RemovePrefix(random_string_1, random_string_2);
(void)ResolveErrMsg(random_string_1, random_string_2);
try {
(void)RPCConvertNamedValues(random_string_1, random_string_vector);
@@ -78,8 +80,10 @@ FUZZ_TARGET(string)
}
(void)SanitizeString(random_string_1);
(void)SanitizeString(random_string_1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3));
+#ifndef WIN32
(void)ShellEscape(random_string_1);
- int port_out;
+#endif // WIN32
+ uint16_t port_out;
std::string host_out;
SplitHostPort(random_string_1, port_out, host_out);
(void)TimingResistantEqual(random_string_1, random_string_2);
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index b66a7abfb3..2c92b159a5 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 47b38b6d23..b25dcfcd3b 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Bitcoin Core developers
+// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -51,10 +51,10 @@ FUZZ_TARGET(system)
// Avoid hitting:
// util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16));
- if (args_manager.GetArgFlags(argument_name) != nullopt) {
+ if (args_manager.GetArgFlags(argument_name) != std::nullopt) {
return;
}
- args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category);
+ args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>() & ~ArgsManager::COMMAND, options_category);
},
[&] {
// Avoid hitting:
@@ -63,7 +63,7 @@ FUZZ_TARGET(system)
std::vector<std::string> hidden_arguments;
for (const std::string& name : names) {
const std::string hidden_argument = GetArgumentName(name);
- if (args_manager.GetArgFlags(hidden_argument) != nullopt) {
+ if (args_manager.GetArgFlags(hidden_argument) != std::nullopt) {
continue;
}
if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) {
diff --git a/src/test/fuzz/torcontrol.cpp b/src/test/fuzz/torcontrol.cpp
new file mode 100644
index 0000000000..a97d3962bf
--- /dev/null
+++ b/src/test/fuzz/torcontrol.cpp
@@ -0,0 +1,80 @@
+// Copyright (c) 2020-2021 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 <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <torcontrol.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+class DummyTorControlConnection : public TorControlConnection
+{
+public:
+ DummyTorControlConnection() : TorControlConnection{nullptr}
+ {
+ }
+
+ bool Connect(const std::string&, const ConnectionCB&, const ConnectionCB&)
+ {
+ return true;
+ }
+
+ void Disconnect()
+ {
+ }
+
+ bool Command(const std::string&, const ReplyHandlerCB&)
+ {
+ return true;
+ }
+};
+
+void initialize_torcontrol()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+}
+
+FUZZ_TARGET_INIT(torcontrol, initialize_torcontrol)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ TorController tor_controller;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ TorControlReply tor_control_reply;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ tor_control_reply.code = 250;
+ },
+ [&] {
+ tor_control_reply.code = 510;
+ },
+ [&] {
+ tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
+ });
+ tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider);
+ if (tor_control_reply.lines.empty()) {
+ break;
+ }
+ DummyTorControlConnection dummy_tor_control_connection;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.auth_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.authchallenge_cb(dummy_tor_control_connection, tor_control_reply);
+ },
+ [&] {
+ tor_controller.protocolinfo_cb(dummy_tor_control_connection, tor_control_reply);
+ });
+ }
+}
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index 13ae450756..41e1687405 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -100,16 +100,7 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
- bool skip_tx_to_univ = false;
- for (const CTxOut& txout : tx.vout) {
- if (txout.nValue == std::numeric_limits<int64_t>::min()) {
- skip_tx_to_univ = true;
- }
- }
- if (!skip_tx_to_univ) {
- TxToUniv(tx, /* hashBlock */ {}, u);
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
- TxToUniv(tx, u256_max, u);
- }
+ TxToUniv(tx, /* hashBlock */ {}, u);
+ static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ TxToUniv(tx, u256_max, u);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 7a2dcfe84a..cdddad82b3 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,6 +10,7 @@
#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
+#include <compat.h>
#include <consensus/consensus.h>
#include <merkleblock.h>
#include <net.h>
@@ -23,14 +24,13 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/net.h>
-#include <test/util/setup_common.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/time.h>
-#include <util/vector.h>
#include <version.h>
#include <algorithm>
+#include <array>
#include <cstdint>
#include <cstdio>
#include <optional>
@@ -251,6 +251,25 @@ template <class T>
}
/**
+ * Sets errno to a value selected from the given std::array `errnos`.
+ */
+template <typename T, size_t size>
+void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array<T, size>& errnos)
+{
+ errno = fuzzed_data_provider.PickValueInArray(errnos);
+}
+
+/*
+ * Sets a fuzzed errno in the range [0, 133 (EHWPOISON)]. Can be used from functions emulating
+ * standard library functions that set errno, or in other contexts where the value of errno
+ * might be relevant for the execution path that will be taken.
+ */
+inline void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ errno = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 133);
+}
+
+/**
* Returns a byte vector of specified size regardless of the number of remaining bytes available
* from the fuzzer. Pads with zero value bytes if needed to achieve the specified size.
*/
@@ -324,19 +343,6 @@ inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, co
void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept;
-template <class T = const BasicTestingSetup>
-std::unique_ptr<T> MakeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {})
-{
- // Prepend default arguments for fuzzing
- const std::vector<const char*> arguments = Cat(
- {
- "-nodebuglogfile",
- },
- extra_args);
-
- return MakeUnique<T>(chain_name, arguments);
-}
-
class FuzzedFileProvider
{
FuzzedDataProvider& m_fuzzed_data_provider;
@@ -349,6 +355,7 @@ public:
FILE* open()
{
+ SetFuzzedErrNo(m_fuzzed_data_provider);
if (m_fuzzed_data_provider.ConsumeBool()) {
return nullptr;
}
@@ -390,6 +397,7 @@ public:
static ssize_t read(void* cookie, char* buf, size_t size)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
}
@@ -408,6 +416,7 @@ public:
static ssize_t write(void* cookie, const char* buf, size_t size)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
@@ -418,8 +427,9 @@ public:
static int seek(void* cookie, int64_t* offset, int whence)
{
- assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet.
+ assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
int64_t new_offset = 0;
if (whence == SEEK_SET) {
new_offset = *offset;
@@ -428,6 +438,12 @@ public:
return -1;
}
new_offset = fuzzed_file->m_offset + *offset;
+ } else if (whence == SEEK_END) {
+ const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
+ if (AdditionOverflow(n, *offset)) {
+ return -1;
+ }
+ new_offset = n + *offset;
}
if (new_offset < 0) {
return -1;
@@ -440,6 +456,7 @@ public:
static int close(void* cookie)
{
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
+ SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
}
};
@@ -534,4 +551,127 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
}
}
+class FuzzedSock : public Sock
+{
+ FuzzedDataProvider& m_fuzzed_data_provider;
+
+public:
+ explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
+ {
+ }
+
+ ~FuzzedSock() override
+ {
+ }
+
+ FuzzedSock& operator=(Sock&& other) override
+ {
+ assert(false && "Not implemented yet.");
+ return *this;
+ }
+
+ SOCKET Get() const override
+ {
+ assert(false && "Not implemented yet.");
+ return INVALID_SOCKET;
+ }
+
+ SOCKET Release() override
+ {
+ assert(false && "Not implemented yet.");
+ return INVALID_SOCKET;
+ }
+
+ void Reset() override
+ {
+ assert(false && "Not implemented yet.");
+ }
+
+ ssize_t Send(const void* data, size_t len, int flags) const override
+ {
+ constexpr std::array send_errnos{
+ EACCES,
+ EAGAIN,
+ EALREADY,
+ EBADF,
+ ECONNRESET,
+ EDESTADDRREQ,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ EISCONN,
+ EMSGSIZE,
+ ENOBUFS,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EOPNOTSUPP,
+ EPIPE,
+ EWOULDBLOCK,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return len;
+ }
+ const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
+ }
+ return r;
+ }
+
+ ssize_t Recv(void* buf, size_t len, int flags) const override
+ {
+ constexpr std::array recv_errnos{
+ EAGAIN,
+ EBADF,
+ ECONNREFUSED,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EWOULDBLOCK,
+ };
+ assert(buf != nullptr || len == 0);
+ if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ const std::vector<uint8_t> random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
+ m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
+ if (random_bytes.empty()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ std::memcpy(buf, random_bytes.data(), random_bytes.size());
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ if (len > random_bytes.size()) {
+ std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
+ }
+ return len;
+ }
+ if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
+ std::this_thread::sleep_for(std::chrono::milliseconds{2});
+ }
+ return random_bytes.size();
+ }
+
+ bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
+ {
+ return m_fuzzed_data_provider.ConsumeBool();
+ }
+};
+
+[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
+{
+ return FuzzedSock{fuzzed_data_provider};
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
new file mode 100644
index 0000000000..e1a21b6c53
--- /dev/null
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2020 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 <chainparamsbase.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <txmempool.h>
+#include <util/time.h>
+#include <validation.h>
+
+#include <cstdint>
+#include <vector>
+
+void initialize_validation_load_mempool()
+{
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+}
+
+FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+ FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider);
+
+ CTxMemPool pool{};
+ auto fuzzed_fopen = [&](const fs::path&, const char*) {
+ return fuzzed_file_provider.open();
+ };
+ (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
+ (void)DumpMempool(pool, fuzzed_fopen, true);
+}
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
new file mode 100644
index 0000000000..a898e2782d
--- /dev/null
+++ b/src/test/fuzz/versionbits.cpp
@@ -0,0 +1,345 @@
+// Copyright (c) 2020-2021 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 <chain.h>
+#include <chainparams.h>
+#include <consensus/params.h>
+#include <primitives/block.h>
+#include <versionbits.h>
+
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <vector>
+
+namespace {
+class TestConditionChecker : public AbstractThresholdConditionChecker
+{
+private:
+ mutable ThresholdConditionCache m_cache;
+ const Consensus::Params dummy_params{};
+
+public:
+ const int64_t m_begin = 0;
+ const int64_t m_end = 0;
+ const int m_period = 0;
+ const int m_threshold = 0;
+ const int m_bit = 0;
+
+ TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit)
+ : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit}
+ {
+ assert(m_period > 0);
+ assert(0 <= m_threshold && m_threshold <= m_period);
+ assert(0 <= m_bit && m_bit <= 32 && m_bit < VERSIONBITS_NUM_BITS);
+ }
+
+ bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
+ int64_t BeginTime(const Consensus::Params& params) const override { return m_begin; }
+ int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
+ int Period(const Consensus::Params& params) const override { return m_period; }
+ int Threshold(const Consensus::Params& params) const override { return m_threshold; }
+
+ ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
+ BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params); }
+
+ bool Condition(int64_t version) const
+ {
+ return ((version >> m_bit) & 1) != 0 && (version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS;
+ }
+
+ bool Condition(const CBlockIndex* pindex) const { return Condition(pindex->nVersion); }
+};
+
+/** Track blocks mined for test */
+class Blocks
+{
+private:
+ std::vector<std::unique_ptr<CBlockIndex>> m_blocks;
+ const uint32_t m_start_time;
+ const uint32_t m_interval;
+ const int32_t m_signal;
+ const int32_t m_no_signal;
+
+public:
+ Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal)
+ : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {}
+
+ size_t size() const { return m_blocks.size(); }
+
+ CBlockIndex* tip() const
+ {
+ return m_blocks.empty() ? nullptr : m_blocks.back().get();
+ }
+
+ CBlockIndex* mine_block(bool signal)
+ {
+ CBlockHeader header;
+ header.nVersion = signal ? m_signal : m_no_signal;
+ header.nTime = m_start_time + m_blocks.size() * m_interval;
+ header.nBits = 0x1d00ffff;
+
+ auto current_block = std::make_unique<CBlockIndex>(header);
+ current_block->pprev = tip();
+ current_block->nHeight = m_blocks.size();
+ current_block->BuildSkip();
+
+ return m_blocks.emplace_back(std::move(current_block)).get();
+ }
+};
+
+void initialize()
+{
+ SelectParams(CBaseChainParams::MAIN);
+}
+} // namespace
+
+constexpr uint32_t MAX_TIME = 4102444800; // 2100-01-01
+
+FUZZ_TARGET_INIT(versionbits, initialize)
+{
+ const CChainParams& params = Params();
+
+ const int64_t interval = params.GetConsensus().nPowTargetSpacing;
+ assert(interval > 1); // need to be able to halve it
+ assert(interval < std::numeric_limits<int32_t>::max());
+
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ // making period/max_periods larger slows these tests down significantly
+ const int period = 32;
+ const size_t max_periods = 16;
+ const size_t max_blocks = 2 * period * max_periods;
+
+ const int threshold = fuzzed_data_provider.ConsumeIntegralInRange(1, period);
+ assert(0 < threshold && threshold <= period); // must be able to both pass and fail threshold!
+
+ // too many blocks at 10min each might cause uint32_t time to overflow if
+ // block_start_time is at the end of the range above
+ assert(std::numeric_limits<uint32_t>::max() - MAX_TIME > interval * max_blocks);
+
+ const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(params.GenesisBlock().nTime, MAX_TIME);
+
+ // what values for version will we use to signal / not signal?
+ const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
+ const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
+
+ // select deployment parameters: bit, start time, timeout
+ const int bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1);
+
+ bool always_active_test = false;
+ bool never_active_test = false;
+ int64_t start_time;
+ int64_t timeout;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ // pick the timestamp to switch based on a block
+ // note states will change *after* these blocks because mediantime lags
+ int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
+ int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(start_block, period * (max_periods - 3));
+
+ start_time = block_start_time + start_block * interval;
+ timeout = block_start_time + end_block * interval;
+
+ assert(start_time <= timeout);
+
+ // allow for times to not exactly match a block
+ if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
+ if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2;
+
+ // this may make timeout too early; if so, don't run the test
+ if (start_time > timeout) return;
+ } else {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
+ timeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ always_active_test = true;
+ } else {
+ start_time = 1199145601; // January 1, 2008
+ timeout = 1230767999; // December 31, 2008
+ never_active_test = true;
+ }
+ }
+
+ TestConditionChecker checker(start_time, timeout, period, threshold, bit);
+
+ // Early exit if the versions don't signal sensibly for the deployment
+ if (!checker.Condition(ver_signal)) return;
+ if (checker.Condition(ver_nosignal)) return;
+ if (ver_nosignal < 0) return;
+
+ // TOP_BITS should ensure version will be positive
+ assert(ver_signal > 0);
+
+ // Now that we have chosen time and versions, setup to mine blocks
+ Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal);
+
+ /* Strategy:
+ * * we will mine a final period worth of blocks, with
+ * randomised signalling according to a mask
+ * * but before we mine those blocks, we will mine some
+ * randomised number of prior periods; with either all
+ * or no blocks in the period signalling
+ *
+ * We establish the mask first, then consume "bools" until
+ * we run out of fuzz data to work out how many prior periods
+ * there are and which ones will signal.
+ */
+
+ // establish the mask
+ const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+
+ // mine prior periods
+ while (fuzzed_data_provider.remaining_bytes() > 0) {
+ // all blocks in these periods either do or don't signal
+ bool signal = fuzzed_data_provider.ConsumeBool();
+ for (int b = 0; b < period; ++b) {
+ blocks.mine_block(signal);
+ }
+
+ // don't risk exceeding max_blocks or times may wrap around
+ if (blocks.size() + period*2 > max_blocks) break;
+ }
+ // NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further
+
+ // now we mine the final period and check that everything looks sane
+
+ // count the number of signalling blocks
+ int blocks_sig = 0;
+
+ // get the info for the first block of the period
+ CBlockIndex* prev = blocks.tip();
+ const int exp_since = checker.GetStateSinceHeightFor(prev);
+ const ThresholdState exp_state = checker.GetStateFor(prev);
+ BIP9Stats last_stats = checker.GetStateStatisticsFor(prev);
+
+ int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
+ assert(exp_since <= prev_next_height);
+
+ // mine (period-1) blocks and check state
+ for (int b = 1; b < period; ++b) {
+ const bool signal = (signalling_mask >> (b % 32)) & 1;
+ if (signal) ++blocks_sig;
+
+ CBlockIndex* current_block = blocks.mine_block(signal);
+
+ // verify that signalling attempt was interpreted correctly
+ assert(checker.Condition(current_block) == signal);
+
+ // state and since don't change within the period
+ const ThresholdState state = checker.GetStateFor(current_block);
+ const int since = checker.GetStateSinceHeightFor(current_block);
+ assert(state == exp_state);
+ assert(since == exp_since);
+
+ // GetStateStatistics may crash when state is not STARTED
+ if (state != ThresholdState::STARTED) continue;
+
+ // check that after mining this block stats change as expected
+ const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
+ assert(stats.period == period);
+ assert(stats.threshold == threshold);
+ assert(stats.elapsed == b);
+ assert(stats.count == last_stats.count + (signal ? 1 : 0));
+ assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
+ last_stats = stats;
+ }
+
+ if (exp_state == ThresholdState::STARTED) {
+ // double check that stats.possible is sane
+ if (blocks_sig >= threshold - 1) assert(last_stats.possible);
+ }
+
+ // mine the final block
+ bool signal = (signalling_mask >> (period % 32)) & 1;
+ if (signal) ++blocks_sig;
+ CBlockIndex* current_block = blocks.mine_block(signal);
+ assert(checker.Condition(current_block) == signal);
+
+ // GetStateStatistics is safe on a period boundary
+ // and has progressed to a new period
+ const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
+ assert(stats.period == period);
+ assert(stats.threshold == threshold);
+ assert(stats.elapsed == 0);
+ assert(stats.count == 0);
+ assert(stats.possible == true);
+
+ // More interesting is whether the state changed.
+ const ThresholdState state = checker.GetStateFor(current_block);
+ const int since = checker.GetStateSinceHeightFor(current_block);
+
+ // since is straightforward:
+ assert(since % period == 0);
+ assert(0 <= since && since <= current_block->nHeight + 1);
+ if (state == exp_state) {
+ assert(since == exp_since);
+ } else {
+ assert(since == current_block->nHeight + 1);
+ }
+
+ // state is where everything interesting is
+ switch (state) {
+ case ThresholdState::DEFINED:
+ assert(since == 0);
+ assert(exp_state == ThresholdState::DEFINED);
+ assert(current_block->GetMedianTimePast() < checker.m_begin);
+ assert(current_block->GetMedianTimePast() < checker.m_end);
+ break;
+ case ThresholdState::STARTED:
+ assert(current_block->GetMedianTimePast() >= checker.m_begin);
+ assert(current_block->GetMedianTimePast() < checker.m_end);
+ if (exp_state == ThresholdState::STARTED) {
+ assert(blocks_sig < threshold);
+ } else {
+ assert(exp_state == ThresholdState::DEFINED);
+ }
+ break;
+ case ThresholdState::LOCKED_IN:
+ assert(exp_state == ThresholdState::STARTED);
+ assert(current_block->GetMedianTimePast() < checker.m_end);
+ assert(blocks_sig >= threshold);
+ break;
+ case ThresholdState::ACTIVE:
+ assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
+ break;
+ case ThresholdState::FAILED:
+ assert(current_block->GetMedianTimePast() >= checker.m_end);
+ assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE);
+ break;
+ default:
+ assert(false);
+ }
+
+ if (blocks.size() >= max_periods * period) {
+ // we chose the timeout (and block times) so that by the time we have this many blocks it's all over
+ assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
+ }
+
+ // "always active" has additional restrictions
+ if (always_active_test) {
+ assert(state == ThresholdState::ACTIVE);
+ assert(exp_state == ThresholdState::ACTIVE);
+ assert(since == 0);
+ } else {
+ // except for always active, the initial state is always DEFINED
+ assert(since > 0 || state == ThresholdState::DEFINED);
+ assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
+ }
+
+ // "never active" does too
+ if (never_active_test) {
+ assert(state == ThresholdState::FAILED);
+ assert(since == period);
+ if (exp_since == 0) {
+ assert(exp_state == ThresholdState::DEFINED);
+ } else {
+ assert(exp_state == ThresholdState::FAILED);
+ }
+ }
+}
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 87f6470afa..41a626c0ea 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -107,14 +107,14 @@ BOOST_AUTO_TEST_CASE(siphash)
// Check test vectors from spec, one byte at a time
CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
- for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); ++x)
+ for (uint8_t x=0; x<std::size(siphash_4_2_testvec); ++x)
{
BOOST_CHECK_EQUAL(hasher2.Finalize(), siphash_4_2_testvec[x]);
hasher2.Write(&x, 1);
}
// Check test vectors from spec, eight bytes at a time
CSipHasher hasher3(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
- for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); x+=8)
+ for (uint8_t x=0; x<std::size(siphash_4_2_testvec); x+=8)
{
BOOST_CHECK_EQUAL(hasher3.Finalize(), siphash_4_2_testvec[x]);
hasher3.Write(uint64_t(x)|(uint64_t(x+1)<<8)|(uint64_t(x+2)<<16)|(uint64_t(x+3)<<24)|
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index e967273636..9acd17c463 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -28,7 +28,7 @@ struct MinerTestingSetup : public TestingSetup {
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
{
- return CheckSequenceLocks(*m_node.mempool, tx, flags);
+ return CheckSequenceLocks(::ChainstateActive(), *m_node.mempool, tx, flags);
}
BlockAssembler AssemblerForTest(const CChainParams& params);
};
@@ -122,7 +122,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
uint256 hashHighFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
- std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
+ BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 4U);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
@@ -142,7 +143,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
uint256 hashLowFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
@@ -156,7 +157,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
+ BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 6U);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
@@ -177,7 +179,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
uint256 hashLowFeeTx2 = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
// Verify that this tx isn't selected.
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -190,7 +192,8 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
- pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+ pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
+ BOOST_REQUIRE_EQUAL(pblocktemplate->block.vtx.size(), 9U);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
@@ -212,15 +215,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
fCheckpointsEnabled = false;
// Simple block creation, nothing special yet:
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// We can't make transactions until we have inputs
// Therefore, load 110 blocks :)
- static_assert(sizeof(blockinfo) / sizeof(*blockinfo) == 110, "Should have 110 blocks to import");
+ static_assert(std::size(blockinfo) == 110, "Should have 110 blocks to import");
int baseheight = 0;
std::vector<CTransactionRef> txFirst;
- for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
- {
+ for (const auto& bi : blockinfo) {
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
{
LOCK(cs_main);
@@ -229,7 +231,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
- txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
+ txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
@@ -239,7 +241,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
- pblock->nNonce = blockinfo[i].nonce;
+ pblock->nNonce = bi.nonce;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr));
@@ -250,7 +252,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
LOCK(m_node.mempool->cs);
// Just to make sure we can still make simple blocks
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
const CAmount BLOCKSUBSIDY = 50*COIN;
const CAmount LOWFEE = CENT;
@@ -275,7 +277,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
m_node.mempool->clear();
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
@@ -289,7 +291,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// block size > limit
@@ -309,13 +311,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// orphan in *m_node.mempool, template creation fails
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
m_node.mempool->clear();
// child with higher feerate than parent
@@ -332,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
m_node.mempool->clear();
// coinbase in *m_node.mempool, template creation fails
@@ -344,7 +346,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// give it a fee so it'll get mined
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-cb-multiple
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
m_node.mempool->clear();
// double spend txn pair in *m_node.mempool, template creation fails
@@ -357,7 +359,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
m_node.mempool->clear();
// subsidy changing
@@ -373,7 +375,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
next->BuildSkip();
::ChainActive().SetTip(next);
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// Extend to a 210000-long block chain.
while (::ChainActive().Tip()->nHeight < 210000) {
CBlockIndex* prev = ::ChainActive().Tip();
@@ -385,7 +387,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
next->BuildSkip();
::ChainActive().SetTip(next);
}
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// invalid p2sh txn in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
@@ -402,7 +404,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw block-validation-failed
- BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
+ BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
m_node.mempool->clear();
// Delete the dummy blocks again.
@@ -435,7 +437,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
@@ -445,7 +447,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
@@ -461,7 +463,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
@@ -472,7 +474,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
@@ -481,7 +483,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -490,7 +492,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
// None of the of the absolute height/time locked tx should have made
// it into the template because we still check IsFinalTx in CreateNewBlock,
@@ -503,7 +505,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
::ChainActive().Tip()->nHeight++;
SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
- BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+ BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
::ChainActive().Tip()->nHeight--;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index a1b41e17ed..c336705d50 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -8,13 +8,12 @@
#include <clientversion.h>
#include <cstdint>
#include <net.h>
+#include <netaddress.h>
#include <netbase.h>
-#include <optional.h>
#include <serialize.h>
#include <span.h>
#include <streams.h>
#include <test/util/setup_common.h>
-#include <util/memory.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
@@ -25,6 +24,7 @@
#include <algorithm>
#include <ios>
#include <memory>
+#include <optional>
#include <string>
using namespace std::literals;
@@ -92,7 +92,7 @@ BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(cnode_listen_port)
{
// test default
- uint16_t port = GetListenPort();
+ uint16_t port{GetListenPort()};
BOOST_CHECK(port == Params().GetDefaultPort());
// test set port
uint16_t altPort = 12345;
@@ -188,21 +188,22 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
- std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(
+ std::unique_ptr<CNode> pnode1 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 0,
/* nLocalHostNonceIn = */ 0,
- CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY,
+ /* inbound_onion = */ false);
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
BOOST_CHECK(pnode1->IsManualConn() == false);
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode1->IsFeelerConn() == false);
BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
BOOST_CHECK(pnode1->IsInboundConn() == false);
- BOOST_CHECK(pnode1->IsInboundOnion() == false);
+ BOOST_CHECK(pnode1->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
- std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(
+ std::unique_ptr<CNode> pnode2 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 1,
/* nLocalHostNonceIn = */ 1,
@@ -214,10 +215,10 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode2->IsFeelerConn() == false);
BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
BOOST_CHECK(pnode2->IsInboundConn() == true);
- BOOST_CHECK(pnode2->IsInboundOnion() == false);
+ BOOST_CHECK(pnode2->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
- std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>(
+ std::unique_ptr<CNode> pnode3 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 0,
/* nLocalHostNonceIn = */ 0,
@@ -229,10 +230,10 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode3->IsFeelerConn() == false);
BOOST_CHECK(pnode3->IsAddrFetchConn() == false);
BOOST_CHECK(pnode3->IsInboundConn() == false);
- BOOST_CHECK(pnode3->IsInboundOnion() == false);
+ BOOST_CHECK(pnode3->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
- std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>(
+ std::unique_ptr<CNode> pnode4 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 1,
/* nLocalHostNonceIn = */ 1,
@@ -244,7 +245,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode4->IsFeelerConn() == false);
BOOST_CHECK(pnode4->IsAddrFetchConn() == false);
BOOST_CHECK(pnode4->IsInboundConn() == true);
- BOOST_CHECK(pnode4->IsInboundOnion() == true);
+ BOOST_CHECK(pnode4->m_inbound_onion == true);
BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION);
}
@@ -321,6 +322,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
@@ -331,6 +333,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
+ BOOST_CHECK(!addr.IsI2P());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
@@ -351,6 +354,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
// TOR, invalid base32
BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
+ // I2P
+ const char* i2p_addr = "UDHDrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.I2P";
+ BOOST_REQUIRE(addr.SetSpecial(i2p_addr));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsI2P());
+
+ BOOST_CHECK(!addr.IsTor());
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), ToLower(i2p_addr));
+
+ // I2P, correct length, but decodes to less than the expected number of bytes.
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jn=.b32.i2p"));
+
+ // I2P, extra unnecessary padding
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna=.b32.i2p"));
+
+ // I2P, malicious
+ BOOST_CHECK(!addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v\0wtf.b32.i2p"s));
+
+ // I2P, valid but unsupported (56 Base32 characters)
+ // See "Encrypted LS with Base 32 Addresses" in
+ // https://geti2p.net/spec/encryptedleaseset.txt
+ BOOST_CHECK(
+ !addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.b32.i2p"));
+
+ // I2P, invalid base32
+ BOOST_CHECK(!addr.SetSpecial(std::string{"tp*szydbh4dp.b32.i2p"}));
+
// Internal
addr.SetInternal("esffpp");
BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid
@@ -679,7 +711,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
in_addr ipv4AddrPeer;
ipv4AddrPeer.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
- std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY);
+ std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress{}, /* pszDest */ std::string{}, ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false);
pnode->fSuccessfullyConnected.store(true);
// the peer claims to be reaching us via IPv6
@@ -690,7 +722,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
pnode->SetAddrLocal(addrLocal);
// before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
- AdvertiseLocal(&*pnode);
+ GetLocalAddrForPeer(&*pnode);
// suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
BOOST_CHECK(1);
@@ -771,21 +803,6 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
BOOST_CHECK_EQUAL(IsLocal(addr), false);
}
-BOOST_AUTO_TEST_CASE(PoissonNextSend)
-{
- g_mock_deterministic_tests = true;
-
- int64_t now = 5000;
- int average_interval_seconds = 600;
-
- auto poisson = ::PoissonNextSend(now, average_interval_seconds);
- std::chrono::microseconds poisson_chrono = ::PoissonNextSend(std::chrono::microseconds{now}, std::chrono::seconds{average_interval_seconds});
-
- BOOST_CHECK_EQUAL(poisson, poisson_chrono.count());
-
- g_mock_deterministic_tests = false;
-}
-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
{
std::vector<NodeEvictionCandidate> candidates;
@@ -793,7 +810,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
candidates.push_back({
/* id */ id,
/* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
- /* nMinPingUsecTime */ static_cast<int64_t>(random_context.randrange(100)),
+ /* m_min_ping_time */ std::chrono::microseconds{random_context.randrange(100)},
/* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
/* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
/* fRelevantServices */ random_context.randbool(),
@@ -811,7 +828,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::vector<NodeId>& node_ids, FastRandomContext& random_context)
{
Shuffle(candidates.begin(), candidates.end(), random_context);
- const Optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
+ const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
if (!evicted_node_id) {
return false;
}
@@ -853,7 +870,7 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
// from eviction.
BOOST_CHECK(!IsEvicted(
number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.nMinPingUsecTime = candidate.id;
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
},
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
@@ -899,10 +916,10 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
// Combination of all tests above.
BOOST_CHECK(!IsEvicted(
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.nMinPingUsecTime = candidate.id; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 66ad7bb5ea..33b56624a8 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 <net_permissions.h>
+#include <netaddress.h>
#include <netbase.h>
#include <protocol.h>
#include <serialize.h>
@@ -83,31 +84,31 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
}
-bool static TestSplitHost(std::string test, std::string host, int port)
+bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port)
{
std::string hostOut;
- int portOut = -1;
+ uint16_t portOut{0};
SplitHostPort(test, portOut, hostOut);
return hostOut == host && port == portOut;
}
BOOST_AUTO_TEST_CASE(netbase_splithost)
{
- BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", -1));
- BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", -1));
+ BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", 0));
+ BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", 0));
BOOST_CHECK(TestSplitHost("www.bitcoincore.org:80", "www.bitcoincore.org", 80));
BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]:80", "www.bitcoincore.org", 80));
- BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
+ BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
- BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
+ BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
- BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
+ BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", 0));
BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
- BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
+ BOOST_CHECK(TestSplitHost("::8333", "::8333", 0));
BOOST_CHECK(TestSplitHost(":8333", "", 8333));
BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
- BOOST_CHECK(TestSplitHost("", "", -1));
+ BOOST_CHECK(TestSplitHost("", "", 0));
}
bool static TestParse(std::string src, std::string canon)
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index b54cbb3f00..2e0fc7e48f 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -36,7 +36,6 @@ UniValue RPCTestingSetup::CallRPC(std::string args)
JSONRPCRequest request(context);
request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs);
- request.fHelp = false;
if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();
try {
UniValue result = tableRPC.execute(request);
@@ -174,6 +173,16 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000000).write(), "0.00000100");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000000).write(), "0.00000010");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001");
+
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max()).write(), "92233720368.54775807");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 1).write(), "92233720368.54775806");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 2).write(), "92233720368.54775805");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::max() - 3).write(), "92233720368.54775804");
+ // ...
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 3).write(), "-92233720368.54775805");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 2).write(), "-92233720368.54775806");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min() + 1).write(), "-92233720368.54775807");
+ BOOST_CHECK_EQUAL(ValueFromAmount(std::numeric_limits<CAmount>::min()).write(), "-92233720368.54775808");
}
static UniValue ValueFromString(const std::string &str)
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 740b2c72db..496292875d 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -5,6 +5,7 @@
#include <compat/sanity.h>
#include <key.h>
#include <test/util/setup_common.h>
+#include <util/time.h>
#include <boost/test/unit_test.hpp>
@@ -12,9 +13,9 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(basic_sanity)
{
- BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test");
BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
+ BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 366385b619..4dc0dd5f51 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -107,6 +107,22 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
+ // TxoutType::WITNESS_V1_TAPROOT
+ s.clear();
+ s << OP_1 << ToByteVector(uint256::ZERO);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V1_TAPROOT);
+ BOOST_CHECK_EQUAL(solutions.size(), 2U);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>{1});
+ BOOST_CHECK(solutions[1] == ToByteVector(uint256::ZERO));
+
+ // TxoutType::WITNESS_UNKNOWN
+ s.clear();
+ s << OP_16 << ToByteVector(uint256::ONE);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
+ BOOST_CHECK_EQUAL(solutions.size(), 2U);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>{16});
+ BOOST_CHECK(solutions[1] == ToByteVector(uint256::ONE));
+
// TxoutType::NONSTANDARD
s.clear();
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index 281018be9f..746d4d3c6b 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -164,9 +164,9 @@ static void RunOperators(const int64_t& num1, const int64_t& num2)
BOOST_AUTO_TEST_CASE(creation)
{
- for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i)
+ for(size_t i = 0; i < std::size(values); ++i)
{
- for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j)
+ for(size_t j = 0; j < std::size(offsets); ++j)
{
RunCreate(values[i]);
RunCreate(values[i] + offsets[j]);
@@ -177,9 +177,9 @@ BOOST_AUTO_TEST_CASE(creation)
BOOST_AUTO_TEST_CASE(operators)
{
- for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i)
+ for(size_t i = 0; i < std::size(values); ++i)
{
- for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j)
+ for(size_t j = 0; j < std::size(offsets); ++j)
{
RunOperators(values[i], values[i]);
RunOperators(values[i], -values[i]);
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index bc862de78a..2eb980e8cd 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -88,7 +88,7 @@ void static RandomScript(CScript &script) {
script = CScript();
int ops = (InsecureRandRange(10));
for (int i=0; i<ops; i++)
- script << oplist[InsecureRandRange(sizeof(oplist)/sizeof(oplist[0]))];
+ script << oplist[InsecureRandRange(std::size(oplist))];
}
void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp
new file mode 100644
index 0000000000..400de875b7
--- /dev/null
+++ b/src/test/sock_tests.cpp
@@ -0,0 +1,180 @@
+// Copyright (c) 2021-2021 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 <compat.h>
+#include <test/util/setup_common.h>
+#include <threadinterrupt.h>
+#include <util/sock.h>
+#include <util/system.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <cassert>
+#include <thread>
+
+using namespace std::chrono_literals;
+
+BOOST_FIXTURE_TEST_SUITE(sock_tests, BasicTestingSetup)
+
+static bool SocketIsClosed(const SOCKET& s)
+{
+ // Notice that if another thread is running and creates its own socket after `s` has been
+ // closed, it may be assigned the same file descriptor number. In this case, our test will
+ // wrongly pretend that the socket is not closed.
+ int type;
+ socklen_t len = sizeof(type);
+ return getsockopt(s, SOL_SOCKET, SO_TYPE, (sockopt_arg_type)&type, &len) == SOCKET_ERROR;
+}
+
+static SOCKET CreateSocket()
+{
+ const SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ BOOST_REQUIRE(s != static_cast<SOCKET>(SOCKET_ERROR));
+ return s;
+}
+
+BOOST_AUTO_TEST_CASE(constructor_and_destructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Get(), s);
+ BOOST_CHECK(!SocketIsClosed(s));
+ delete sock;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_constructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock(std::move(*sock1));
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_assignment)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock();
+ *sock2 = std::move(*sock1);
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(release)
+{
+ SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Release(), s);
+ delete sock;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_REQUIRE(CloseSocket(s));
+}
+
+BOOST_AUTO_TEST_CASE(reset)
+{
+ const SOCKET s = CreateSocket();
+ Sock sock(s);
+ sock.Reset();
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+#ifndef WIN32 // Windows does not have socketpair(2).
+
+static void CreateSocketPair(int s[2])
+{
+ BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
+}
+
+static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
+{
+ const char* msg = "abcd";
+ constexpr ssize_t msg_len = 4;
+ char recv_buf[10];
+
+ BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
+ BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
+ BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
+}
+
+BOOST_AUTO_TEST_CASE(send_and_receive)
+{
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock* sock0 = new Sock(s[0]);
+ Sock* sock1 = new Sock(s[1]);
+
+ SendAndRecvMessage(*sock0, *sock1);
+
+ Sock* sock0moved = new Sock(std::move(*sock0));
+ Sock* sock1moved = new Sock();
+ *sock1moved = std::move(*sock1);
+
+ delete sock0;
+ delete sock1;
+
+ SendAndRecvMessage(*sock1moved, *sock0moved);
+
+ delete sock0moved;
+ delete sock1moved;
+
+ BOOST_CHECK(SocketIsClosed(s[0]));
+ BOOST_CHECK(SocketIsClosed(s[1]));
+}
+
+BOOST_AUTO_TEST_CASE(wait)
+{
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock sock0(s[0]);
+ Sock sock1(s[1]);
+
+ std::thread waiter([&sock0]() { sock0.Wait(24h, Sock::RECV); });
+
+ BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
+
+ waiter.join();
+}
+
+BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
+{
+ constexpr auto timeout = 1min; // High enough so that it is never hit.
+ CThreadInterrupt interrupt;
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock sock_send(s[0]);
+ Sock sock_recv(s[1]);
+
+ std::thread receiver([&sock_recv, &timeout, &interrupt]() {
+ constexpr size_t max_data{10};
+ bool threw_as_expected{false};
+ // BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which
+ // creates a data race. So mimic it manually.
+ try {
+ sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data);
+ } catch (const std::runtime_error& e) {
+ threw_as_expected = HasReason("too many bytes without a terminator")(e);
+ }
+ assert(threw_as_expected);
+ });
+
+ BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt));
+ BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt));
+
+ receiver.join();
+}
+
+#endif /* WIN32 */
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp
index ce555f7299..940145b84f 100644
--- a/src/test/system_tests.cpp
+++ b/src/test/system_tests.cpp
@@ -6,22 +6,22 @@
#include <util/system.h>
#include <univalue.h>
-#ifdef HAVE_BOOST_PROCESS
+#ifdef ENABLE_EXTERNAL_SIGNER
#include <boost/process.hpp>
-#endif // HAVE_BOOST_PROCESS
+#endif // ENABLE_EXTERNAL_SIGNER
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(system_tests, BasicTestingSetup)
-// At least one test is required (in case HAVE_BOOST_PROCESS is not defined).
+// At least one test is required (in case ENABLE_EXTERNAL_SIGNER is not defined).
// Workaround for https://github.com/bitcoin/bitcoin/issues/19128
BOOST_AUTO_TEST_CASE(dummy)
{
BOOST_CHECK(true);
}
-#ifdef HAVE_BOOST_PROCESS
+#ifdef ENABLE_EXTERNAL_SIGNER
bool checkMessage(const std::runtime_error& ex)
{
@@ -90,6 +90,6 @@ BOOST_AUTO_TEST_CASE(run_command)
}
#endif
}
-#endif // HAVE_BOOST_PROCESS
+#endif // ENABLE_EXTERNAL_SIGNER
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 1eb30c68d6..8f1d99b199 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -22,6 +22,7 @@
#include <streams.h>
#include <test/util/transaction_utils.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <validation.h>
#include <functional>
@@ -40,7 +41,6 @@ typedef std::vector<unsigned char> valtype;
extern UniValue read_json(const std::string& jsondata);
static std::map<std::string, unsigned int> mapFlagNames = {
- {std::string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE},
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
@@ -63,9 +63,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
unsigned int ParseScriptFlags(std::string strFlags)
{
- if (strFlags.empty()) {
- return 0;
- }
+ if (strFlags.empty() || strFlags == "NONE") return 0;
unsigned int flags = 0;
std::vector<std::string> words;
boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
@@ -96,20 +94,84 @@ std::string FormatScriptFlags(unsigned int flags)
return ret.substr(0, ret.size() - 1);
}
+/*
+* Check that the input scripts of a transaction are valid/invalid as expected.
+*/
+bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
+ const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags,
+ const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
+{
+ bool tx_valid = true;
+ ScriptError err = expect_valid ? SCRIPT_ERR_UNKNOWN_ERROR : SCRIPT_ERR_OK;
+ for (unsigned int i = 0; i < tx.vin.size() && tx_valid; ++i) {
+ const CTxIn input = tx.vin[i];
+ const CAmount amount = map_prevout_values.count(input.prevout) ? map_prevout_values.at(input.prevout) : 0;
+ try {
+ tx_valid = VerifyScript(input.scriptSig, map_prevout_scriptPubKeys.at(input.prevout),
+ &input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
+ } catch (...) {
+ BOOST_ERROR("Bad test: " << strTest);
+ return true; // The test format is bad and an error is thrown. Return true to silence further error.
+ }
+ if (expect_valid) {
+ BOOST_CHECK_MESSAGE(tx_valid, strTest);
+ BOOST_CHECK_MESSAGE((err == SCRIPT_ERR_OK), ScriptErrorString(err));
+ err = SCRIPT_ERR_UNKNOWN_ERROR;
+ }
+ }
+ if (!expect_valid) {
+ BOOST_CHECK_MESSAGE(!tx_valid, strTest);
+ BOOST_CHECK_MESSAGE((err != SCRIPT_ERR_OK), ScriptErrorString(err));
+ }
+ return (tx_valid == expect_valid);
+}
+
+/*
+ * Trim or fill flags to make the combination valid:
+ * WITNESS must be used with P2SH
+ * CLEANSTACK must be used WITNESS and P2SH
+ */
+
+unsigned int TrimFlags(unsigned int flags)
+{
+ // WITNESS requires P2SH
+ if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS;
+
+ // CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
+ if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK;
+ return flags;
+}
+
+unsigned int FillFlags(unsigned int flags)
+{
+ // CLEANSTACK implies WITNESS
+ if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
+
+ // WITNESS implies P2SH (and transitively CLEANSTACK implies P2SH)
+ if (flags & SCRIPT_VERIFY_WITNESS) flags |= SCRIPT_VERIFY_P2SH;
+ return flags;
+}
+
+// Return valid flags that are all except one flag for each flag
+std::vector<unsigned int> ExcludeIndividualFlags(unsigned int flags)
+{
+ std::vector<unsigned int> flags_combos;
+ for (unsigned int i = 0; i < mapFlagNames.size(); ++i) {
+ const unsigned int flags_excluding_i = TrimFlags(flags & ~(1U << i));
+ if (flags != flags_excluding_i && std::find(flags_combos.begin(), flags_combos.end(), flags_excluding_i) != flags_combos.end()) {
+ flags_combos.push_back(flags_excluding_i);
+ }
+ }
+ return flags_combos;
+}
+
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(tx_valid)
{
// Read tests from test/data/tx_valid.json
- // Format is an array of arrays
- // Inner arrays are either [ "comment" ]
- // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
- // ... where all scripts are stringified scripts.
- //
- // verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
- ScriptError err;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
@@ -159,24 +221,36 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_CHECK(state.IsValid());
PrecomputedTransactionData txdata(tx);
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
- {
- BOOST_ERROR("Bad test: " << strTest);
- break;
+ unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
+
+ // Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
+ if (~verify_flags != FillFlags(~verify_flags)) {
+ BOOST_ERROR("Bad test flags: " << strTest);
+ }
+
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true)) {
+ BOOST_ERROR("Tx unexpectedly failed: " << strTest);
+ }
+
+ // Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
+ for (size_t i = 0; i < mapFlagNames.size(); ++i) {
+ // Removing individual flags
+ unsigned int flags = TrimFlags(~(verify_flags | (1U << i)));
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) {
+ BOOST_ERROR("Tx unexpectedly failed with flag " << ToString(i) << " unset: " << strTest);
+ }
+ // Removing random combinations of flags
+ flags = TrimFlags(~(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size())));
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) {
+ BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags) << ": " << strTest);
}
+ }
- CAmount amount = 0;
- if (mapprevOutValues.count(tx.vin[i].prevout)) {
- amount = mapprevOutValues[tx.vin[i].prevout];
+ // Check that flags are maximal: transaction should fail if any unset flags are set.
+ for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) {
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~flags_excluding_one, txdata, strTest, /* expect_valid */ false)) {
+ BOOST_ERROR("Too many flags unset: " << strTest);
}
- unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
- const CScriptWitness *witness = &tx.vin[i].scriptWitness;
- BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
- witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err),
- strTest);
- BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
@@ -185,17 +259,8 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_AUTO_TEST_CASE(tx_invalid)
{
// Read tests from test/data/tx_invalid.json
- // Format is an array of arrays
- // Inner arrays are either [ "comment" ]
- // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
- // ... where all scripts are stringified scripts.
- //
- // verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
- // Initialize to SCRIPT_ERR_OK. The tests expect err to be changed to a
- // value other than SCRIPT_ERR_OK.
- ScriptError err = SCRIPT_ERR_OK;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
@@ -241,28 +306,39 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
CTransaction tx(deserialize, stream);
TxValidationState state;
- fValid = CheckTransaction(tx, state) && state.IsValid();
+ if (!CheckTransaction(tx, state) || state.IsInvalid()) {
+ BOOST_CHECK_MESSAGE(test[2].get_str() == "BADTX", strTest);
+ continue;
+ }
PrecomputedTransactionData txdata(tx);
- for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
- {
- if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
- {
- BOOST_ERROR("Bad test: " << strTest);
- break;
+ unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
+
+ // Not using FillFlags() in the main test, in order to detect invalid verifyFlags combination
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false)) {
+ BOOST_ERROR("Tx unexpectedly passed: " << strTest);
+ }
+
+ // Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
+ for (size_t i = 0; i < mapFlagNames.size(); i++) {
+ unsigned int flags = FillFlags(verify_flags | (1U << i));
+ // Adding individual flags
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) {
+ BOOST_ERROR("Tx unexpectedly passed with flag " << ToString(i) << " set: " << strTest);
}
+ // Adding random combinations of flags
+ flags = FillFlags(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size()));
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) {
+ BOOST_ERROR("Tx unexpectedly passed with random flags " << ToString(flags) << ": " << strTest);
+ }
+ }
- unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
- CAmount amount = 0;
- if (mapprevOutValues.count(tx.vin[i].prevout)) {
- amount = mapprevOutValues[tx.vin[i].prevout];
+ // Check that flags are minimal: transaction should succeed if any set flags are unset.
+ for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) {
+ if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags_excluding_one, txdata, strTest, /* expect_valid */ true)) {
+ BOOST_ERROR("Too many flags set: " << strTest);
}
- const CScriptWitness *witness = &tx.vin[i].scriptWitness;
- fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
- witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
}
- BOOST_CHECK_MESSAGE(!fValid, strTest);
- BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 7e6246d68f..8d14071297 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -30,25 +30,21 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
- TxValidationState state;
-
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
+ const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
+ true /* bypass_limits */);
- BOOST_CHECK_EQUAL(
- false,
- AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
- nullptr /* plTxnReplaced */,
- true /* bypass_limits */));
+ BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
// Check that the transaction hasn't been added to mempool.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
// Check that the validation state reflects the unsuccessful attempt.
- BOOST_CHECK(state.IsInvalid());
- BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
- BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS);
+ BOOST_CHECK(result.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
+ BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index bed2ba3608..3244b58082 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -13,7 +13,10 @@
#include <boost/test/unit_test.hpp>
-bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
+bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
+ const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
+ bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
@@ -28,9 +31,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- TxValidationState state;
- return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(tx),
+ true /* bypass_limits */);
+ return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
// Create a double-spend of mature coinbase txn:
diff --git a/src/test/util/logging.cpp b/src/test/util/logging.cpp
index c1dd47f06c..66f4efc139 100644
--- a/src/test/util/logging.cpp
+++ b/src/test/util/logging.cpp
@@ -7,7 +7,6 @@
#include <logging.h>
#include <noui.h>
#include <tinyformat.h>
-#include <util/memory.h>
#include <stdexcept>
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 0c6487fbfa..ba1edba0ae 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -42,7 +42,7 @@ std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coi
{
auto block = std::make_shared<CBlock>(
BlockAssembler{*Assert(node.mempool), Params()}
- .CreateNewBlock(coinbase_scriptPubKey)
+ .CreateNewBlock(::ChainstateActive(), coinbase_scriptPubKey)
->block);
LOCK(cs_main);
diff --git a/src/test/util/script.h b/src/test/util/script.h
new file mode 100644
index 0000000000..abd14c2067
--- /dev/null
+++ b/src/test/util/script.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2021 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_TEST_UTIL_SCRIPT_H
+#define BITCOIN_TEST_UTIL_SCRIPT_H
+
+#include <crypto/sha256.h>
+#include <script/script.h>
+
+static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
+static const CScript P2WSH_OP_TRUE{
+ CScript{}
+ << OP_0
+ << ToByteVector([] {
+ uint256 hash;
+ CSHA256().Write(WITNESS_STACK_ELEM_OP_TRUE.data(), WITNESS_STACK_ELEM_OP_TRUE.size()).Finalize(hash.begin());
+ return hash;
+ }())};
+
+#endif // BITCOIN_TEST_UTIL_SCRIPT_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index b9f3f8c955..f866c2a1f9 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -25,7 +25,6 @@
#include <script/sigcache.h>
#include <streams.h>
#include <txdb.h>
-#include <util/memory.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
@@ -77,6 +76,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
{
"dummy",
"-printtoconsole=0",
+ "-logsourcelocations",
"-logtimemicros",
"-logthreadnames",
"-debug",
@@ -130,7 +130,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
{
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
- m_node.scheduler = MakeUnique<CScheduler>();
+ m_node.scheduler = std::make_unique<CScheduler>();
m_node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); });
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
@@ -178,7 +178,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
assert(!::ChainstateActive().CanFlushToDisk());
::ChainstateActive().InitCoinsCache(1 << 23);
assert(::ChainstateActive().CanFlushToDisk());
- if (!LoadGenesisBlock(chainparams)) {
+ if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
@@ -187,8 +187,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
- m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
+ m_node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, m_node.banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool,
false);
@@ -199,14 +199,43 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup()
+TestChain100Setup::TestChain100Setup(bool deterministic)
{
+ m_deterministic = deterministic;
+
+ if (m_deterministic) {
+ SetMockTime(1598887952);
+ constexpr std::array<unsigned char, 32> vchKey = {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+ }
+ };
+ coinbaseKey.Set(vchKey.begin(), vchKey.end(), false);
+ } else {
+ coinbaseKey.MakeNewKey(true);
+ }
+
// Generate a 100-block chain:
- coinbaseKey.MakeNewKey(true);
+ this->mineBlocks(COINBASE_MATURITY);
+
+ if (m_deterministic) {
+ LOCK(::cs_main);
+ assert(
+ m_node.chainman->ActiveChain().Tip()->GetBlockHash().ToString() ==
+ "49c95db1e470fed04496d801c9d8fbb78155d2c7f855232c918823d2c17d0cf6");
+ }
+}
+
+void TestChain100Setup::mineBlocks(int num_blocks)
+{
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- for (int i = 0; i < COINBASE_MATURITY; i++) {
+ for (int i = 0; i < num_blocks; i++)
+ {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
+ if (m_deterministic) {
+ SetMockTime(GetTime() + 1);
+ }
m_coinbase_txns.push_back(b.vtx[0]);
}
}
@@ -215,13 +244,13 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
- RegenerateCommitments(block);
+ RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
@@ -231,9 +260,61 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
return block;
}
+
+CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactionRef input_transaction,
+ int input_vout,
+ int input_height,
+ CKey input_signing_key,
+ CScript output_destination,
+ CAmount output_amount)
+{
+ // Transaction we will submit to the mempool
+ CMutableTransaction mempool_txn;
+
+ // Create an input
+ COutPoint outpoint_to_spend(input_transaction->GetHash(), input_vout);
+ CTxIn input(outpoint_to_spend);
+ mempool_txn.vin.push_back(input);
+
+ // Create an output
+ CTxOut output(output_amount, output_destination);
+ mempool_txn.vout.push_back(output);
+
+ // Sign the transaction
+ // - Add the signing key to a keystore
+ FillableSigningProvider keystore;
+ keystore.AddKey(input_signing_key);
+ // - Populate a CoinsViewCache with the unspent output
+ CCoinsView coins_view;
+ CCoinsViewCache coins_cache(&coins_view);
+ AddCoins(coins_cache, *input_transaction.get(), input_height);
+ // - Use GetCoin to properly populate utxo_to_spend,
+ Coin utxo_to_spend;
+ assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
+ // - Then add it to a map to pass in to SignTransaction
+ std::map<COutPoint, Coin> input_coins;
+ input_coins.insert({outpoint_to_spend, utxo_to_spend});
+ // - Default signature hashing type
+ int nHashType = SIGHASH_ALL;
+ std::map<int, std::string> input_errors;
+ assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
+
+ // Add transaction to the mempool
+ {
+ LOCK(cs_main);
+ const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
+ }
+
+ return mempool_txn;
+}
+
TestChain100Setup::~TestChain100Setup()
{
gArgs.ForceSetArg("-segwitheight", "0");
+ if (m_deterministic) {
+ SetMockTime(0);
+ }
}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 331c1235cb..7323f1f0b6 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -15,6 +15,7 @@
#include <txmempool.h>
#include <util/check.h>
#include <util/string.h>
+#include <util/vector.h>
#include <type_traits>
#include <vector>
@@ -78,7 +79,6 @@ struct BasicTestingSetup {
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {});
~BasicTestingSetup();
-private:
const fs::path m_path_root;
};
@@ -112,7 +112,7 @@ class CScript;
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public RegTestingSetup {
- TestChain100Setup();
+ TestChain100Setup(bool deterministic = false);
/**
* Create a new block with just given transactions, coinbase paying to
@@ -121,12 +121,55 @@ struct TestChain100Setup : public RegTestingSetup {
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey);
+ //! Mine a series of new blocks on the active chain.
+ void mineBlocks(int num_blocks);
+
+ /**
+ * Create a transaction and submit to the mempool.
+ *
+ * @param input_transaction The transaction to spend
+ * @param input_vout The vout to spend from the input_transaction
+ * @param input_height The height of the block that included the input_transaction
+ * @param input_signing_key The key to spend the input_transaction
+ * @param output_destination Where to send the output
+ * @param output_amount How much to send
+ */
+ CMutableTransaction CreateValidMempoolTransaction(CTransactionRef input_transaction,
+ int input_vout,
+ int input_height,
+ CKey input_signing_key,
+ CScript output_destination,
+ CAmount output_amount = CAmount(1 * COIN));
+
~TestChain100Setup();
+ bool m_deterministic;
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
+
+struct TestChain100DeterministicSetup : public TestChain100Setup {
+ TestChain100DeterministicSetup() : TestChain100Setup(true) { }
+};
+
+/**
+ * Make a test setup that has disk access to the debug.log file disabled. Can
+ * be used in "hot loops", for example fuzzing or benchmarking.
+ */
+template <class T = const BasicTestingSetup>
+std::unique_ptr<T> MakeNoLogFileContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {})
+{
+ const std::vector<const char*> arguments = Cat(
+ {
+ "-nodebuglogfile",
+ "-nodebug",
+ },
+ extra_args);
+
+ return std::make_unique<T>(chain_name, arguments);
+}
+
class CTxMemPoolEntry;
struct TestMemPoolEntryHelper
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 4133f2623b..98a322eae5 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -7,12 +7,12 @@
#include <clientversion.h>
#include <hash.h> // For Hash()
#include <key.h> // For CKey
-#include <optional.h>
#include <sync.h>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
#include <test/util/str.h>
#include <uint256.h>
+#include <util/getuniquepath.h>
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/spanparsing.h>
@@ -22,6 +22,7 @@
#include <util/vector.h>
#include <array>
+#include <optional>
#include <stdint.h>
#include <string.h>
#include <thread>
@@ -37,6 +38,7 @@
#include <boost/test/unit_test.hpp>
using namespace std::literals;
+static const std::string STRING_WITH_EMBEDDED_NULL_CHAR{"1"s "\0" "1"s};
/* defined in logging.cpp */
namespace BCLog {
@@ -70,7 +72,7 @@ BOOST_AUTO_TEST_CASE(util_datadir)
BOOST_AUTO_TEST_CASE(util_check)
{
// Check that Assert can forward
- const std::unique_ptr<int> p_two = Assert(MakeUnique<int>(2));
+ const std::unique_ptr<int> p_two = Assert(std::make_unique<int>(2));
// Check that Assert works on lvalues and rvalues
const int two = *Assert(p_two);
Assert(two == 2);
@@ -226,9 +228,9 @@ public:
bool default_int = false;
bool default_bool = false;
const char* string_value = nullptr;
- Optional<int64_t> int_value;
- Optional<bool> bool_value;
- Optional<std::vector<std::string>> list_value;
+ std::optional<int64_t> int_value;
+ std::optional<bool> bool_value;
+ std::optional<std::vector<std::string>> list_value;
const char* error = nullptr;
explicit Expect(util::SettingsValue s) : setting(std::move(s)) {}
@@ -1179,6 +1181,16 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000), "0.000001");
BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000), "0.0000001");
BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000), "0.00000001");
+
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max()), "92233720368.54775807");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 1), "92233720368.54775806");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 2), "92233720368.54775805");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::max() - 3), "92233720368.54775804");
+ // ...
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 3), "-92233720368.54775805");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 2), "-92233720368.54775806");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min() + 1), "-92233720368.54775807");
+ BOOST_CHECK_EQUAL(FormatMoney(std::numeric_limits<CAmount>::min()), "-92233720368.54775808");
}
BOOST_AUTO_TEST_CASE(util_ParseMoney)
@@ -1261,7 +1273,7 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
// Parsing strings with embedded NUL characters should fail
BOOST_CHECK(!ParseMoney("\0-1"s, ret));
- BOOST_CHECK(!ParseMoney("\0" "1"s, ret));
+ BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR, ret));
BOOST_CHECK(!ParseMoney("1\0"s, ret));
}
@@ -1439,9 +1451,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32)
BOOST_CHECK(!ParseInt32("aap", &n));
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
- const char test_bytes[] = {'1', 0, '1'};
- std::string teststr(test_bytes, sizeof(test_bytes));
- BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs
+ BOOST_CHECK(!ParseInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseInt32("-2147483649", nullptr));
BOOST_CHECK(!ParseInt32("2147483648", nullptr));
@@ -1469,9 +1479,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64)
BOOST_CHECK(!ParseInt64("1a", &n));
BOOST_CHECK(!ParseInt64("aap", &n));
BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex
- const char test_bytes[] = {'1', 0, '1'};
- std::string teststr(test_bytes, sizeof(test_bytes));
- BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs
+ BOOST_CHECK(!ParseInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr));
BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr));
@@ -1479,6 +1487,42 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64)
BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr));
}
+BOOST_AUTO_TEST_CASE(test_ParseUInt8)
+{
+ uint8_t n;
+ // Valid values
+ BOOST_CHECK(ParseUInt8("255", nullptr));
+ BOOST_CHECK(ParseUInt8("0", &n) && n == 0);
+ BOOST_CHECK(ParseUInt8("255", &n) && n == 255);
+ BOOST_CHECK(ParseUInt8("0255", &n) && n == 255); // no octal
+ BOOST_CHECK(ParseUInt8("255", &n) && n == static_cast<uint8_t>(255));
+ BOOST_CHECK(ParseUInt8("+255", &n) && n == 255);
+ BOOST_CHECK(ParseUInt8("00000000000000000012", &n) && n == 12);
+ BOOST_CHECK(ParseUInt8("00000000000000000000", &n) && n == 0);
+ // Invalid values
+ BOOST_CHECK(!ParseUInt8("-00000000000000000000", &n));
+ BOOST_CHECK(!ParseUInt8("", &n));
+ BOOST_CHECK(!ParseUInt8(" 1", &n)); // no padding inside
+ BOOST_CHECK(!ParseUInt8(" -1", &n));
+ BOOST_CHECK(!ParseUInt8("++1", &n));
+ BOOST_CHECK(!ParseUInt8("+-1", &n));
+ BOOST_CHECK(!ParseUInt8("-+1", &n));
+ BOOST_CHECK(!ParseUInt8("--1", &n));
+ BOOST_CHECK(!ParseUInt8("-1", &n));
+ BOOST_CHECK(!ParseUInt8("1 ", &n));
+ BOOST_CHECK(!ParseUInt8("1a", &n));
+ BOOST_CHECK(!ParseUInt8("aap", &n));
+ BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex
+ BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex
+ BOOST_CHECK(!ParseUInt8(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
+ // Overflow and underflow
+ BOOST_CHECK(!ParseUInt8("-255", &n));
+ BOOST_CHECK(!ParseUInt8("256", &n));
+ BOOST_CHECK(!ParseUInt8("-123", &n));
+ BOOST_CHECK(!ParseUInt8("-123", nullptr));
+ BOOST_CHECK(!ParseUInt8("256", nullptr));
+}
+
BOOST_AUTO_TEST_CASE(test_ParseUInt32)
{
uint32_t n;
@@ -1508,9 +1552,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt32)
BOOST_CHECK(!ParseUInt32("aap", &n));
BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex
BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex
- const char test_bytes[] = {'1', 0, '1'};
- std::string teststr(test_bytes, sizeof(test_bytes));
- BOOST_CHECK(!ParseUInt32(teststr, &n)); // no embedded NULs
+ BOOST_CHECK(!ParseUInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt32("-2147483648", &n));
BOOST_CHECK(!ParseUInt32("4294967296", &n));
@@ -1539,9 +1581,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt64)
BOOST_CHECK(!ParseUInt64("1a", &n));
BOOST_CHECK(!ParseUInt64("aap", &n));
BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex
- const char test_bytes[] = {'1', 0, '1'};
- std::string teststr(test_bytes, sizeof(test_bytes));
- BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs
+ BOOST_CHECK(!ParseUInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr));
BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr));
@@ -1571,9 +1611,7 @@ BOOST_AUTO_TEST_CASE(test_ParseDouble)
BOOST_CHECK(!ParseDouble("1a", &n));
BOOST_CHECK(!ParseDouble("aap", &n));
BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex
- const char test_bytes[] = {'1', 0, '1'};
- std::string teststr(test_bytes, sizeof(test_bytes));
- BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs
+ BOOST_CHECK(!ParseDouble(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
// Overflow and underflow
BOOST_CHECK(!ParseDouble("-1e10000", nullptr));
BOOST_CHECK(!ParseDouble("1e10000", nullptr));
@@ -1816,7 +1854,7 @@ BOOST_AUTO_TEST_CASE(test_DirIsWritable)
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
- tmpdirname = tmpdirname / fs::unique_path();
+ tmpdirname = GetUniquePath(tmpdirname);
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
fs::create_directory(tmpdirname);
@@ -2200,4 +2238,17 @@ BOOST_AUTO_TEST_CASE(message_hash)
BOOST_CHECK_NE(message_hash1, signature_hash);
}
+BOOST_AUTO_TEST_CASE(remove_prefix)
+{
+ BOOST_CHECK_EQUAL(RemovePrefix("./util/system.h", "./"), "util/system.h");
+ BOOST_CHECK_EQUAL(RemovePrefix("foo", "foo"), "");
+ BOOST_CHECK_EQUAL(RemovePrefix("foo", "fo"), "o");
+ BOOST_CHECK_EQUAL(RemovePrefix("foo", "f"), "oo");
+ BOOST_CHECK_EQUAL(RemovePrefix("foo", ""), "foo");
+ BOOST_CHECK_EQUAL(RemovePrefix("fo", "foo"), "fo");
+ BOOST_CHECK_EQUAL(RemovePrefix("f", "foo"), "f");
+ BOOST_CHECK_EQUAL(RemovePrefix("", "foo"), "");
+ BOOST_CHECK_EQUAL(RemovePrefix("", ""), "");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index ec45d9a434..f3fc83078f 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -11,6 +11,7 @@
#include <pow.h>
#include <random.h>
#include <script/standard.h>
+#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h>
@@ -18,8 +19,6 @@
#include <thread>
-static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};
-
namespace validation_block_tests {
struct MinerTestingSetup : public RegTestingSetup {
std::shared_ptr<CBlock> Block(const uint256& prev_hash);
@@ -64,27 +63,17 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
- CScript pubKey;
- pubKey << i++ << OP_TRUE;
-
- auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
+ auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), CScript{} << i++ << OP_TRUE);
auto pblock = std::make_shared<CBlock>(ptemplate->block);
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
- pubKey.clear();
- {
- WitnessV0ScriptHash witness_program;
- CSHA256().Write(&V_OP_TRUE[0], V_OP_TRUE.size()).Finalize(witness_program.begin());
- pubKey << OP_0 << ToByteVector(witness_program);
- }
-
// Make the coinbase transaction with two outputs:
// One zero-value one that has a unique pubkey to make sure that blocks at the same height can have a different hash
// Another one that has the coinbase reward in a P2WSH with OP_TRUE as witness program to make it easy to spend
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.vout.resize(2);
- txCoinbase.vout[1].scriptPubKey = pubKey;
+ txCoinbase.vout[1].scriptPubKey = P2WSH_OP_TRUE;
txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue;
txCoinbase.vout[0].nValue = 0;
txCoinbase.vin[0].scriptWitness.SetNull();
@@ -254,7 +243,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
for (int num_txs = 22; num_txs > 0; --num_txs) {
CMutableTransaction mtx;
mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}});
- mtx.vin[0].scriptWitness.stack.push_back(V_OP_TRUE);
+ mtx.vin[0].scriptWitness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE);
mtx.vout.push_back(last_mined->vtx[0]->vout[1]);
mtx.vout[0].nValue -= 1000;
txs.push_back(MakeTransactionRef(mtx));
@@ -283,15 +272,9 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Add the txs to the tx pool
{
LOCK(cs_main);
- TxValidationState state;
- std::list<CTransactionRef> plTxnReplaced;
for (const auto& tx : txs) {
- BOOST_REQUIRE(AcceptToMemoryPool(
- *m_node.mempool,
- state,
- tx,
- &plTxnReplaced,
- /* bypass_limits */ false));
+ const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, tx, false /* bypass_limits */);
+ BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
@@ -342,7 +325,7 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
{
CScript pubKey;
pubKey << 1 << OP_TRUE;
- auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
+ auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), pubKey);
CBlock pblock = ptemplate->block;
CTxOut witness;
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 3d8570e27c..94d4277019 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -4,13 +4,18 @@
//
#include <chainparams.h>
#include <consensus/validation.h>
+#include <node/utxo_snapshot.h>
#include <random.h>
+#include <rpc/blockchain.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <validation.h>
#include <validationinterface.h>
+#include <tinyformat.h>
+#include <univalue.h>
+
#include <vector>
#include <boost/test/unit_test.hpp>
@@ -28,6 +33,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
std::vector<CChainState*> chainstates;
const CChainParams& chainparams = Params();
+ BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
+
// Create a legacy (IBD) chainstate.
//
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
@@ -54,10 +61,17 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
auto& validated_cs = manager.ValidatedChainstate();
BOOST_CHECK_EQUAL(&validated_cs, &c1);
+ BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
+
// Create a snapshot-based chainstate.
//
- CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
+ const uint256 snapshot_blockhash = GetRandHash();
+ CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
+ mempool, snapshot_blockhash));
chainstates.push_back(&c2);
+
+ BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
+
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
@@ -155,4 +169,175 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
}
+auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
+
+template<typename F = decltype(NoMalleation)>
+static bool
+CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
+{
+ // Write out a snapshot to the test's tempdir.
+ //
+ int height;
+ WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
+ fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
+ FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
+ CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
+
+ UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
+ BOOST_TEST_MESSAGE(
+ "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
+
+ // Read the written snapshot in and then activate it.
+ //
+ FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
+ CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
+ SnapshotMetadata metadata;
+ auto_infile >> metadata;
+
+ malleation(auto_infile, metadata);
+
+ return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
+}
+
+//! Test basic snapshot activation.
+BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100DeterministicSetup)
+{
+ ChainstateManager& chainman = *Assert(m_node.chainman);
+
+ size_t initial_size;
+ size_t initial_total_coins{100};
+
+ // Make some initial assertions about the contents of the chainstate.
+ {
+ LOCK(::cs_main);
+ CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
+ initial_size = ibd_coinscache.GetCacheSize();
+ size_t total_coins{0};
+
+ for (CTransactionRef& txn : m_coinbase_txns) {
+ COutPoint op{txn->GetHash(), 0};
+ BOOST_CHECK(ibd_coinscache.HaveCoin(op));
+ total_coins++;
+ }
+
+ BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
+ BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
+ }
+
+ // Snapshot should refuse to load at this height.
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+ BOOST_CHECK(chainman.ActiveChainstate().m_from_snapshot_blockhash.IsNull());
+ BOOST_CHECK_EQUAL(
+ chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ chainman.SnapshotBlockhash().value_or(uint256()));
+
+ // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
+ // be found.
+ mineBlocks(10);
+ initial_size += 10;
+ initial_total_coins += 10;
+
+ // Should not load malleated snapshots
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // A UTXO is missing but count is correct
+ metadata.m_coins_count -= 1;
+
+ COutPoint outpoint;
+ Coin coin;
+
+ auto_infile >> outpoint;
+ auto_infile >> coin;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Coins count is larger than coins in file
+ metadata.m_coins_count += 1;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Coins count is smaller than coins in file
+ metadata.m_coins_count -= 1;
+ }));
+
+ BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+
+ // Ensure our active chain is the snapshot chainstate.
+ BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash.IsNull());
+ BOOST_CHECK_EQUAL(
+ chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ *chainman.SnapshotBlockhash());
+
+ // To be checked against later when we try loading a subsequent snapshot.
+ uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
+
+ // Make some assertions about the both chainstates. These checks ensure the
+ // legacy chainstate hasn't changed and that the newly created chainstate
+ // reflects the expected content.
+ {
+ LOCK(::cs_main);
+ int chains_tested{0};
+
+ for (CChainState* chainstate : chainman.GetAll()) {
+ BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
+ CCoinsViewCache& coinscache = chainstate->CoinsTip();
+
+ // Both caches will be empty initially.
+ BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
+
+ size_t total_coins{0};
+
+ for (CTransactionRef& txn : m_coinbase_txns) {
+ COutPoint op{txn->GetHash(), 0};
+ BOOST_CHECK(coinscache.HaveCoin(op));
+ total_coins++;
+ }
+
+ BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
+ BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
+ chains_tested++;
+ }
+
+ BOOST_CHECK_EQUAL(chains_tested, 2);
+ }
+
+ // Mine some new blocks on top of the activated snapshot chainstate.
+ constexpr size_t new_coins{100};
+ mineBlocks(new_coins); // Defined in TestChain100Setup.
+
+ {
+ LOCK(::cs_main);
+ size_t coins_in_active{0};
+ size_t coins_in_ibd{0};
+ size_t coins_missing_ibd{0};
+
+ for (CChainState* chainstate : chainman.GetAll()) {
+ BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
+ CCoinsViewCache& coinscache = chainstate->CoinsTip();
+ bool is_ibd = chainman.IsBackgroundIBD(chainstate);
+
+ for (CTransactionRef& txn : m_coinbase_txns) {
+ COutPoint op{txn->GetHash(), 0};
+ if (coinscache.HaveCoin(op)) {
+ (is_ibd ? coins_in_ibd : coins_in_active)++;
+ } else if (is_ibd) {
+ coins_missing_ibd++;
+ }
+ }
+ }
+
+ BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
+ BOOST_CHECK_EQUAL(coins_in_ibd, initial_total_coins);
+ BOOST_CHECK_EQUAL(coins_missing_ibd, new_coins);
+ }
+
+ // Snapshot should refuse to load after one has already loaded.
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+
+ // Snapshot blockhash should be unchanged.
+ BOOST_CHECK_EQUAL(
+ chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ loaded_snapshot_blockhash);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 9e37f14921..ecf9453094 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -5,6 +5,7 @@
#include <chainparams.h>
#include <net.h>
#include <signet.h>
+#include <uint256.h>
#include <validation.h>
#include <test/util/setup_common.h>
@@ -119,4 +120,27 @@ BOOST_AUTO_TEST_CASE(signet_parse_tests)
BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
}
+//! Test retrieval of valid assumeutxo values.
+BOOST_AUTO_TEST_CASE(test_assumeutxo)
+{
+ const auto params = CreateChainParams(*m_node.args, CBaseChainParams::REGTEST);
+
+ // These heights don't have assumeutxo configurations associated, per the contents
+ // of chainparams.cpp.
+ std::vector<int> bad_heights{0, 100, 111, 115, 209, 211};
+
+ for (auto empty : bad_heights) {
+ const auto out = ExpectedAssumeutxo(empty, *params);
+ BOOST_CHECK(!out);
+ }
+
+ const auto out110 = *ExpectedAssumeutxo(110, *params);
+ BOOST_CHECK_EQUAL(out110.hash_serialized, uint256S("76fd7334ac7c1baf57ddc0c626f073a655a35d98a4258cd1382c8cc2b8392e10"));
+ BOOST_CHECK_EQUAL(out110.nChainTx, (unsigned int)110);
+
+ const auto out210 = *ExpectedAssumeutxo(210, *params);
+ BOOST_CHECK_EQUAL(out210.hash_serialized, uint256S("9c5ed99ef98544b34f8920b6d1802f72ac28ae6e2bd2bd4c316ff10c230df3f2"));
+ BOOST_CHECK_EQUAL(out210.nChainTx, (unsigned int)210);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 50444f7bbe..8841a540f2 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -14,6 +14,18 @@
/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */
static int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
+static const std::string StateName(ThresholdState state)
+{
+ switch (state) {
+ case ThresholdState::DEFINED: return "DEFINED";
+ case ThresholdState::STARTED: return "STARTED";
+ case ThresholdState::LOCKED_IN: return "LOCKED_IN";
+ case ThresholdState::ACTIVE: return "ACTIVE";
+ case ThresholdState::FAILED: return "FAILED";
+ } // no default case, so the compiler can warn about missing cases
+ return "";
+}
+
static const Consensus::Params paramsDummy = Consensus::Params();
class TestConditionChecker : public AbstractThresholdConditionChecker
@@ -38,6 +50,13 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; }
};
+class TestNeverActiveConditionChecker : public TestConditionChecker
+{
+public:
+ int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
+ int64_t EndTime(const Consensus::Params& params) const override { return 1230768000; }
+};
+
#define CHECKERS 6
class VersionBitsTester
@@ -51,6 +70,8 @@ class VersionBitsTester
TestConditionChecker checker[CHECKERS];
// Another 6 that assume always active activation
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
+ // Another 6 that assume never active activation
+ TestNeverActiveConditionChecker checker_never[CHECKERS];
// Test counter (to identify failures)
int num;
@@ -65,6 +86,7 @@ public:
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
checker_always[i] = TestAlwaysActiveConditionChecker();
+ checker_never[i] = TestNeverActiveConditionChecker();
}
vpblock.clear();
return *this;
@@ -92,66 +114,40 @@ public:
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
- }
- }
- num++;
- return *this;
- }
- VersionBitsTester& TestDefined() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ // never active may go from DEFINED -> FAILED at the first period
+ const auto never_height = checker_never[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back());
+ BOOST_CHECK_MESSAGE(never_height == 0 || never_height == checker_never[i].Period(paramsDummy), strprintf("Test %i for StateSinceHeight (never active)", num));
}
}
num++;
return *this;
}
- VersionBitsTester& TestStarted() {
+ VersionBitsTester& TestState(ThresholdState exp) {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::STARTED, strprintf("Test %i for STARTED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
+ const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
+ ThresholdState got = checker[i].GetStateFor(pindex);
+ ThresholdState got_always = checker_always[i].GetStateFor(pindex);
+ ThresholdState got_never = checker_never[i].GetStateFor(pindex);
+ // nHeight of the next block. If vpblock is empty, the next (ie first)
+ // block should be the genesis block with nHeight == 0.
+ int height = pindex == nullptr ? 0 : pindex->nHeight + 1;
+ BOOST_CHECK_MESSAGE(got == exp, strprintf("Test %i for %s height %d (got %s)", num, StateName(exp), height, StateName(got)));
+ BOOST_CHECK_MESSAGE(got_always == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE height %d (got %s; always active case)", num, height, StateName(got_always)));
+ BOOST_CHECK_MESSAGE(got_never == ThresholdState::DEFINED|| got_never == ThresholdState::FAILED, strprintf("Test %i for DEFINED/FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
}
}
num++;
return *this;
}
- VersionBitsTester& TestLockedIn() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
-
- VersionBitsTester& TestActive() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
-
- VersionBitsTester& TestFailed() {
- for (int i = 0; i < CHECKERS; i++) {
- if (InsecureRandBits(i) == 0) {
- BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::FAILED, strprintf("Test %i for FAILED", num));
- BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
- }
- }
- num++;
- return *this;
- }
+ VersionBitsTester& TestDefined() { return TestState(ThresholdState::DEFINED); }
+ VersionBitsTester& TestStarted() { return TestState(ThresholdState::STARTED); }
+ VersionBitsTester& TestLockedIn() { return TestState(ThresholdState::LOCKED_IN); }
+ VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
+ VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; }
};
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 908ad35e1b..6666e49a2b 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -12,8 +12,10 @@
#include <net.h>
#include <netaddress.h>
#include <netbase.h>
+#include <util/readwritefile.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/time.h>
#include <deque>
#include <functional>
@@ -54,77 +56,6 @@ static const int MAX_LINE_LENGTH = 100000;
/****** Low-level TorControlConnection ********/
-/** Reply from Tor, can be single or multi-line */
-class TorControlReply
-{
-public:
- TorControlReply() { Clear(); }
-
- int code;
- std::vector<std::string> lines;
-
- void Clear()
- {
- code = 0;
- lines.clear();
- }
-};
-
-/** Low-level handling for Tor control connection.
- * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
- */
-class TorControlConnection
-{
-public:
- typedef std::function<void(TorControlConnection&)> ConnectionCB;
- typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
-
- /** Create a new TorControlConnection.
- */
- explicit TorControlConnection(struct event_base *base);
- ~TorControlConnection();
-
- /**
- * Connect to a Tor control port.
- * tor_control_center is address of the form host:port.
- * connected is the handler that is called when connection is successfully established.
- * disconnected is a handler that is called when the connection is broken.
- * Return true on success.
- */
- bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
-
- /**
- * Disconnect from Tor control port.
- */
- void Disconnect();
-
- /** Send a command, register a handler for the reply.
- * A trailing CRLF is automatically added.
- * Return true on success.
- */
- bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
-
- /** Response handlers for async replies */
- boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler;
-private:
- /** Callback when ready for use */
- std::function<void(TorControlConnection&)> connected;
- /** Callback when connection lost */
- std::function<void(TorControlConnection&)> disconnected;
- /** Libevent event base */
- struct event_base *base;
- /** Connection to control socket */
- struct bufferevent *b_conn;
- /** Message being received */
- TorControlReply message;
- /** Response handlers */
- std::deque<ReplyHandlerCB> reply_handlers;
-
- /** Libevent handlers: internal */
- static void readcb(struct bufferevent *bev, void *ctx);
- static void eventcb(struct bufferevent *bev, short what, void *ctx);
-};
-
TorControlConnection::TorControlConnection(struct event_base *_base):
base(_base), b_conn(nullptr)
{
@@ -361,101 +292,6 @@ std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
return mapping;
}
-/** Read full contents of a file and return them in a std::string.
- * Returns a pair <status, string>.
- * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
- *
- * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
- * (with len > maxsize) will be returned.
- */
-static std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
-{
- FILE *f = fsbridge::fopen(filename, "rb");
- if (f == nullptr)
- return std::make_pair(false,"");
- std::string retval;
- char buffer[128];
- size_t n;
- while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
- // Check for reading errors so we don't return any data if we couldn't
- // read the entire file (or up to maxsize)
- if (ferror(f)) {
- fclose(f);
- return std::make_pair(false,"");
- }
- retval.append(buffer, buffer+n);
- if (retval.size() > maxsize)
- break;
- }
- fclose(f);
- return std::make_pair(true,retval);
-}
-
-/** Write contents of std::string to a file.
- * @return true on success.
- */
-static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
-{
- FILE *f = fsbridge::fopen(filename, "wb");
- if (f == nullptr)
- return false;
- if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
- fclose(f);
- return false;
- }
- fclose(f);
- return true;
-}
-
-/****** Bitcoin specific TorController implementation ********/
-
-/** Controller that connects to Tor control socket, authenticate, then create
- * and maintain an ephemeral onion service.
- */
-class TorController
-{
-public:
- TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
- ~TorController();
-
- /** Get name of file to store private key in */
- fs::path GetPrivateKeyFile();
-
- /** Reconnect, after getting disconnected */
- void Reconnect();
-private:
- struct event_base* base;
- const std::string m_tor_control_center;
- TorControlConnection conn;
- std::string private_key;
- std::string service_id;
- bool reconnect;
- struct event *reconnect_ev;
- float reconnect_timeout;
- CService service;
- const CService m_target;
- /** Cookie for SAFECOOKIE auth */
- std::vector<uint8_t> cookie;
- /** ClientNonce for SAFECOOKIE auth */
- std::vector<uint8_t> clientNonce;
-
- /** Callback for ADD_ONION result */
- void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for AUTHENTICATE result */
- void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for AUTHCHALLENGE result */
- void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback for PROTOCOLINFO result */
- void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
- /** Callback after successful connection */
- void connected_cb(TorControlConnection& conn);
- /** Callback after connection lost or failed connection attempt */
- void disconnected_cb(TorControlConnection& conn);
-
- /** Callback for reconnect timer */
- static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
-};
-
TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
base(_base),
m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0),
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 00f19db6ae..7258f27cb6 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -8,7 +8,19 @@
#ifndef BITCOIN_TORCONTROL_H
#define BITCOIN_TORCONTROL_H
+#include <fs.h>
+#include <netaddress.h>
+
+#include <boost/signals2/signal.hpp>
+
+#include <event2/bufferevent.h>
+#include <event2/event.h>
+
+#include <cstdlib>
+#include <deque>
+#include <functional>
#include <string>
+#include <vector>
class CService;
@@ -21,4 +33,128 @@ void StopTorControl();
CService DefaultOnionServiceTarget();
+/** Reply from Tor, can be single or multi-line */
+class TorControlReply
+{
+public:
+ TorControlReply() { Clear(); }
+
+ int code;
+ std::vector<std::string> lines;
+
+ void Clear()
+ {
+ code = 0;
+ lines.clear();
+ }
+};
+
+/** Low-level handling for Tor control connection.
+ * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
+ */
+class TorControlConnection
+{
+public:
+ typedef std::function<void(TorControlConnection&)> ConnectionCB;
+ typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
+
+ /** Create a new TorControlConnection.
+ */
+ explicit TorControlConnection(struct event_base *base);
+ ~TorControlConnection();
+
+ /**
+ * Connect to a Tor control port.
+ * tor_control_center is address of the form host:port.
+ * connected is the handler that is called when connection is successfully established.
+ * disconnected is a handler that is called when the connection is broken.
+ * Return true on success.
+ */
+ bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
+
+ /**
+ * Disconnect from Tor control port.
+ */
+ void Disconnect();
+
+ /** Send a command, register a handler for the reply.
+ * A trailing CRLF is automatically added.
+ * Return true on success.
+ */
+ bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
+
+ /** Response handlers for async replies */
+ boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler;
+private:
+ /** Callback when ready for use */
+ std::function<void(TorControlConnection&)> connected;
+ /** Callback when connection lost */
+ std::function<void(TorControlConnection&)> disconnected;
+ /** Libevent event base */
+ struct event_base *base;
+ /** Connection to control socket */
+ struct bufferevent *b_conn;
+ /** Message being received */
+ TorControlReply message;
+ /** Response handlers */
+ std::deque<ReplyHandlerCB> reply_handlers;
+
+ /** Libevent handlers: internal */
+ static void readcb(struct bufferevent *bev, void *ctx);
+ static void eventcb(struct bufferevent *bev, short what, void *ctx);
+};
+
+/****** Bitcoin specific TorController implementation ********/
+
+/** Controller that connects to Tor control socket, authenticate, then create
+ * and maintain an ephemeral onion service.
+ */
+class TorController
+{
+public:
+ TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
+ TorController() : conn{nullptr} {
+ // Used for testing only.
+ }
+ ~TorController();
+
+ /** Get name of file to store private key in */
+ fs::path GetPrivateKeyFile();
+
+ /** Reconnect, after getting disconnected */
+ void Reconnect();
+private:
+ struct event_base* base;
+ const std::string m_tor_control_center;
+ TorControlConnection conn;
+ std::string private_key;
+ std::string service_id;
+ bool reconnect;
+ struct event *reconnect_ev = nullptr;
+ float reconnect_timeout;
+ CService service;
+ const CService m_target;
+ /** Cookie for SAFECOOKIE auth */
+ std::vector<uint8_t> cookie;
+ /** ClientNonce for SAFECOOKIE auth */
+ std::vector<uint8_t> clientNonce;
+
+public:
+ /** Callback for ADD_ONION result */
+ void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for AUTHENTICATE result */
+ void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for AUTHCHALLENGE result */
+ void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback for PROTOCOLINFO result */
+ void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
+ /** Callback after successful connection */
+ void connected_cb(TorControlConnection& conn);
+ /** Callback after connection lost or failed connection attempt */
+ void disconnected_cb(TorControlConnection& conn);
+
+ /** Callback for reconnect timer */
+ static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
+};
+
#endif /* BITCOIN_TORCONTROL_H */
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 72460e7c69..3a08e28c01 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -10,7 +10,6 @@
#include <random.h>
#include <shutdown.h>
#include <uint256.h>
-#include <util/memory.h>
#include <util/system.h>
#include <util/translation.h>
#include <util/vector.h>
@@ -41,17 +40,21 @@ struct CoinEntry {
}
CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) :
- m_db(MakeUnique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)),
+ m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)),
m_ldb_path(ldb_path),
m_is_memory(fMemory) { }
void CCoinsViewDB::ResizeCache(size_t new_cache_size)
{
- // Have to do a reset first to get the original `m_db` state to release its
- // filesystem lock.
- m_db.reset();
- m_db = MakeUnique<CDBWrapper>(
- m_ldb_path, new_cache_size, m_is_memory, /*fWipe*/ false, /*obfuscate*/ true);
+ // We can't do this operation with an in-memory DB since we'll lose all the coins upon
+ // reset.
+ if (!m_is_memory) {
+ // Have to do a reset first to get the original `m_db` state to release its
+ // filesystem lock.
+ m_db.reset();
+ m_db = std::make_unique<CDBWrapper>(
+ m_ldb_path, new_cache_size, m_is_memory, /*fWipe*/ false, /*obfuscate*/ true);
+ }
}
bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c370f9e981..faccd1ade0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -8,22 +8,23 @@
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
-#include <optional.h>
-#include <validation.h>
-#include <policy/policy.h>
#include <policy/fees.h>
+#include <policy/policy.h>
#include <policy/settings.h>
#include <reverse_iterator.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/system.h>
#include <util/time.h>
+#include <validation.h>
#include <validationinterface.h>
+#include <optional>
+
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp)
: tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight),
- spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), m_epoch(0)
+ spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{
nCountWithDescendants = 1;
nSizeWithDescendants = GetTxSize();
@@ -132,7 +133,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// include them, and update their CTxMemPoolEntry::m_parents to include this tx.
// we cache the in-mempool children to avoid duplicate updates
{
- const auto epoch = GetFreshEpoch();
+ WITH_FRESH_EPOCH(m_epoch);
for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
const uint256 &childHash = iter->second->GetHash();
txiter childIter = mapTx.find(childHash);
@@ -159,7 +160,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
// GetMemPoolParents() is only valid for entries in the mempool, so we
// iterate mapTx to find parents.
for (unsigned int i = 0; i < tx.vin.size(); i++) {
- Optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
+ std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
if (piter) {
staged_ancestors.insert(**piter);
if (staged_ancestors.size() + 1 > limitAncestorCount) {
@@ -396,7 +397,10 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
- if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);}
+ m_total_fee += entry.GetFee();
+ if (minerPolicyEstimator) {
+ minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
+ }
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
@@ -432,6 +436,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
vTxHashes.clear();
totalTxSize -= it->GetTxSize();
+ m_total_fee -= it->GetFee();
cachedInnerUsage -= it->DynamicMemoryUsage();
cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
mapTx.erase(it);
@@ -499,7 +504,7 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
RemoveStaged(setAllRemoves, false, reason);
}
-void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags)
+void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
{
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
AssertLockHeld(cs);
@@ -507,8 +512,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
- bool validLP = TestLockPointValidity(&lp);
- if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
+ if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags) || !CheckSequenceLocks(active_chainstate, *this, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
@@ -517,8 +523,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
continue;
- const Coin &coin = pcoins->AccessCoin(txin.prevout);
+ const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout);
if (m_check_ratio != 0) assert(!coin.IsSpent());
+ unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1;
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
txToRemove.insert(it);
break;
@@ -590,6 +597,7 @@ void CTxMemPool::_clear()
mapTx.clear();
mapNextTx.clear();
totalTxSize = 0;
+ m_total_fee = 0;
cachedInnerUsage = 0;
lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = false;
@@ -612,7 +620,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
}
-void CTxMemPool::check(const CCoinsViewCache *pcoins) const
+void CTxMemPool::check(CChainState& active_chainstate) const
{
if (m_check_ratio == 0) return;
@@ -623,15 +631,20 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
uint64_t checkTotal = 0;
+ CAmount check_total_fee{0};
uint64_t innerUsage = 0;
- CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
- const int64_t spendheight = g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate);
+ CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
+ CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
+ const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
+ assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
checkTotal += it->GetTxSize();
+ check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
@@ -646,7 +659,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
fDependsWait = true;
setParentCheck.insert(*it2);
} else {
- assert(pcoins->HaveCoin(txin.prevout));
+ assert(active_coins_tip.HaveCoin(txin.prevout));
}
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
@@ -726,6 +739,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
}
assert(totalTxSize == checkTotal);
+ assert(m_total_fee == check_total_fee);
assert(innerUsage == cachedInnerUsage);
}
@@ -877,11 +891,11 @@ const CTransaction* CTxMemPool::GetConflictTx(const COutPoint& prevout) const
return it == mapNextTx.end() ? nullptr : it->second;
}
-Optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const
+std::optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const
{
auto it = mapTx.find(txid);
if (it != mapTx.end()) return it;
- return Optional<txiter>{};
+ return {};
}
CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) const
@@ -1109,22 +1123,3 @@ void CTxMemPool::SetIsLoaded(bool loaded)
LOCK(cs);
m_is_loaded = loaded;
}
-
-
-CTxMemPool::EpochGuard CTxMemPool::GetFreshEpoch() const
-{
- return EpochGuard(*this);
-}
-CTxMemPool::EpochGuard::EpochGuard(const CTxMemPool& in) : pool(in)
-{
- assert(!pool.m_has_epoch_guard);
- ++pool.m_epoch;
- pool.m_has_epoch_guard = true;
-}
-
-CTxMemPool::EpochGuard::~EpochGuard()
-{
- // prevents stale results being used
- ++pool.m_epoch;
- pool.m_has_epoch_guard = false;
-}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0a9cd81ff5..9d4ea760e7 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -8,6 +8,7 @@
#include <atomic>
#include <map>
+#include <optional>
#include <set>
#include <string>
#include <utility>
@@ -16,11 +17,11 @@
#include <amount.h>
#include <coins.h>
#include <indirectmap.h>
-#include <optional.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
-#include <sync.h>
#include <random.h>
+#include <sync.h>
+#include <util/epochguard.h>
#include <util/hasher.h>
#include <boost/multi_index_container.hpp>
@@ -29,6 +30,7 @@
#include <boost/multi_index/sequenced_index.hpp>
class CBlockIndex;
+class CChainState;
extern RecursiveMutex cs_main;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
@@ -63,6 +65,7 @@ struct CompareIteratorByHash {
return a->GetTx().GetHash() < b->GetTx().GetHash();
}
};
+
/** \class CTxMemPoolEntry
*
* CTxMemPoolEntry stores data about the corresponding transaction, as well
@@ -155,7 +158,7 @@ public:
Children& GetMemPoolChildren() const { return m_children; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
- mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms
+ mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
};
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
@@ -478,14 +481,14 @@ private:
std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
CBlockPolicyEstimator* minerPolicyEstimator;
- uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
- uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
+ uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
+ CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee)
+ uint64_t cachedInnerUsage GUARDED_BY(cs); //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
mutable int64_t lastRollingFeeUpdate;
mutable bool blockSinceLastRollingFeeBump;
mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially
- mutable uint64_t m_epoch{0};
- mutable bool m_has_epoch_guard{false};
+ mutable Epoch m_epoch GUARDED_BY(cs);
// In-memory counter for external mempool tracking purposes.
// This number is incremented once every time a transaction
@@ -602,7 +605,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing.
*/
- void check(const CCoinsViewCache *pcoins) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
@@ -615,7 +618,7 @@ public:
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
- void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -641,7 +644,7 @@ public:
const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Returns an iterator to the given hash, if found */
- Optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ std::optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Translate a set of hashes into a set of pool iterators to avoid repeated lookups */
setEntries GetIterSet(const std::set<uint256>& hashes) const EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -664,7 +667,7 @@ public:
* for). Note: vHashesToUpdate should be the set of transactions from the
* disconnected block that have been accepted back into the mempool.
*/
- void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
/** Try to calculate all in-mempool ancestors of entry.
* (these are all calculated including the tx itself)
@@ -724,6 +727,12 @@ public:
return totalTxSize;
}
+ CAmount GetTotalFee() const EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+ AssertLockHeld(cs);
+ return m_total_fee;
+ }
+
bool exists(const GenTxid& gtxid) const
{
LOCK(cs);
@@ -819,52 +828,22 @@ private:
*/
void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
- /** EpochGuard: RAII-style guard for using epoch-based graph traversal algorithms.
- * When walking ancestors or descendants, we generally want to avoid
- * visiting the same transactions twice. Some traversal algorithms use
- * std::set (or setEntries) to deduplicate the transaction we visit.
- * However, use of std::set is algorithmically undesirable because it both
- * adds an asymptotic factor of O(log n) to traverals cost and triggers O(n)
- * more dynamic memory allocations.
- * In many algorithms we can replace std::set with an internal mempool
- * counter to track the time (or, "epoch") that we began a traversal, and
- * check + update a per-transaction epoch for each transaction we look at to
- * determine if that transaction has not yet been visited during the current
- * traversal's epoch.
- * Algorithms using std::set can be replaced on a one by one basis.
- * Both techniques are not fundamentally incompatible across the codebase.
- * Generally speaking, however, the remaining use of std::set for mempool
- * traversal should be viewed as a TODO for replacement with an epoch based
- * traversal, rather than a preference for std::set over epochs in that
- * algorithm.
- */
- class EpochGuard {
- const CTxMemPool& pool;
- public:
- explicit EpochGuard(const CTxMemPool& in);
- ~EpochGuard();
- };
- // N.B. GetFreshEpoch modifies mutable state via the EpochGuard construction
- // (and later destruction)
- EpochGuard GetFreshEpoch() const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
/** visited marks a CTxMemPoolEntry as having been traversed
- * during the lifetime of the most recently created EpochGuard
+ * during the lifetime of the most recently created Epoch::Guard
* and returns false if we are the first visitor, true otherwise.
*
- * An EpochGuard must be held when visited is called or an assert will be
+ * An Epoch::Guard must be held when visited is called or an assert will be
* triggered.
*
*/
- bool visited(txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
- assert(m_has_epoch_guard);
- bool ret = it->m_epoch >= m_epoch;
- it->m_epoch = std::max(it->m_epoch, m_epoch);
- return ret;
+ bool visited(const txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch)
+ {
+ return m_epoch.visited(it->m_epoch_marker);
}
- bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs) {
- assert(m_has_epoch_guard);
+ bool visited(std::optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch)
+ {
+ assert(m_epoch.guarded()); // verify guard even when it==nullopt
return !it || visited(*it);
}
};
diff --git a/src/txorphanage.cpp b/src/txorphanage.cpp
new file mode 100644
index 0000000000..ed4783f1a5
--- /dev/null
+++ b/src/txorphanage.cpp
@@ -0,0 +1,202 @@
+// Copyright (c) 2021 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 <txorphanage.h>
+
+#include <consensus/validation.h>
+#include <logging.h>
+#include <policy/policy.h>
+
+#include <cassert>
+
+/** Expiration time for orphan transactions in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
+/** Minimum time between orphan transactions expire time checks in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+
+RecursiveMutex g_cs_orphans;
+
+bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ const uint256& hash = tx->GetHash();
+ if (m_orphans.count(hash))
+ return false;
+
+ // Ignore big transactions, to avoid a
+ // send-big-orphans memory exhaustion attack. If a peer has a legitimate
+ // large transaction with a missing parent then we assume
+ // it will rebroadcast it later, after the parent transaction(s)
+ // have been mined or received.
+ // 100 orphans, each of which is at most 100,000 bytes big is
+ // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
+ unsigned int sz = GetTransactionWeight(*tx);
+ if (sz > MAX_STANDARD_TX_WEIGHT)
+ {
+ LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
+ return false;
+ }
+
+ auto ret = m_orphans.emplace(hash, OrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
+ assert(ret.second);
+ m_orphan_list.push_back(ret.first);
+ // Allow for lookups in the orphan pool by wtxid, as well as txid
+ m_wtxid_to_orphan_it.emplace(tx->GetWitnessHash(), ret.first);
+ for (const CTxIn& txin : tx->vin) {
+ m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
+ }
+
+ LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
+ m_orphans.size(), m_outpoint_to_orphan_it.size());
+ return true;
+}
+
+int TxOrphanage::EraseTx(const uint256& txid)
+{
+ AssertLockHeld(g_cs_orphans);
+ std::map<uint256, OrphanTx>::iterator it = m_orphans.find(txid);
+ if (it == m_orphans.end())
+ return 0;
+ for (const CTxIn& txin : it->second.tx->vin)
+ {
+ auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
+ if (itPrev == m_outpoint_to_orphan_it.end())
+ continue;
+ itPrev->second.erase(it);
+ if (itPrev->second.empty())
+ m_outpoint_to_orphan_it.erase(itPrev);
+ }
+
+ size_t old_pos = it->second.list_pos;
+ assert(m_orphan_list[old_pos] == it);
+ if (old_pos + 1 != m_orphan_list.size()) {
+ // Unless we're deleting the last entry in m_orphan_list, move the last
+ // entry to the position we're deleting.
+ auto it_last = m_orphan_list.back();
+ m_orphan_list[old_pos] = it_last;
+ it_last->second.list_pos = old_pos;
+ }
+ m_orphan_list.pop_back();
+ m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
+
+ m_orphans.erase(it);
+ return 1;
+}
+
+void TxOrphanage::EraseForPeer(NodeId peer)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ int nErased = 0;
+ std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
+ while (iter != m_orphans.end())
+ {
+ std::map<uint256, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
+ if (maybeErase->second.fromPeer == peer)
+ {
+ nErased += EraseTx(maybeErase->second.tx->GetHash());
+ }
+ }
+ if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
+}
+
+unsigned int TxOrphanage::LimitOrphans(unsigned int max_orphans)
+{
+ AssertLockHeld(g_cs_orphans);
+
+ unsigned int nEvicted = 0;
+ static int64_t nNextSweep;
+ int64_t nNow = GetTime();
+ if (nNextSweep <= nNow) {
+ // Sweep out expired orphan pool entries:
+ int nErased = 0;
+ int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
+ std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
+ while (iter != m_orphans.end())
+ {
+ std::map<uint256, OrphanTx>::iterator maybeErase = iter++;
+ if (maybeErase->second.nTimeExpire <= nNow) {
+ nErased += EraseTx(maybeErase->second.tx->GetHash());
+ } else {
+ nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
+ }
+ }
+ // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
+ nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
+ if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
+ }
+ FastRandomContext rng;
+ while (m_orphans.size() > max_orphans)
+ {
+ // Evict a random orphan:
+ size_t randompos = rng.randrange(m_orphan_list.size());
+ EraseTx(m_orphan_list[randompos]->first);
+ ++nEvicted;
+ }
+ return nEvicted;
+}
+
+void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const
+{
+ AssertLockHeld(g_cs_orphans);
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
+ if (it_by_prev != m_outpoint_to_orphan_it.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ orphan_work_set.insert(elem->first);
+ }
+ }
+ }
+}
+
+bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
+{
+ LOCK(g_cs_orphans);
+ if (gtxid.IsWtxid()) {
+ return m_wtxid_to_orphan_it.count(gtxid.GetHash());
+ } else {
+ return m_orphans.count(gtxid.GetHash());
+ }
+}
+
+std::pair<CTransactionRef, NodeId> TxOrphanage::GetTx(const uint256& txid) const
+{
+ AssertLockHeld(g_cs_orphans);
+
+ const auto it = m_orphans.find(txid);
+ if (it == m_orphans.end()) return {nullptr, -1};
+ return {it->second.tx, it->second.fromPeer};
+}
+
+void TxOrphanage::EraseForBlock(const CBlock& block)
+{
+ LOCK(g_cs_orphans);
+
+ std::vector<uint256> vOrphanErase;
+
+ for (const CTransactionRef& ptx : block.vtx) {
+ const CTransaction& tx = *ptx;
+
+ // Which orphan pool entries must we evict?
+ for (const auto& txin : tx.vin) {
+ auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
+ if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = *(*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
+ }
+ }
+
+ // Erase orphan transactions included or precluded by this block
+ if (vOrphanErase.size()) {
+ int nErased = 0;
+ for (const uint256& orphanHash : vOrphanErase) {
+ nErased += EraseTx(orphanHash);
+ }
+ LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
+ }
+}
diff --git a/src/txorphanage.h b/src/txorphanage.h
new file mode 100644
index 0000000000..df55cdb3be
--- /dev/null
+++ b/src/txorphanage.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2021 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_TXORPHANAGE_H
+#define BITCOIN_TXORPHANAGE_H
+
+#include <net.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <sync.h>
+
+/** Guards orphan transactions and extra txs for compact blocks */
+extern RecursiveMutex g_cs_orphans;
+
+/** A class to track orphan transactions (failed on TX_MISSING_INPUTS)
+ * Since we cannot distinguish orphans from bad transactions with
+ * non-existent inputs, we heavily limit the number of orphans
+ * we keep and the duration we keep them for.
+ */
+class TxOrphanage {
+public:
+ /** Add a new orphan transaction */
+ bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Check if we already have an orphan transaction (by txid or wtxid) */
+ bool HaveTx(const GenTxid& gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!g_cs_orphans);
+
+ /** Get an orphan transaction and its orginating peer
+ * (Transaction ref will be nullptr if not found)
+ */
+ std::pair<CTransactionRef, NodeId> GetTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase an orphan by txid */
+ int EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase all orphans announced by a peer (eg, after that peer disconnects) */
+ void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Erase all orphans included in or invalidated by a new block */
+ void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(!g_cs_orphans);
+
+ /** Limit the orphanage to the given maximum */
+ unsigned int LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+ /** Add any orphans that list a particular tx as a parent into a peer's work set
+ * (ie orphans that may have found their final missing parent, and so should be reconsidered for the mempool) */
+ void AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
+
+protected:
+ struct OrphanTx {
+ CTransactionRef tx;
+ NodeId fromPeer;
+ int64_t nTimeExpire;
+ size_t list_pos;
+ };
+
+ /** Map from txid to orphan transaction record. Limited by
+ * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
+ std::map<uint256, OrphanTx> m_orphans GUARDED_BY(g_cs_orphans);
+
+ using OrphanMap = decltype(m_orphans);
+
+ struct IteratorComparator
+ {
+ template<typename I>
+ bool operator()(const I& a, const I& b) const
+ {
+ return &(*a) < &(*b);
+ }
+ };
+
+ /** Index from the parents' COutPoint into the m_orphans. Used
+ * to remove orphan transactions from the m_orphans */
+ std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(g_cs_orphans);
+
+ /** Orphan transactions in vector for quick random eviction */
+ std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(g_cs_orphans);
+
+ /** Index from wtxid into the m_orphans to lookup orphan
+ * transactions using their witness ids. */
+ std::map<uint256, OrphanMap::iterator> m_wtxid_to_orphan_it GUARDED_BY(g_cs_orphans);
+};
+
+#endif // BITCOIN_TXORPHANAGE_H
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index e54c073328..58424134b0 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -9,7 +9,6 @@
#include <primitives/transaction.h>
#include <random.h>
#include <uint256.h>
-#include <util/memory.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
@@ -711,7 +710,7 @@ public:
};
TxRequestTracker::TxRequestTracker(bool deterministic) :
- m_impl{MakeUnique<TxRequestTracker::Impl>(deterministic)} {}
+ m_impl{std::make_unique<TxRequestTracker::Impl>(deterministic)} {}
TxRequestTracker::~TxRequestTracker() = default;
diff --git a/src/util/epochguard.h b/src/util/epochguard.h
new file mode 100644
index 0000000000..1570ec4eb4
--- /dev/null
+++ b/src/util/epochguard.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2020 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_EPOCHGUARD_H
+#define BITCOIN_UTIL_EPOCHGUARD_H
+
+#include <threadsafety.h>
+
+#include <cassert>
+
+/** Epoch: RAII-style guard for using epoch-based graph traversal algorithms.
+ * When walking ancestors or descendants, we generally want to avoid
+ * visiting the same transactions twice. Some traversal algorithms use
+ * std::set (or setEntries) to deduplicate the transaction we visit.
+ * However, use of std::set is algorithmically undesirable because it both
+ * adds an asymptotic factor of O(log n) to traversals cost and triggers O(n)
+ * more dynamic memory allocations.
+ * In many algorithms we can replace std::set with an internal mempool
+ * counter to track the time (or, "epoch") that we began a traversal, and
+ * check + update a per-transaction epoch for each transaction we look at to
+ * determine if that transaction has not yet been visited during the current
+ * traversal's epoch.
+ * Algorithms using std::set can be replaced on a one by one basis.
+ * Both techniques are not fundamentally incompatible across the codebase.
+ * Generally speaking, however, the remaining use of std::set for mempool
+ * traversal should be viewed as a TODO for replacement with an epoch based
+ * traversal, rather than a preference for std::set over epochs in that
+ * algorithm.
+ */
+
+class LOCKABLE Epoch
+{
+private:
+ uint64_t m_raw_epoch = 0;
+ bool m_guarded = false;
+
+public:
+ Epoch() = default;
+ Epoch(const Epoch&) = delete;
+ Epoch& operator=(const Epoch&) = delete;
+
+ bool guarded() const { return m_guarded; }
+
+ class Marker
+ {
+ private:
+ uint64_t m_marker = 0;
+
+ // only allow modification via Epoch member functions
+ friend class Epoch;
+ Marker& operator=(const Marker&) = delete;
+ };
+
+ class SCOPED_LOCKABLE Guard
+ {
+ private:
+ Epoch& m_epoch;
+
+ public:
+ explicit Guard(Epoch& epoch) EXCLUSIVE_LOCK_FUNCTION(epoch) : m_epoch(epoch)
+ {
+ assert(!m_epoch.m_guarded);
+ ++m_epoch.m_raw_epoch;
+ m_epoch.m_guarded = true;
+ }
+ ~Guard() UNLOCK_FUNCTION()
+ {
+ assert(m_epoch.m_guarded);
+ ++m_epoch.m_raw_epoch; // ensure clear separation between epochs
+ m_epoch.m_guarded = false;
+ }
+ };
+
+ bool visited(Marker& marker) const EXCLUSIVE_LOCKS_REQUIRED(*this)
+ {
+ assert(m_guarded);
+ if (marker.m_marker < m_raw_epoch) {
+ // marker is from a previous epoch, so this is its first visit
+ marker.m_marker = m_raw_epoch;
+ return false;
+ } else {
+ return true;
+ }
+ }
+};
+
+#define WITH_FRESH_EPOCH(epoch) const Epoch::Guard PASTE2(epoch_guard_, __COUNTER__)(epoch)
+
+#endif // BITCOIN_UTIL_EPOCHGUARD_H
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 76fac4d391..48c81693f3 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -31,6 +31,10 @@ bilingual_str TransactionErrorString(const TransactionError err)
return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
+ case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
+ return Untranslated("External signer not found");
+ case TransactionError::EXTERNAL_SIGNER_FAILED:
+ return Untranslated("External signer failed to sign");
// no default case, so the compiler can warn about missing cases
}
assert(false);
diff --git a/src/util/error.h b/src/util/error.h
index 6633498d2b..4cc35eb1fd 100644
--- a/src/util/error.h
+++ b/src/util/error.h
@@ -30,6 +30,8 @@ enum class TransactionError {
PSBT_MISMATCH,
SIGHASH_MISMATCH,
MAX_FEE_EXCEEDED,
+ EXTERNAL_SIGNER_NOT_FOUND,
+ EXTERNAL_SIGNER_FAILED,
};
bilingual_str TransactionErrorString(const TransactionError error);
diff --git a/src/util/getuniquepath.cpp b/src/util/getuniquepath.cpp
new file mode 100644
index 0000000000..9839d2f624
--- /dev/null
+++ b/src/util/getuniquepath.cpp
@@ -0,0 +1,10 @@
+#include <random.h>
+#include <fs.h>
+#include <util/strencodings.h>
+
+fs::path GetUniquePath(const fs::path& base)
+{
+ FastRandomContext rnd;
+ fs::path tmpFile = base / HexStr(rnd.randbytes(8));
+ return tmpFile;
+} \ No newline at end of file
diff --git a/src/util/getuniquepath.h b/src/util/getuniquepath.h
new file mode 100644
index 0000000000..e0c6147876
--- /dev/null
+++ b/src/util/getuniquepath.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2021 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_GETUNIQUEPATH_H
+#define BITCOIN_UTIL_GETUNIQUEPATH_H
+
+#include <fs.h>
+
+/**
+ * Helper function for getting a unique path
+ *
+ * @param[in] base Base path
+ * @returns base joined with a random 8-character long string.
+ * @post Returned path is unique with high probability.
+ */
+fs::path GetUniquePath(const fs::path& base);
+
+#endif // BITCOIN_UTIL_GETUNIQUEPATH_H \ No newline at end of file
diff --git a/src/util/memory.h b/src/util/memory.h
deleted file mode 100644
index f21b81bade..0000000000
--- a/src/util/memory.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2020 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_MEMORY_H
-#define BITCOIN_UTIL_MEMORY_H
-
-#include <memory>
-#include <utility>
-
-//! Substitute for C++14 std::make_unique.
-//! DEPRECATED use std::make_unique in new code.
-template <typename T, typename... Args>
-std::unique_ptr<T> MakeUnique(Args&&... args)
-{
- return std::make_unique<T>(std::forward<Args>(args)...);
-}
-
-#endif
diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp
index 1bc8d02eab..3f9ce7dce4 100644
--- a/src/util/moneystr.cpp
+++ b/src/util/moneystr.cpp
@@ -9,13 +9,17 @@
#include <util/strencodings.h>
#include <util/string.h>
-std::string FormatMoney(const CAmount& n)
+std::string FormatMoney(const CAmount n)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
- int64_t n_abs = (n > 0 ? n : -n);
- int64_t quotient = n_abs/COIN;
- int64_t remainder = n_abs%COIN;
+ static_assert(COIN > 1);
+ int64_t quotient = n / COIN;
+ int64_t remainder = n % COIN;
+ if (n < 0) {
+ quotient = -quotient;
+ remainder = -remainder;
+ }
std::string str = strprintf("%d.%08d", quotient, remainder);
// Right-trim excess zeros before the decimal point:
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index da7f673cda..2aedbee358 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -17,7 +17,7 @@
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
-std::string FormatMoney(const CAmount& n);
+std::string FormatMoney(const CAmount n);
/** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/
[[nodiscard]] bool ParseMoney(const std::string& str, CAmount& nRet);
diff --git a/src/util/readwritefile.cpp b/src/util/readwritefile.cpp
new file mode 100644
index 0000000000..a45c41d367
--- /dev/null
+++ b/src/util/readwritefile.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2015-2020 The Bitcoin Core developers
+// Copyright (c) 2017 The Zcash developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <fs.h>
+
+#include <limits>
+#include <stdio.h>
+#include <string>
+#include <utility>
+
+std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
+{
+ FILE *f = fsbridge::fopen(filename, "rb");
+ if (f == nullptr)
+ return std::make_pair(false,"");
+ std::string retval;
+ char buffer[128];
+ do {
+ const size_t n = fread(buffer, 1, sizeof(buffer), f);
+ // Check for reading errors so we don't return any data if we couldn't
+ // read the entire file (or up to maxsize)
+ if (ferror(f)) {
+ fclose(f);
+ return std::make_pair(false,"");
+ }
+ retval.append(buffer, buffer+n);
+ } while (!feof(f) && retval.size() <= maxsize);
+ fclose(f);
+ return std::make_pair(true,retval);
+}
+
+bool WriteBinaryFile(const fs::path &filename, const std::string &data)
+{
+ FILE *f = fsbridge::fopen(filename, "wb");
+ if (f == nullptr)
+ return false;
+ if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
+ fclose(f);
+ return false;
+ }
+ if (fclose(f) != 0) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/util/readwritefile.h b/src/util/readwritefile.h
new file mode 100644
index 0000000000..1dab874b38
--- /dev/null
+++ b/src/util/readwritefile.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015-2020 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_READWRITEFILE_H
+#define BITCOIN_UTIL_READWRITEFILE_H
+
+#include <fs.h>
+
+#include <limits>
+#include <string>
+#include <utility>
+
+/** Read full contents of a file and return them in a std::string.
+ * Returns a pair <status, string>.
+ * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
+ *
+ * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
+ * (with len > maxsize) will be returned.
+ */
+std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
+
+/** Write contents of std::string to a file.
+ * @return true on success.
+ */
+bool WriteBinaryFile(const fs::path &filename, const std::string &data);
+
+#endif /* BITCOIN_UTIL_READWRITEFILE_H */
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
new file mode 100644
index 0000000000..f9ecfef5d4
--- /dev/null
+++ b/src/util/sock.cpp
@@ -0,0 +1,333 @@
+// Copyright (c) 2020-2021 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 <compat.h>
+#include <logging.h>
+#include <threadinterrupt.h>
+#include <tinyformat.h>
+#include <util/sock.h>
+#include <util/system.h>
+#include <util/time.h>
+
+#include <codecvt>
+#include <cwchar>
+#include <locale>
+#include <stdexcept>
+#include <string>
+
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+
+static inline bool IOErrorIsPermanent(int err)
+{
+ return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
+}
+
+Sock::Sock() : m_socket(INVALID_SOCKET) {}
+
+Sock::Sock(SOCKET s) : m_socket(s) {}
+
+Sock::Sock(Sock&& other)
+{
+ m_socket = other.m_socket;
+ other.m_socket = INVALID_SOCKET;
+}
+
+Sock::~Sock() { Reset(); }
+
+Sock& Sock::operator=(Sock&& other)
+{
+ Reset();
+ m_socket = other.m_socket;
+ other.m_socket = INVALID_SOCKET;
+ return *this;
+}
+
+SOCKET Sock::Get() const { return m_socket; }
+
+SOCKET Sock::Release()
+{
+ const SOCKET s = m_socket;
+ m_socket = INVALID_SOCKET;
+ return s;
+}
+
+void Sock::Reset() { CloseSocket(m_socket); }
+
+ssize_t Sock::Send(const void* data, size_t len, int flags) const
+{
+ return send(m_socket, static_cast<const char*>(data), len, flags);
+}
+
+ssize_t Sock::Recv(void* buf, size_t len, int flags) const
+{
+ return recv(m_socket, static_cast<char*>(buf), len, flags);
+}
+
+bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
+{
+#ifdef USE_POLL
+ pollfd fd;
+ fd.fd = m_socket;
+ fd.events = 0;
+ if (requested & RECV) {
+ fd.events |= POLLIN;
+ }
+ if (requested & SEND) {
+ fd.events |= POLLOUT;
+ }
+
+ if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
+ return false;
+ }
+
+ if (occurred != nullptr) {
+ *occurred = 0;
+ if (fd.revents & POLLIN) {
+ *occurred |= RECV;
+ }
+ if (fd.revents & POLLOUT) {
+ *occurred |= SEND;
+ }
+ }
+
+ return true;
+#else
+ if (!IsSelectableSocket(m_socket)) {
+ return false;
+ }
+
+ fd_set fdset_recv;
+ fd_set fdset_send;
+ FD_ZERO(&fdset_recv);
+ FD_ZERO(&fdset_send);
+
+ if (requested & RECV) {
+ FD_SET(m_socket, &fdset_recv);
+ }
+
+ if (requested & SEND) {
+ FD_SET(m_socket, &fdset_send);
+ }
+
+ timeval timeout_struct = MillisToTimeval(timeout);
+
+ if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
+ return false;
+ }
+
+ if (occurred != nullptr) {
+ *occurred = 0;
+ if (FD_ISSET(m_socket, &fdset_recv)) {
+ *occurred |= RECV;
+ }
+ if (FD_ISSET(m_socket, &fdset_send)) {
+ *occurred |= SEND;
+ }
+ }
+
+ return true;
+#endif /* USE_POLL */
+}
+
+void Sock::SendComplete(const std::string& data,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const
+{
+ const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
+ size_t sent{0};
+
+ for (;;) {
+ const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
+
+ if (ret > 0) {
+ sent += static_cast<size_t>(ret);
+ if (sent == data.size()) {
+ break;
+ }
+ } else {
+ const int err{WSAGetLastError()};
+ if (IOErrorIsPermanent(err)) {
+ throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
+ }
+ }
+
+ const auto now = GetTime<std::chrono::milliseconds>();
+
+ if (now >= deadline) {
+ throw std::runtime_error(strprintf(
+ "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
+ }
+
+ if (interrupt) {
+ throw std::runtime_error(strprintf(
+ "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
+ }
+
+ // Wait for a short while (or the socket to become ready for sending) before retrying
+ // if nothing was sent.
+ const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ Wait(wait_time, SEND);
+ }
+}
+
+std::string Sock::RecvUntilTerminator(uint8_t terminator,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt,
+ size_t max_data) const
+{
+ const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
+ std::string data;
+ bool terminator_found{false};
+
+ // We must not consume any bytes past the terminator from the socket.
+ // One option is to read one byte at a time and check if we have read a terminator.
+ // However that is very slow. Instead, we peek at what is in the socket and only read
+ // as many bytes as possible without crossing the terminator.
+ // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
+ // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
+ // at a time is about 50 times slower.
+
+ for (;;) {
+ if (data.size() >= max_data) {
+ throw std::runtime_error(
+ strprintf("Received too many bytes without a terminator (%u)", data.size()));
+ }
+
+ char buf[512];
+
+ const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
+
+ switch (peek_ret) {
+ case -1: {
+ const int err{WSAGetLastError()};
+ if (IOErrorIsPermanent(err)) {
+ throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
+ }
+ break;
+ }
+ case 0:
+ throw std::runtime_error("Connection unexpectedly closed by peer");
+ default:
+ auto end = buf + peek_ret;
+ auto terminator_pos = std::find(buf, end, terminator);
+ terminator_found = terminator_pos != end;
+
+ const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
+ static_cast<size_t>(peek_ret)};
+
+ const ssize_t read_ret{Recv(buf, try_len, 0)};
+
+ if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
+ throw std::runtime_error(
+ strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
+ "peek claimed %u bytes are available",
+ read_ret, try_len, peek_ret));
+ }
+
+ // Don't include the terminator in the output.
+ const size_t append_len{terminator_found ? try_len - 1 : try_len};
+
+ data.append(buf, buf + append_len);
+
+ if (terminator_found) {
+ return data;
+ }
+ }
+
+ const auto now = GetTime<std::chrono::milliseconds>();
+
+ if (now >= deadline) {
+ throw std::runtime_error(strprintf(
+ "Receive timeout (received %u bytes without terminator before that)", data.size()));
+ }
+
+ if (interrupt) {
+ throw std::runtime_error(strprintf(
+ "Receive interrupted (received %u bytes without terminator before that)",
+ data.size()));
+ }
+
+ // Wait for a short while (or the socket to become ready for reading) before retrying.
+ const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
+ Wait(wait_time, RECV);
+ }
+}
+
+bool Sock::IsConnected(std::string& errmsg) const
+{
+ if (m_socket == INVALID_SOCKET) {
+ errmsg = "not connected";
+ return false;
+ }
+
+ char c;
+ switch (Recv(&c, sizeof(c), MSG_PEEK)) {
+ case -1: {
+ const int err = WSAGetLastError();
+ if (IOErrorIsPermanent(err)) {
+ errmsg = NetworkErrorString(err);
+ return false;
+ }
+ return true;
+ }
+ case 0:
+ errmsg = "closed";
+ return false;
+ default:
+ return true;
+ }
+}
+
+#ifdef WIN32
+std::string NetworkErrorString(int err)
+{
+ wchar_t buf[256];
+ buf[0] = 0;
+ if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buf, ARRAYSIZE(buf), nullptr))
+ {
+ return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
+ }
+ else
+ {
+ return strprintf("Unknown error (%d)", err);
+ }
+}
+#else
+std::string NetworkErrorString(int err)
+{
+ char buf[256];
+ buf[0] = 0;
+ /* Too bad there are two incompatible implementations of the
+ * thread-safe strerror. */
+ const char *s;
+#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
+ s = strerror_r(err, buf, sizeof(buf));
+#else /* POSIX variant always returns message in buffer */
+ s = buf;
+ if (strerror_r(err, buf, sizeof(buf)))
+ buf[0] = 0;
+#endif
+ return strprintf("%s (%d)", s, err);
+}
+#endif
+
+bool CloseSocket(SOCKET& hSocket)
+{
+ if (hSocket == INVALID_SOCKET)
+ return false;
+#ifdef WIN32
+ int ret = closesocket(hSocket);
+#else
+ int ret = close(hSocket);
+#endif
+ if (ret) {
+ LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
+ }
+ hSocket = INVALID_SOCKET;
+ return ret != SOCKET_ERROR;
+}
diff --git a/src/util/sock.h b/src/util/sock.h
new file mode 100644
index 0000000000..4b0618dcff
--- /dev/null
+++ b/src/util/sock.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2020-2021 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_SOCK_H
+#define BITCOIN_UTIL_SOCK_H
+
+#include <compat.h>
+#include <threadinterrupt.h>
+#include <util/time.h>
+
+#include <chrono>
+#include <string>
+
+/**
+ * Maximum time to wait for I/O readiness.
+ * It will take up until this time to break off in case of an interruption.
+ */
+static constexpr auto MAX_WAIT_FOR_IO = 1s;
+
+/**
+ * RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
+ * contains a socket and closes it automatically when it goes out of scope.
+ */
+class Sock
+{
+public:
+ /**
+ * Default constructor, creates an empty object that does nothing when destroyed.
+ */
+ Sock();
+
+ /**
+ * Take ownership of an existent socket.
+ */
+ explicit Sock(SOCKET s);
+
+ /**
+ * Copy constructor, disabled because closing the same socket twice is undesirable.
+ */
+ Sock(const Sock&) = delete;
+
+ /**
+ * Move constructor, grab the socket from another object and close ours (if set).
+ */
+ Sock(Sock&& other);
+
+ /**
+ * Destructor, close the socket or do nothing if empty.
+ */
+ virtual ~Sock();
+
+ /**
+ * Copy assignment operator, disabled because closing the same socket twice is undesirable.
+ */
+ Sock& operator=(const Sock&) = delete;
+
+ /**
+ * Move assignment operator, grab the socket from another object and close ours (if set).
+ */
+ virtual Sock& operator=(Sock&& other);
+
+ /**
+ * Get the value of the contained socket.
+ * @return socket or INVALID_SOCKET if empty
+ */
+ virtual SOCKET Get() const;
+
+ /**
+ * Get the value of the contained socket and drop ownership. It will not be closed by the
+ * destructor after this call.
+ * @return socket or INVALID_SOCKET if empty
+ */
+ virtual SOCKET Release();
+
+ /**
+ * Close if non-empty.
+ */
+ virtual void Reset();
+
+ /**
+ * send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
+ * wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
+ */
+ virtual ssize_t Send(const void* data, size_t len, int flags) const;
+
+ /**
+ * recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
+ * wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
+ */
+ virtual ssize_t Recv(void* buf, size_t len, int flags) const;
+
+ using Event = uint8_t;
+
+ /**
+ * If passed to `Wait()`, then it will wait for readiness to read from the socket.
+ */
+ static constexpr Event RECV = 0b01;
+
+ /**
+ * If passed to `Wait()`, then it will wait for readiness to send to the socket.
+ */
+ static constexpr Event SEND = 0b10;
+
+ /**
+ * Wait for readiness for input (recv) or output (send).
+ * @param[in] timeout Wait this much for at least one of the requested events to occur.
+ * @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
+ * @param[out] occurred If not nullptr and `true` is returned, then upon return this
+ * indicates which of the requested events occurred. A timeout is indicated by return
+ * value of `true` and `occurred` being set to 0.
+ * @return true on success and false otherwise
+ */
+ virtual bool Wait(std::chrono::milliseconds timeout,
+ Event requested,
+ Event* occurred = nullptr) const;
+
+ /* Higher level, convenience, methods. These may throw. */
+
+ /**
+ * Send the given data, retrying on transient errors.
+ * @param[in] data Data to send.
+ * @param[in] timeout Timeout for the entire operation.
+ * @param[in] interrupt If this is signaled then the operation is canceled.
+ * @throws std::runtime_error if the operation cannot be completed. In this case only some of
+ * the data will be written to the socket.
+ */
+ virtual void SendComplete(const std::string& data,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt) const;
+
+ /**
+ * Read from socket until a terminator character is encountered. Will never consume bytes past
+ * the terminator from the socket.
+ * @param[in] terminator Character up to which to read from the socket.
+ * @param[in] timeout Timeout for the entire operation.
+ * @param[in] interrupt If this is signaled then the operation is canceled.
+ * @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes
+ * are received and there is still no terminator, then this method will throw an exception.
+ * @return The data that has been read, without the terminating character.
+ * @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
+ * have been consumed from the socket.
+ */
+ virtual std::string RecvUntilTerminator(uint8_t terminator,
+ std::chrono::milliseconds timeout,
+ CThreadInterrupt& interrupt,
+ size_t max_data) const;
+
+ /**
+ * Check if still connected.
+ * @param[out] err The error string, if the socket has been disconnected.
+ * @return true if connected
+ */
+ virtual bool IsConnected(std::string& errmsg) const;
+
+private:
+ /**
+ * Contained socket. `INVALID_SOCKET` designates the object is empty.
+ */
+ SOCKET m_socket;
+};
+
+/** Return readable error string for a network error code */
+std::string NetworkErrorString(int err);
+
+/** Close socket and set hSocket to INVALID_SOCKET */
+bool CloseSocket(SOCKET& hSocket);
+
+#endif // BITCOIN_UTIL_SOCK_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index f3d54a2ac9..4734de3e0b 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -107,23 +107,25 @@ std::vector<unsigned char> ParseHex(const std::string& str)
return ParseHex(str.c_str());
}
-void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
+void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut)
+{
size_t colon = in.find_last_of(':');
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
bool fHaveColon = colon != in.npos;
- bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
- bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
- if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
- int32_t n;
- if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) {
+ bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
+ bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos);
+ if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
+ uint16_t n;
+ if (ParseUInt16(in.substr(colon + 1), &n)) {
in = in.substr(0, colon);
portOut = n;
}
}
- if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']')
- hostOut = in.substr(1, in.size()-2);
- else
+ if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
+ hostOut = in.substr(1, in.size() - 2);
+ } else {
hostOut = in;
+ }
}
std::string EncodeBase64(Span<const unsigned char> input)
@@ -334,6 +336,18 @@ bool ParseUInt8(const std::string& str, uint8_t *out)
return true;
}
+bool ParseUInt16(const std::string& str, uint16_t* out)
+{
+ uint32_t u32;
+ if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<uint16_t>(u32);
+ }
+ return true;
+}
+
bool ParseUInt32(const std::string& str, uint32_t *out)
{
if (!ParsePrechecks(str))
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index b4a61202ef..26dc0a0ce3 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -17,8 +17,6 @@
#include <string>
#include <vector>
-#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
-
/** Used by SanitizeString() */
enum SafeChars
{
@@ -67,7 +65,7 @@ std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
*/
std::string EncodeBase32(const std::string& str, bool pad = true);
-void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
+void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
int64_t atoi64(const std::string& str);
int atoi(const std::string& str);
@@ -118,6 +116,13 @@ constexpr inline bool IsSpace(char c) noexcept {
[[nodiscard]] bool ParseUInt8(const std::string& str, uint8_t *out);
/**
+ * Convert decimal string to unsigned 16-bit integer with strict parse error feedback.
+ * @returns true if the entire string could be parsed as valid integer,
+ * false if the entire string could not be parsed or if overflow or underflow occurred.
+ */
+[[nodiscard]] bool ParseUInt16(const std::string& str, uint16_t* out);
+
+/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
diff --git a/src/util/string.h b/src/util/string.h
index 5ffdc80d88..b26facc502 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -25,6 +25,14 @@
return str.substr(front, end - front + 1);
}
+[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix)
+{
+ if (str.substr(0, prefix.size()) == prefix) {
+ return str.substr(prefix.size());
+ }
+ return str;
+}
+
/**
* Join a list of items
*
diff --git a/src/util/system.cpp b/src/util/system.cpp
index d1fb921642..0b83a76504 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -3,14 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <sync.h>
#include <util/system.h>
-#ifdef HAVE_BOOST_PROCESS
+#ifdef ENABLE_EXTERNAL_SIGNER
#include <boost/process.hpp>
-#endif // HAVE_BOOST_PROCESS
+#endif // ENABLE_EXTERNAL_SIGNER
#include <chainparamsbase.h>
+#include <sync.h>
+#include <util/check.h>
+#include <util/getuniquepath.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
@@ -98,7 +100,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
// Create empty lock file if it doesn't exist.
FILE* file = fsbridge::fopen(pathLockFile, "a");
if (file) fclose(file);
- auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile);
+ auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
if (!lock->TryLock()) {
return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
}
@@ -123,7 +125,7 @@ void ReleaseDirectoryLocks()
bool DirIsWritable(const fs::path& directory)
{
- fs::path tmpFile = directory / fs::unique_path();
+ fs::path tmpFile = GetUniquePath(directory);
FILE* file = fsbridge::fopen(tmpFile, "a");
if (!file) return false;
@@ -310,8 +312,22 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key[0] = '-';
#endif
- if (key[0] != '-')
+ if (key[0] != '-') {
+ if (!m_accept_any_command && m_command.empty()) {
+ // The first non-dash arg is a registered command
+ std::optional<unsigned int> flags = GetArgFlags(key);
+ if (!flags || !(*flags & ArgsManager::COMMAND)) {
+ error = strprintf("Invalid command '%s'", argv[i]);
+ return false;
+ }
+ }
+ m_command.push_back(key);
+ while (++i < argc) {
+ // The remaining args are command args
+ m_command.push_back(argv[i]);
+ }
break;
+ }
// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-')
@@ -321,7 +337,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key.erase(0, 1);
std::string section;
util::SettingsValue value = InterpretOption(section, key, val);
- Optional<unsigned int> flags = GetArgFlags('-' + key);
+ std::optional<unsigned int> flags = GetArgFlags('-' + key);
// Unknown command line options and command line options with dot
// characters (which are returned from InterpretOption with nonempty
@@ -347,7 +363,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
return success;
}
-Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
+std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
{
LOCK(cs_args);
for (const auto& arg_map : m_available_args) {
@@ -356,7 +372,27 @@ Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
return search->second.m_flags;
}
}
- return nullopt;
+ return std::nullopt;
+}
+
+std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
+{
+ Command ret;
+ LOCK(cs_args);
+ auto it = m_command.begin();
+ if (it == m_command.end()) {
+ // No command was passed
+ return std::nullopt;
+ }
+ if (!m_accept_any_command) {
+ // The registered command
+ ret.command = *(it++);
+ }
+ while (it != m_command.end()) {
+ // The unregistered command and args (if any)
+ ret.args.push_back(*(it++));
+ }
+ return ret;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
@@ -504,8 +540,22 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
m_settings.forced_settings[SettingName(strArg)] = strValue;
}
+void ArgsManager::AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat)
+{
+ Assert(cmd.find('=') == std::string::npos);
+ Assert(cmd.at(0) != '-');
+
+ LOCK(cs_args);
+ m_accept_any_command = false; // latch to false
+ std::map<std::string, Arg>& arg_map = m_available_args[cat];
+ auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
+ Assert(ret.second); // Fail on duplicate commands
+}
+
void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
{
+ Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
+
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
@@ -824,7 +874,7 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
std::string section;
std::string key = option.first;
util::SettingsValue value = InterpretOption(section, key, option.second);
- Optional<unsigned int> flags = GetArgFlags('-' + key);
+ std::optional<unsigned int> flags = GetArgFlags('-' + key);
if (flags) {
if (!CheckValid(key, value, *flags, error)) {
return false;
@@ -984,7 +1034,7 @@ void ArgsManager::logArgsPrefix(
std::string section_str = section.empty() ? "" : "[" + section + "] ";
for (const auto& arg : args) {
for (const auto& value : arg.second) {
- Optional<unsigned int> flags = GetArgFlags('-' + arg.first);
+ std::optional<unsigned int> flags = GetArgFlags('-' + arg.first);
if (flags) {
std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
@@ -1197,7 +1247,7 @@ void runCommand(const std::string& strCommand)
}
#endif
-#ifdef HAVE_BOOST_PROCESS
+#ifdef ENABLE_EXTERNAL_SIGNER
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
{
namespace bp = boost::process;
@@ -1232,7 +1282,7 @@ UniValue RunCommandParseJSON(const std::string& str_command, const std::string&
return result_json;
}
-#endif // HAVE_BOOST_PROCESS
+#endif // ENABLE_EXTERNAL_SIGNER
void SetupEnvironment()
{
diff --git a/src/util/system.h b/src/util/system.h
index d06c30bfa7..291f3f5541 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -19,16 +19,15 @@
#include <compat/assumptions.h>
#include <fs.h>
#include <logging.h>
-#include <optional.h>
#include <sync.h>
#include <tinyformat.h>
-#include <util/memory.h>
#include <util/settings.h>
#include <util/threadnames.h>
#include <util/time.h>
#include <exception>
#include <map>
+#include <optional>
#include <set>
#include <stdint.h>
#include <string>
@@ -108,7 +107,7 @@ std::string ShellEscape(const std::string& arg);
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
-#ifdef HAVE_BOOST_PROCESS
+#ifdef ENABLE_EXTERNAL_SIGNER
/**
* Execute a command which returns JSON, and parse the result.
*
@@ -117,7 +116,7 @@ void runCommand(const std::string& strCommand);
* @return parsed JSON
*/
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
-#endif // HAVE_BOOST_PROCESS
+#endif // ENABLE_EXTERNAL_SIGNER
/**
* Most paths passed as configuration arguments are treated as relative to
@@ -166,7 +165,7 @@ struct SectionInfo
class ArgsManager
{
public:
- enum Flags {
+ enum Flags : uint32_t {
// Boolean options can accept negation syntax -noOPTION or -noOPTION=1
ALLOW_BOOL = 0x01,
ALLOW_INT = 0x02,
@@ -181,6 +180,7 @@ public:
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
+ COMMAND = 0x800,
};
protected:
@@ -193,9 +193,11 @@ protected:
mutable RecursiveMutex cs_args;
util::Settings m_settings GUARDED_BY(cs_args);
+ std::vector<std::string> m_command GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
+ bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
@@ -246,6 +248,20 @@ public:
*/
const std::list<SectionInfo> GetUnrecognizedSections() const;
+ struct Command {
+ /** The command (if one has been registered with AddCommand), or empty */
+ std::string command;
+ /**
+ * If command is non-empty: Any args that followed it
+ * If command is empty: The unregistered command and any args that followed it
+ */
+ std::vector<std::string> args;
+ };
+ /**
+ * Get the command and command args (returns std::nullopt if no command provided)
+ */
+ std::optional<const Command> GetCommand() const;
+
/**
* Return a vector of strings of the given argument
*
@@ -332,6 +348,11 @@ public:
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
+ * Add subcommand
+ */
+ void AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat);
+
+ /**
* Add many hidden arguments
*/
void AddHiddenArgs(const std::vector<std::string>& args);
@@ -354,7 +375,7 @@ public:
* Return Flags for known arg.
* Return nullopt for unknown arg.
*/
- Optional<unsigned int> GetArgFlags(const std::string& name) const;
+ std::optional<unsigned int> GetArgFlags(const std::string& name) const;
/**
* Read and update settings file with saved settings. This needs to be
diff --git a/src/util/time.cpp b/src/util/time.cpp
index e96972fe12..e6f0986a39 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -7,8 +7,11 @@
#include <config/bitcoin-config.h>
#endif
+#include <compat.h>
#include <util/time.h>
+#include <util/check.h>
+
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
@@ -18,7 +21,7 @@
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
-static std::atomic<int64_t> nMockTime(0); //!< For unit testing
+static std::atomic<int64_t> nMockTime(0); //!< For testing
int64_t GetTime()
{
@@ -30,6 +33,49 @@ int64_t GetTime()
return now;
}
+bool ChronoSanityCheck()
+{
+ // std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
+ // to use the Unix epoch timestamp, prior to C++20, but in practice they almost
+ // certainly will. Any differing behavior will be assumed to be an error, unless
+ // certain platforms prove to consistently deviate, at which point we'll cope
+ // with it by adding offsets.
+
+ // Create a new clock from time_t(0) and make sure that it represents 0
+ // seconds from the system_clock's time_since_epoch. Then convert that back
+ // to a time_t and verify that it's the same as before.
+ const time_t time_t_epoch{};
+ auto clock = std::chrono::system_clock::from_time_t(time_t_epoch);
+ if (std::chrono::duration_cast<std::chrono::seconds>(clock.time_since_epoch()).count() != 0) {
+ return false;
+ }
+
+ time_t time_val = std::chrono::system_clock::to_time_t(clock);
+ if (time_val != time_t_epoch) {
+ return false;
+ }
+
+ // Check that the above zero time is actually equal to the known unix timestamp.
+ struct tm epoch;
+#ifdef HAVE_GMTIME_R
+ if (gmtime_r(&time_val, &epoch) == nullptr) {
+#else
+ if (gmtime_s(&epoch, &time_val) != 0) {
+#endif
+ return false;
+ }
+
+ if ((epoch.tm_sec != 0) ||
+ (epoch.tm_min != 0) ||
+ (epoch.tm_hour != 0) ||
+ (epoch.tm_mday != 1) ||
+ (epoch.tm_mon != 0) ||
+ (epoch.tm_year != 70)) {
+ return false;
+ }
+ return true;
+}
+
template <typename T>
T GetTime()
{
@@ -44,35 +90,43 @@ template std::chrono::seconds GetTime();
template std::chrono::milliseconds GetTime();
template std::chrono::microseconds GetTime();
+template <typename T>
+static T GetSystemTime()
+{
+ const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch());
+ assert(now.count() > 0);
+ return now;
+}
+
void SetMockTime(int64_t nMockTimeIn)
{
+ Assert(nMockTimeIn >= 0);
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}
-int64_t GetMockTime()
+void SetMockTime(std::chrono::seconds mock_time_in)
+{
+ nMockTime.store(mock_time_in.count(), std::memory_order_relaxed);
+}
+
+std::chrono::seconds GetMockTime()
{
- return nMockTime.load(std::memory_order_relaxed);
+ return std::chrono::seconds(nMockTime.load(std::memory_order_relaxed));
}
int64_t GetTimeMillis()
{
- int64_t now = (boost::posix_time::microsec_clock::universal_time() -
- boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds();
- assert(now > 0);
- return now;
+ return int64_t{GetSystemTime<std::chrono::milliseconds>().count()};
}
int64_t GetTimeMicros()
{
- int64_t now = (boost::posix_time::microsec_clock::universal_time() -
- boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds();
- assert(now > 0);
- return now;
+ return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
}
int64_t GetSystemTimeInSeconds()
{
- return GetTimeMicros()/1000000;
+ return int64_t{GetSystemTime<std::chrono::seconds>().count()};
}
std::string FormatISO8601DateTime(int64_t nTime) {
@@ -114,3 +168,16 @@ int64_t ParseISO8601DateTime(const std::string& str)
return 0;
return (ptime - epoch).total_seconds();
}
+
+struct timeval MillisToTimeval(int64_t nTimeout)
+{
+ struct timeval timeout;
+ timeout.tv_sec = nTimeout / 1000;
+ timeout.tv_usec = (nTimeout % 1000) * 1000;
+ return timeout;
+}
+
+struct timeval MillisToTimeval(std::chrono::milliseconds ms)
+{
+ return MillisToTimeval(count_milliseconds(ms));
+}
diff --git a/src/util/time.h b/src/util/time.h
index c69f604dc6..7ebcaaa339 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_UTIL_TIME_H
#define BITCOIN_UTIL_TIME_H
+#include <compat.h>
+
#include <chrono>
#include <stdint.h>
#include <string>
@@ -24,8 +26,16 @@ void UninterruptibleSleep(const std::chrono::microseconds& n);
* This helper is used to convert durations before passing them over an
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
-inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
-inline int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
+constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
+constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
+constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
+
+using SecondsDouble = std::chrono::duration<double, std::chrono::seconds::period>;
+
+/**
+ * Helper to count the seconds in any std::chrono::duration type
+ */
+inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
/**
* DEPRECATED
@@ -40,10 +50,19 @@ int64_t GetTimeMicros();
/** Returns the system time (not mockable) */
int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable
-/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
+/**
+ * DEPRECATED
+ * Use SetMockTime with chrono type
+ *
+ * @param[in] nMockTimeIn Time in seconds.
+ */
void SetMockTime(int64_t nMockTimeIn);
+
+/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
+void SetMockTime(std::chrono::seconds mock_time_in);
+
/** For testing */
-int64_t GetMockTime();
+std::chrono::seconds GetMockTime();
/** Return system time (or mocked time, if set) */
template <typename T>
@@ -57,4 +76,17 @@ std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
int64_t ParseISO8601DateTime(const std::string& str);
+/**
+ * Convert milliseconds to a struct timeval for e.g. select.
+ */
+struct timeval MillisToTimeval(int64_t nTimeout);
+
+/**
+ * Convert milliseconds to a struct timeval for e.g. select.
+ */
+struct timeval MillisToTimeval(std::chrono::milliseconds ms);
+
+/** Sanity check epoch match normal Unix epoch */
+bool ChronoSanityCheck();
+
#endif // BITCOIN_UTIL_TIME_H
diff --git a/src/util/tokenpipe.cpp b/src/util/tokenpipe.cpp
new file mode 100644
index 0000000000..4c091cd2e6
--- /dev/null
+++ b/src/util/tokenpipe.cpp
@@ -0,0 +1,109 @@
+// Copyright (c) 2021 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/tokenpipe.h>
+
+#include <config/bitcoin-config.h>
+
+#ifndef WIN32
+
+#include <errno.h>
+#include <fcntl.h>
+#include <optional>
+#include <unistd.h>
+
+TokenPipeEnd TokenPipe::TakeReadEnd()
+{
+ TokenPipeEnd res(m_fds[0]);
+ m_fds[0] = -1;
+ return res;
+}
+
+TokenPipeEnd TokenPipe::TakeWriteEnd()
+{
+ TokenPipeEnd res(m_fds[1]);
+ m_fds[1] = -1;
+ return res;
+}
+
+TokenPipeEnd::TokenPipeEnd(int fd) : m_fd(fd)
+{
+}
+
+TokenPipeEnd::~TokenPipeEnd()
+{
+ Close();
+}
+
+int TokenPipeEnd::TokenWrite(uint8_t token)
+{
+ while (true) {
+ ssize_t result = write(m_fd, &token, 1);
+ if (result < 0) {
+ // Failure. It's possible that the write was interrupted by a signal,
+ // in that case retry.
+ if (errno != EINTR) {
+ return TS_ERR;
+ }
+ } else if (result == 0) {
+ return TS_EOS;
+ } else { // ==1
+ return 0;
+ }
+ }
+}
+
+int TokenPipeEnd::TokenRead()
+{
+ uint8_t token;
+ while (true) {
+ ssize_t result = read(m_fd, &token, 1);
+ if (result < 0) {
+ // Failure. Check if the read was interrupted by a signal,
+ // in that case retry.
+ if (errno != EINTR) {
+ return TS_ERR;
+ }
+ } else if (result == 0) {
+ return TS_EOS;
+ } else { // ==1
+ return token;
+ }
+ }
+ return token;
+}
+
+void TokenPipeEnd::Close()
+{
+ if (m_fd != -1) close(m_fd);
+ m_fd = -1;
+}
+
+std::optional<TokenPipe> TokenPipe::Make()
+{
+ int fds[2] = {-1, -1};
+#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2
+ if (pipe2(fds, O_CLOEXEC) != 0) {
+ return std::nullopt;
+ }
+#else
+ if (pipe(fds) != 0) {
+ return std::nullopt;
+ }
+#endif
+ return TokenPipe(fds);
+}
+
+TokenPipe::~TokenPipe()
+{
+ Close();
+}
+
+void TokenPipe::Close()
+{
+ if (m_fds[0] != -1) close(m_fds[0]);
+ if (m_fds[1] != -1) close(m_fds[1]);
+ m_fds[0] = m_fds[1] = -1;
+}
+
+#endif // WIN32
diff --git a/src/util/tokenpipe.h b/src/util/tokenpipe.h
new file mode 100644
index 0000000000..f56be93a38
--- /dev/null
+++ b/src/util/tokenpipe.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2021 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_TOKENPIPE_H
+#define BITCOIN_UTIL_TOKENPIPE_H
+
+#ifndef WIN32
+
+#include <cstdint>
+#include <optional>
+
+/** One end of a token pipe. */
+class TokenPipeEnd
+{
+private:
+ int m_fd = -1;
+
+public:
+ TokenPipeEnd(int fd = -1);
+ ~TokenPipeEnd();
+
+ /** Return value constants for TokenWrite and TokenRead. */
+ enum Status {
+ TS_ERR = -1, //!< I/O error
+ TS_EOS = -2, //!< Unexpected end of stream
+ };
+
+ /** Write token to endpoint.
+ *
+ * @returns 0 If successful.
+ * <0 if error:
+ * TS_ERR If an error happened.
+ * TS_EOS If end of stream happened.
+ */
+ int TokenWrite(uint8_t token);
+
+ /** Read token from endpoint.
+ *
+ * @returns >=0 Token value, if successful.
+ * <0 if error:
+ * TS_ERR If an error happened.
+ * TS_EOS If end of stream happened.
+ */
+ int TokenRead();
+
+ /** Explicit close function.
+ */
+ void Close();
+
+ /** Return whether endpoint is open.
+ */
+ bool IsOpen() { return m_fd != -1; }
+
+ // Move-only class.
+ TokenPipeEnd(TokenPipeEnd&& other)
+ {
+ m_fd = other.m_fd;
+ other.m_fd = -1;
+ }
+ TokenPipeEnd& operator=(TokenPipeEnd&& other)
+ {
+ Close();
+ m_fd = other.m_fd;
+ other.m_fd = -1;
+ return *this;
+ }
+ TokenPipeEnd(const TokenPipeEnd&) = delete;
+ TokenPipeEnd& operator=(const TokenPipeEnd&) = delete;
+};
+
+/** An interprocess or interthread pipe for sending tokens (one-byte values)
+ * over.
+ */
+class TokenPipe
+{
+private:
+ int m_fds[2] = {-1, -1};
+
+ TokenPipe(int fds[2]) : m_fds{fds[0], fds[1]} {}
+
+public:
+ ~TokenPipe();
+
+ /** Create a new pipe.
+ * @returns The created TokenPipe, or an empty std::nullopt in case of error.
+ */
+ static std::optional<TokenPipe> Make();
+
+ /** Take the read end of this pipe. This can only be called once,
+ * as the object will be moved out.
+ */
+ TokenPipeEnd TakeReadEnd();
+
+ /** Take the write end of this pipe. This should only be called once,
+ * as the object will be moved out.
+ */
+ TokenPipeEnd TakeWriteEnd();
+
+ /** Close and end of the pipe that hasn't been moved out.
+ */
+ void Close();
+
+ // Move-only class.
+ TokenPipe(TokenPipe&& other)
+ {
+ for (int i = 0; i < 2; ++i) {
+ m_fds[i] = other.m_fds[i];
+ other.m_fds[i] = -1;
+ }
+ }
+ TokenPipe& operator=(TokenPipe&& other)
+ {
+ Close();
+ for (int i = 0; i < 2; ++i) {
+ m_fds[i] = other.m_fds[i];
+ other.m_fds[i] = -1;
+ }
+ return *this;
+ }
+ TokenPipe(const TokenPipe&) = delete;
+ TokenPipe& operator=(const TokenPipe&) = delete;
+};
+
+#endif // WIN32
+
+#endif // BITCOIN_UTIL_TOKENPIPE_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 38df71b994..96d12f10e2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -17,11 +17,12 @@
#include <cuckoocache.h>
#include <flatfile.h>
#include <hash.h>
+#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
+#include <node/coinstats.h>
#include <node/ui_interface.h>
-#include <optional.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <pow.h>
@@ -48,6 +49,7 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <optional>
#include <string>
#include <boost/algorithm/string/replace.hpp>
@@ -197,14 +199,20 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
std::unique_ptr<CBlockTreeDB> pblocktree;
-bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
+bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
+ const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
+ bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ std::vector<CScriptCheck>* pvChecks = nullptr)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main);
static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();
-bool CheckFinalTx(const CTransaction &tx, int flags)
+bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags)
{
AssertLockHeld(cs_main);
+ assert(active_chain_tip); // TODO: Make active_chain_tip a reference
+ assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
@@ -214,13 +222,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// scheduled, so no flags are set.
flags = std::max(flags, 0);
- // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate
+ // CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
// CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
- // IsFinalTx() with one more than ::ChainActive().Height().
- const int nBlockHeight = ::ChainActive().Height() + 1;
+ // IsFinalTx() with one more than active_chain_tip.Height().
+ const int nBlockHeight = active_chain_tip->nHeight + 1;
// BIP113 requires that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
@@ -228,13 +236,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
// chain tip, so we use that to calculate the median time passed to
// IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
- ? ::ChainActive().Tip()->GetMedianTimePast()
+ ? active_chain_tip->GetMedianTimePast()
: GetAdjustedTime();
return IsFinalTx(tx, nBlockHeight, nBlockTime);
}
-bool TestLockPointValidity(const LockPoints* lp)
+bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
{
AssertLockHeld(cs_main);
assert(lp);
@@ -243,7 +251,8 @@ bool TestLockPointValidity(const LockPoints* lp)
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- if (!::ChainActive().Contains(lp->maxInputBlock)) {
+ assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
+ if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
}
@@ -252,22 +261,28 @@ bool TestLockPointValidity(const LockPoints* lp)
return true;
}
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints)
+bool CheckSequenceLocks(CChainState& active_chainstate,
+ const CTxMemPool& pool,
+ const CTransaction& tx,
+ int flags,
+ LockPoints* lp,
+ bool useExistingLockPoints)
{
AssertLockHeld(cs_main);
AssertLockHeld(pool.cs);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
- CBlockIndex* tip = ::ChainActive().Tip();
+ CBlockIndex* tip = active_chainstate.m_chain.Tip();
assert(tip != nullptr);
CBlockIndex index;
index.pprev = tip;
- // CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate
+ // CheckSequenceLocks() uses active_chainstate.m_chain.Height()+1 to evaluate
// height based locks because when SequenceLocks() is called within
// ConnectBlock(), the height of the block *being*
// evaluated is what is used.
// Thus if we want to know if a transaction can be part of the
- // *next* block, we need to use one more than ::ChainActive().Height()
+ // *next* block, we need to use one more than active_chainstate.m_chain.Height()
index.nHeight = tip->nHeight + 1;
std::pair<int, int64_t> lockPair;
@@ -277,8 +292,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
- // CoinsTip() contains the UTXO set for ::ChainActive().Tip()
- CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
+ // CoinsTip() contains the UTXO set for active_chainstate.m_chain.Tip()
+ CCoinsViewMemPool viewMemPool(&active_chainstate.CoinsTip(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -327,7 +342,7 @@ 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, std::chrono::seconds age)
+static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age)
EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
{
int expired = pool.Expire(GetTime<std::chrono::seconds>() - age);
@@ -337,18 +352,20 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::second
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
for (const COutPoint& removed : vNoSpendsRemaining)
- ::ChainstateActive().CoinsTip().Uncache(removed);
+ coins_cache.Uncache(removed);
}
-static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload())
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (active_chainstate.IsInitialBlockDownload())
return false;
- if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
- if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
+ if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
return false;
return true;
}
@@ -366,10 +383,11 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
* and instead just erase from the mempool as needed.
*/
-static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
+static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -380,10 +398,8 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
- TxValidationState stateDummy;
if (!fAddToMempool || (*it)->IsCoinBase() ||
- !AcceptToMemoryPool(mempool, stateDummy, *it,
- nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
+ AcceptToMemoryPool(active_chainstate, mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -401,9 +417,9 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
- mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
+ mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
- LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(mempool, active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
}
/**
@@ -413,7 +429,7 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
* */
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& view, const CTxMemPool& pool,
- unsigned int flags, PrecomputedTransactionData& txdata)
+ unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{
AssertLockHeld(cs_main);
@@ -438,7 +454,8 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- const Coin& coinFromUTXOSet = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
+ const Coin& coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout);
assert(!coinFromUTXOSet.IsSpent());
assert(coinFromUTXOSet.out == coin.out);
}
@@ -453,19 +470,19 @@ namespace {
class MemPoolAccept
{
public:
- explicit MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool),
+ explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
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) {}
+ m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+ }
// We put the arguments we're handed into a struct, so we can pass them
// around easier.
struct ATMPArgs {
const CChainParams& m_chainparams;
- TxValidationState &m_state;
const int64_t m_accept_time;
- std::list<CTransactionRef>* m_replaced_transactions;
const bool m_bypass_limits;
/*
* Return any outpoints which were not previously present in the coins
@@ -476,11 +493,10 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
- CAmount* m_fee_out;
};
// Single transaction acceptance
- bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
private:
// All the intermediate state that gets passed between the various levels
@@ -491,14 +507,17 @@ private:
CTxMemPool::setEntries m_all_conflicting;
CTxMemPool::setEntries m_ancestors;
std::unique_ptr<CTxMemPoolEntry> m_entry;
+ std::list<CTransactionRef> m_replaced_transactions;
bool m_replacement_transaction;
+ CAmount m_base_fees;
CAmount m_modified_fees;
CAmount m_conflicting_fees;
size_t m_conflicting_size;
const CTransactionRef& m_ptx;
const uint256& m_hash;
+ TxValidationState m_state;
};
// Run the policy checks on a given transaction, excluding any script checks.
@@ -509,18 +528,18 @@ private:
// 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, const Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// 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, const Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// 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);
+ bool Finalize(const 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, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
@@ -542,6 +561,8 @@ private:
CCoinsViewMemPool m_viewmempool;
CCoinsView m_dummy;
+ CChainState& m_active_chainstate;
+
// The package limits in effect at the time of invocation.
const size_t m_limit_ancestors;
const size_t m_limit_ancestor_size;
@@ -558,12 +579,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
const uint256& hash = ws.m_hash;
// Copy/alias what we need out of args
- TxValidationState &state = args.m_state;
const int64_t nAcceptTime = args.m_accept_time;
const bool bypass_limits = args.m_bypass_limits;
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
// Alias what we need out of ws
+ TxValidationState& state = ws.m_state;
std::set<uint256>& setConflicts = ws.m_conflicts;
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
@@ -596,7 +617,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
+ assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+ if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
// is it already in the memory pool?
@@ -644,7 +666,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
LockPoints lp;
m_view.SetBackend(m_viewmempool);
- const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+ const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
if (!coins_cache.HaveCoinInCache(txin.prevout)) {
@@ -680,22 +703,19 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// 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))
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+ if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- CAmount nFees = 0;
- if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
+ assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
+ if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
- // If fee_out is passed, return the fee to the caller
- if (args.m_fee_out) {
- *args.m_fee_out = nFees;
- }
-
// Check for non-standard pay-to-script-hash in inputs
const auto& params = args.m_chainparams.GetConsensus();
- auto taproot_state = VersionBitsState(::ChainActive().Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
+ assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+ auto taproot_state = VersionBitsState(m_active_chainstate.m_chain.Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}
@@ -707,7 +727,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
// nModifiedFees includes any fee deltas from PrioritiseTransaction
- nModifiedFees = nFees;
+ nModifiedFees = ws.m_base_fees;
m_pool.ApplyDelta(hash, nModifiedFees);
// Keep track of transactions that spend a coinbase, which we re-scan
@@ -721,7 +741,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
+ assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+ entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -925,11 +946,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return true;
}
-bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
{
const CTransaction& tx = *ws.m_ptx;
-
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
@@ -952,12 +972,11 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, Prec
return true;
}
-bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
{
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
-
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
const CChainParams& chainparams = args.m_chainparams;
// Check again against the current block tip's script verification
@@ -975,8 +994,10 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, P
// 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, txdata)) {
+ assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+ if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
}
@@ -984,11 +1005,11 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, P
return true;
}
-bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
+bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
{
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
const bool bypass_limits = args.m_bypass_limits;
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
@@ -1007,8 +1028,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
hash.ToString(),
FormatMoney(nModifiedFees - nConflictingFees),
(int)entry->GetTxSize() - (int)nConflictingSize);
- if (args.m_replaced_transactions)
- args.m_replaced_transactions->push_back(it->GetSharedTx());
+ ws.m_replaced_transactions.push_back(it->GetSharedTx());
}
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
@@ -1017,28 +1037,30 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
// - 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);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+ bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && 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, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+ LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
return true;
}
-bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
+MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
{
AssertLockHeld(cs_main);
LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
- Workspace workspace(ptx);
+ Workspace ws(ptx);
- if (!PreChecks(args, workspace)) return false;
+ if (!PreChecks(args, ws)) return MempoolAcceptResult(ws.m_state);
// Only compute the precomputed transaction data if we need to verify
// scripts (ie, other policy checks pass). We perform the inexpensive
@@ -1046,51 +1068,56 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata;
- if (!PolicyScriptChecks(args, workspace, txdata)) return false;
+ if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
- if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
+ if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
// Tx was accepted, but not added
- if (args.m_test_accept) return true;
+ if (args.m_test_accept) {
+ return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ }
- if (!Finalize(args, workspace)) return false;
+ if (!Finalize(args, ws)) return MempoolAcceptResult(ws.m_state);
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
- return true;
+ return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
}
} // anon namespace
/** (try to) add transaction to memory pool with a specified acceptance time **/
-static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
+ CChainState& active_chainstate,
+ const CTransactionRef &tx, int64_t nAcceptTime,
+ bool bypass_limits, bool test_accept)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
- bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
- if (!res) {
+ MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
+
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// 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
// invalid transactions that attempt to overrun the in-memory coins cache
// (`CCoinsViewCache::cacheCoins`).
for (const COutPoint& hashTx : coins_to_uncache)
- ::ChainstateActive().CoinsTip().Uncache(hashTx);
+ active_chainstate.CoinsTip().Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
BlockValidationState state_dummy;
- ::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
- return res;
+ active_chainstate.FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
+ return result;
}
-bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept, CAmount* fee_out)
+MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
+ bool bypass_limits, bool test_accept)
{
- const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -1260,7 +1287,7 @@ CoinsViews::CoinsViews(
void CoinsViews::InitCache()
{
- m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
+ m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash)
@@ -1278,7 +1305,7 @@ void CChainState::InitCoinsDB(
leveldb_name += "_" + m_from_snapshot_blockhash.ToString();
}
- m_coins_views = MakeUnique<CoinsViews>(
+ m_coins_views = std::make_unique<CoinsViews>(
leveldb_name, cache_size_bytes, in_memory, should_wipe);
}
@@ -1336,16 +1363,18 @@ static void AlertNotify(const std::string& strMessage)
#endif
}
-static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
- if (::ChainstateActive().IsInitialBlockDownload()) {
+ if (IsInitialBlockDownload()) {
return;
}
- if (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)) {
+ if (pindexBestInvalid && pindexBestInvalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) {
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
SetfLargeWorkInvalidChainFound(true);
} else {
@@ -1354,21 +1383,22 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
}
// Called both upon regular invalid block discovery *and* InvalidateBlock
-void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
- pindexBestHeader = ::ChainActive().Tip();
+ pindexBestHeader = m_chain.Tip();
}
LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
- CBlockIndex *tip = ::ChainActive().Tip();
+ CBlockIndex *tip = m_chain.Tip();
assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n", __func__,
- tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
+ tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
}
@@ -1459,7 +1489,10 @@ void InitScriptExecutionCache() {
*
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
*/
-bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
+ const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
+ bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ std::vector<CScriptCheck>* pvChecks)
{
if (tx.IsCoinBase()) return true;
@@ -2249,17 +2282,25 @@ bool CChainState::FlushStateToDisk(
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
+
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
+ // make sure we don't prune above the blockfilterindexes bestblocks
+ // pruning is height-based
+ int last_prune = m_chain.Height(); // last height we can prune
+ ForEachBlockFilterIndex([&](BlockFilterIndex& index) {
+ last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
+ });
+
if (nManualPruneHeight > 0) {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
- m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height());
+ m_blockman.FindFilesToPruneManual(setFilesToPrune, std::min(last_prune, nManualPruneHeight), m_chain.Height());
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload());
+ m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -2395,7 +2436,7 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
@@ -2408,7 +2449,8 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- if (!::ChainstateActive().IsInitialBlockDownload()) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (!active_chainstate.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
@@ -2423,11 +2465,12 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f 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), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
@@ -2483,7 +2526,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(m_mempool, pindexDelete->pprev, chainparams);
+ UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2591,7 +2634,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(m_mempool, pindexNew, chainparams);
+ UpdateTip(m_mempool, pindexNew, chainparams, *this);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2682,6 +2725,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2693,7 +2737,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(m_mempool, disconnectpool, false);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2737,7 +2781,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(m_mempool, disconnectpool, false);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
return false;
}
} else {
@@ -2754,9 +2798,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(m_mempool, disconnectpool, true);
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
}
- m_mempool.check(&CoinsTip());
+ m_mempool.check(*this);
CheckForkWarningConditions();
@@ -2927,12 +2971,13 @@ bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams&
return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
}
-bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
- return ::ChainstateActive().PreciousBlock(state, params, pindex);
-}
bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
+ // Genesis block can't be invalidated
+ assert(pindex);
+ if (pindex->nHeight == 0) return false;
+
CBlockIndex* to_mark_failed = pindex;
bool pindex_was_in_chain = false;
int disconnected = 0;
@@ -2991,7 +3036,8 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3067,10 +3113,6 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
return true;
}
-bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
- return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
-}
-
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
@@ -3105,10 +3147,6 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
}
-void ResetBlockFailureFlags(CBlockIndex *pindex) {
- return ::ChainstateActive().ResetBlockFailureFlags(pindex);
-}
-
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{
AssertLockHeld(cs_main);
@@ -3192,7 +3230,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
}
}
-static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -3203,11 +3241,12 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
bool finalize_undo = false;
if (!fKnown) {
- while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
+ while (vinfoBlockFile[nFile].nSize + nAddSize >= (gArgs.GetBoolArg("-fastprune", false) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) {
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
+ assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -3219,7 +3258,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
if ((int)nFile != nLastBlockFile) {
if (!fKnown) {
- LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ LogPrint(BCLog::VALIDATION, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
@@ -3669,12 +3708,12 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) {
+static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) {
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
FlatFilePos blockPos;
if (dbp != nullptr)
blockPos = *dbp;
- if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
+ if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) {
error("%s: FindBlockPos failed", __func__);
return FlatFilePos();
}
@@ -3752,8 +3791,9 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
+ assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
- FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+ FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3924,17 +3964,18 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM
}
/* This function is called from the RPC code for pruneblockchain */
-void PruneBlockFilesManual(int nManualPruneHeight)
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
{
BlockValidationState state;
const CChainParams& chainparams = Params();
- if (!::ChainstateActive().FlushStateToDisk(
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (!active_chainstate.FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
}
-void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd)
+void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
{
LOCK2(cs_main, cs_LastBlockFile);
if (chain_tip_height < 0 || nPruneTarget == 0) {
@@ -3944,7 +3985,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
return;
}
- unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP));
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
@@ -3995,7 +4036,7 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
static FlatFileSeq BlockFileSeq()
{
- return FlatFileSeq(GetBlocksDir(), "blk", BLOCKFILE_CHUNK_SIZE);
+ return FlatFileSeq(GetBlocksDir(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
}
static FlatFileSeq UndoFileSeq()
@@ -4103,11 +4144,12 @@ void BlockManager::Unload() {
m_block_index.clear();
}
-bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
{
- if (!chainman.m_blockman.LoadBlockIndex(
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ if (!m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
- ::ChainstateActive().setBlockIndexCandidates)) {
+ setBlockIndexCandidates)) {
return false;
}
@@ -4131,7 +4173,7 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
+ for (const std::pair<const uint256, CBlockIndex*>& item : m_blockman.m_block_index) {
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
@@ -4161,7 +4203,8 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
void CChainState::LoadMempool(const ArgsManager& args)
{
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- ::LoadMempool(m_mempool);
+ assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+ ::LoadMempool(m_mempool, *this);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
}
@@ -4204,15 +4247,17 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100, false);
}
-bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
- LOCK(cs_main);
- if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr)
+ AssertLockHeld(cs_main);
+
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (active_chainstate.m_chain.Tip() == nullptr || active_chainstate.m_chain.Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height())
- nCheckDepth = ::ChainActive().Height();
+ if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height())
+ nCheckDepth = active_chainstate.m_chain.Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
@@ -4222,15 +4267,15 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
BlockValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
- const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+ for (pindex = active_chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
+ if (pindex->nHeight <= active_chainstate.m_chain.Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -4255,9 +4300,9 @@ 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() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= ::ChainstateActive().m_coinstip_cache_size_bytes) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@@ -4271,26 +4316,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (ShutdownRequested()) return true;
}
if (pindexFailure)
- return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", active_chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = ::ChainActive().Height() - pindex->nHeight;
+ int block_count = active_chainstate.m_chain.Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- while (pindex != ::ChainActive().Tip()) {
- const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+ while (pindex != active_chainstate.m_chain.Tip()) {
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
LogPrintf("[%d%%]...", percentageDone); /* Continued */
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
- pindex = ::ChainActive().Next(pindex);
+ pindex = active_chainstate.m_chain.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
- if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
if (ShutdownRequested()) return true;
}
@@ -4561,7 +4606,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = LoadBlockIndexDB(*this, chainparams);
+ bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams);
if (!ret) return false;
needs_init = m_blockman.m_block_index.empty();
}
@@ -4589,9 +4634,10 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
+ assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
const CBlock& block = chainparams.GenesisBlock();
- FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+ FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
@@ -4603,11 +4649,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
return true;
}
-bool LoadGenesisBlock(const CChainParams& chainparams)
-{
- return ::ChainstateActive().LoadGenesisBlock(chainparams);
-}
-
void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
@@ -4971,31 +5012,13 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
return &vinfoBlockFile.at(n);
}
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsStatistics(::ChainActive().Tip(), params, pos);
-}
-
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
- LOCK(cs_main);
- return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool LoadMempool(CTxMemPool& pool)
+bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
{
const CChainParams& chainparams = Params();
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
- FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
+ FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")};
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -5029,13 +5052,11 @@ bool LoadMempool(CTxMemPool& pool)
if (amountdelta) {
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}
- TxValidationState state;
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
- nullptr /* plTxnReplaced */, false /* bypass_limits */,
- false /* test_accept */);
- if (state.IsValid()) {
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
+ false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
} else {
// mempool may contain the transaction already, e.g. from
@@ -5078,7 +5099,7 @@ bool LoadMempool(CTxMemPool& pool)
return true;
}
-bool DumpMempool(const CTxMemPool& pool)
+bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit)
{
int64_t start = GetTimeMicros();
@@ -5101,7 +5122,7 @@ bool DumpMempool(const CTxMemPool& pool)
int64_t mid = GetTimeMicros();
try {
- FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
+ FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")};
if (!filestr) {
return false;
}
@@ -5124,7 +5145,7 @@ bool DumpMempool(const CTxMemPool& pool)
LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
file << unbroadcast_txids;
- if (!FileCommit(file.Get()))
+ if (!skip_file_commit && !FileCommit(file.Get()))
throw std::runtime_error("FileCommit failed");
file.fclose();
if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) {
@@ -5158,9 +5179,10 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
}
-Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
- LOCK(::cs_main); // for m_active_chainstate access
- if (m_active_chainstate != nullptr) {
+std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
+ LOCK(::cs_main);
+ if (m_active_chainstate != nullptr &&
+ !m_active_chainstate->m_from_snapshot_blockhash.IsNull()) {
// If a snapshot chainstate exists, it will always be our active.
return m_active_chainstate->m_from_snapshot_blockhash;
}
@@ -5169,6 +5191,7 @@ Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
std::vector<CChainState*> ChainstateManager::GetAll()
{
+ LOCK(::cs_main);
std::vector<CChainState*> out;
if (!IsSnapshotValidated() && m_ibd_chainstate) {
@@ -5204,6 +5227,295 @@ CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const
return *to_modify;
}
+const AssumeutxoData* ExpectedAssumeutxo(
+ const int height, const CChainParams& chainparams)
+{
+ const MapAssumeutxo& valid_assumeutxos_map = chainparams.Assumeutxo();
+ const auto assumeutxo_found = valid_assumeutxos_map.find(height);
+
+ if (assumeutxo_found != valid_assumeutxos_map.end()) {
+ return &assumeutxo_found->second;
+ }
+ return nullptr;
+}
+
+bool ChainstateManager::ActivateSnapshot(
+ CAutoFile& coins_file,
+ const SnapshotMetadata& metadata,
+ bool in_memory)
+{
+ uint256 base_blockhash = metadata.m_base_blockhash;
+
+ if (this->SnapshotBlockhash()) {
+ LogPrintf("[snapshot] can't activate a snapshot-based chainstate more than once\n");
+ return false;
+ }
+
+ int64_t current_coinsdb_cache_size{0};
+ int64_t current_coinstip_cache_size{0};
+
+ // Cache percentages to allocate to each chainstate.
+ //
+ // These particular percentages don't matter so much since they will only be
+ // relevant during snapshot activation; caches are rebalanced at the conclusion of
+ // this function. We want to give (essentially) all available cache capacity to the
+ // snapshot to aid the bulk load later in this function.
+ static constexpr double IBD_CACHE_PERC = 0.01;
+ static constexpr double SNAPSHOT_CACHE_PERC = 0.99;
+
+ {
+ LOCK(::cs_main);
+ // Resize the coins caches to ensure we're not exceeding memory limits.
+ //
+ // Allocate the majority of the cache to the incoming snapshot chainstate, since
+ // (optimistically) getting to its tip will be the top priority. We'll need to call
+ // `MaybeRebalanceCaches()` once we're done with this function to ensure
+ // the right allocation (including the possibility that no snapshot was activated
+ // and that we should restore the active chainstate caches to their original size).
+ //
+ current_coinsdb_cache_size = this->ActiveChainstate().m_coinsdb_cache_size_bytes;
+ current_coinstip_cache_size = this->ActiveChainstate().m_coinstip_cache_size_bytes;
+
+ // Temporarily resize the active coins cache to make room for the newly-created
+ // snapshot chain.
+ this->ActiveChainstate().ResizeCoinsCaches(
+ static_cast<size_t>(current_coinstip_cache_size * IBD_CACHE_PERC),
+ static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC));
+ }
+
+ auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
+ this->ActiveChainstate().m_mempool, m_blockman, base_blockhash));
+
+ {
+ LOCK(::cs_main);
+ snapshot_chainstate->InitCoinsDB(
+ static_cast<size_t>(current_coinsdb_cache_size * SNAPSHOT_CACHE_PERC),
+ in_memory, false, "chainstate");
+ snapshot_chainstate->InitCoinsCache(
+ static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
+ }
+
+ const bool snapshot_ok = this->PopulateAndValidateSnapshot(
+ *snapshot_chainstate, coins_file, metadata);
+
+ if (!snapshot_ok) {
+ WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
+ return false;
+ }
+
+ {
+ LOCK(::cs_main);
+ assert(!m_snapshot_chainstate);
+ m_snapshot_chainstate.swap(snapshot_chainstate);
+ const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(::Params());
+ assert(chaintip_loaded);
+
+ m_active_chainstate = m_snapshot_chainstate.get();
+
+ LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
+ LogPrintf("[snapshot] (%.2f MB)\n",
+ m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
+
+ this->MaybeRebalanceCaches();
+ }
+ return true;
+}
+
+bool ChainstateManager::PopulateAndValidateSnapshot(
+ CChainState& snapshot_chainstate,
+ CAutoFile& coins_file,
+ const SnapshotMetadata& metadata)
+{
+ // It's okay to release cs_main before we're done using `coins_cache` because we know
+ // that nothing else will be referencing the newly created snapshot_chainstate yet.
+ CCoinsViewCache& coins_cache = *WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsTip());
+
+ uint256 base_blockhash = metadata.m_base_blockhash;
+
+ COutPoint outpoint;
+ Coin coin;
+ const uint64_t coins_count = metadata.m_coins_count;
+ uint64_t coins_left = metadata.m_coins_count;
+
+ LogPrintf("[snapshot] loading coins from snapshot %s\n", base_blockhash.ToString());
+ int64_t flush_now{0};
+ int64_t coins_processed{0};
+
+ while (coins_left > 0) {
+ try {
+ coins_file >> outpoint;
+ } catch (const std::ios_base::failure&) {
+ LogPrintf("[snapshot] bad snapshot - no coins left after deserializing %d coins\n",
+ coins_count - coins_left);
+ return false;
+ }
+ coins_file >> coin;
+ coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
+
+ --coins_left;
+ ++coins_processed;
+
+ if (coins_processed % 1000000 == 0) {
+ LogPrintf("[snapshot] %d coins loaded (%.2f%%, %.2f MB)\n",
+ coins_processed,
+ static_cast<float>(coins_processed) * 100 / static_cast<float>(coins_count),
+ coins_cache.DynamicMemoryUsage() / (1000 * 1000));
+ }
+
+ // Batch write and flush (if we need to) every so often.
+ //
+ // If our average Coin size is roughly 41 bytes, checking every 120,000 coins
+ // means <5MB of memory imprecision.
+ if (coins_processed % 120000 == 0) {
+ if (ShutdownRequested()) {
+ return false;
+ }
+
+ const auto snapshot_cache_state = WITH_LOCK(::cs_main,
+ return snapshot_chainstate.GetCoinsCacheSizeState(&snapshot_chainstate.m_mempool));
+
+ if (snapshot_cache_state >=
+ CoinsCacheSizeState::CRITICAL) {
+ LogPrintf("[snapshot] flushing coins cache (%.2f MB)... ", /* Continued */
+ coins_cache.DynamicMemoryUsage() / (1000 * 1000));
+ flush_now = GetTimeMillis();
+
+ // This is a hack - we don't know what the actual best block is, but that
+ // doesn't matter for the purposes of flushing the cache here. We'll set this
+ // to its correct value (`base_blockhash`) below after the coins are loaded.
+ coins_cache.SetBestBlock(GetRandHash());
+
+ coins_cache.Flush();
+ LogPrintf("done (%.2fms)\n", GetTimeMillis() - flush_now);
+ }
+ }
+ }
+
+ // Important that we set this. This and the coins_cache accesses above are
+ // sort of a layer violation, but either we reach into the innards of
+ // CCoinsViewCache here or we have to invert some of the CChainState to
+ // embed them in a snapshot-activation-specific CCoinsViewCache bulk load
+ // method.
+ coins_cache.SetBestBlock(base_blockhash);
+
+ bool out_of_coins{false};
+ try {
+ coins_file >> outpoint;
+ } catch (const std::ios_base::failure&) {
+ // We expect an exception since we should be out of coins.
+ out_of_coins = true;
+ }
+ if (!out_of_coins) {
+ LogPrintf("[snapshot] bad snapshot - coins left over after deserializing %d coins\n",
+ coins_count);
+ return false;
+ }
+
+ LogPrintf("[snapshot] loaded %d (%.2f MB) coins from snapshot %s\n",
+ coins_count,
+ coins_cache.DynamicMemoryUsage() / (1000 * 1000),
+ base_blockhash.ToString());
+
+ LogPrintf("[snapshot] flushing snapshot chainstate to disk\n");
+ // No need to acquire cs_main since this chainstate isn't being used yet.
+ coins_cache.Flush(); // TODO: if #17487 is merged, add erase=false here for better performance.
+
+ assert(coins_cache.GetBestBlock() == base_blockhash);
+
+ CCoinsStats stats;
+ auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
+
+ // As above, okay to immediately release cs_main here since no other context knows
+ // about the snapshot_chainstate.
+ CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
+
+ if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
+ LogPrintf("[snapshot] failed to generate coins stats\n");
+ return false;
+ }
+
+ // Ensure that the base blockhash appears in the known chain of valid headers. We're willing to
+ // wait a bit here because the snapshot may have been loaded on startup, before we've
+ // received headers from the network.
+
+ int max_secs_to_wait_for_headers = 60 * 10;
+ CBlockIndex* snapshot_start_block = nullptr;
+
+ while (max_secs_to_wait_for_headers > 0) {
+ snapshot_start_block = WITH_LOCK(::cs_main,
+ return m_blockman.LookupBlockIndex(base_blockhash));
+ --max_secs_to_wait_for_headers;
+
+ if (!snapshot_start_block) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ } else {
+ break;
+ }
+ }
+
+ if (snapshot_start_block == nullptr) {
+ LogPrintf("[snapshot] timed out waiting for snapshot start blockheader %s\n",
+ base_blockhash.ToString());
+ return false;
+ }
+
+ // Assert that the deserialized chainstate contents match the expected assumeutxo value.
+
+ int base_height = snapshot_start_block->nHeight;
+ auto maybe_au_data = ExpectedAssumeutxo(base_height, ::Params());
+
+ if (!maybe_au_data) {
+ LogPrintf("[snapshot] assumeutxo height in snapshot metadata not recognized " /* Continued */
+ "(%d) - refusing to load snapshot\n", base_height);
+ return false;
+ }
+
+ const AssumeutxoData& au_data = *maybe_au_data;
+
+ if (stats.hashSerialized != au_data.hash_serialized) {
+ LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
+ au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
+ return false;
+ }
+
+ snapshot_chainstate.m_chain.SetTip(snapshot_start_block);
+
+ // The remainder of this function requires modifying data protected by cs_main.
+ LOCK(::cs_main);
+
+ // Fake various pieces of CBlockIndex state:
+ //
+ // - nChainTx: so that we accurately report IBD-to-tip progress
+ // - nTx: so that LoadBlockIndex() loads assumed-valid CBlockIndex entries
+ // (among other things)
+ // - nStatus & BLOCK_OPT_WITNESS: so that RewindBlockIndex() doesn't zealously
+ // unwind the assumed-valid chain.
+ //
+ CBlockIndex* index = nullptr;
+ for (int i = 0; i <= snapshot_chainstate.m_chain.Height(); ++i) {
+ index = snapshot_chainstate.m_chain[i];
+
+ if (!index->nTx) {
+ index->nTx = 1;
+ }
+ index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
+
+ // We need to fake this flag so that CChainState::RewindBlockIndex()
+ // won't try to rewind the entire assumed-valid chain on startup.
+ if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) {
+ index->nStatus |= BLOCK_OPT_WITNESS;
+ }
+ }
+
+ assert(index);
+ index->nChainTx = metadata.m_nchaintx;
+ snapshot_chainstate.setBlockIndexCandidates.insert(snapshot_start_block);
+
+ LogPrintf("[snapshot] validated snapshot (%.2f MB)\n",
+ coins_cache.DynamicMemoryUsage() / (1000 * 1000));
+ return true;
+}
+
CChainState& ChainstateManager::ActiveChainstate() const
{
LOCK(::cs_main);
@@ -5213,11 +5525,13 @@ CChainState& ChainstateManager::ActiveChainstate() const
bool ChainstateManager::IsSnapshotActive() const
{
- return m_snapshot_chainstate && WITH_LOCK(::cs_main, return m_active_chainstate) == m_snapshot_chainstate.get();
+ LOCK(::cs_main);
+ return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
}
CChainState& ChainstateManager::ValidatedChainstate() const
{
+ LOCK(::cs_main);
if (m_snapshot_chainstate && IsSnapshotValidated()) {
return *m_snapshot_chainstate.get();
}
@@ -5227,6 +5541,7 @@ CChainState& ChainstateManager::ValidatedChainstate() const
bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
{
+ LOCK(::cs_main);
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
}
@@ -5242,12 +5557,10 @@ void ChainstateManager::Unload()
void ChainstateManager::Reset()
{
+ LOCK(::cs_main);
m_ibd_chainstate.reset();
m_snapshot_chainstate.reset();
- {
- LOCK(::cs_main);
- m_active_chainstate = nullptr;
- }
+ m_active_chainstate = nullptr;
m_snapshot_validated = false;
}
diff --git a/src/validation.h b/src/validation.h
index fc7add85b7..2ff5f4ac87 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -11,10 +11,12 @@
#endif
#include <amount.h>
+#include <attributes.h>
#include <coins.h>
+#include <consensus/validation.h>
#include <crypto/common.h> // for ReadLE64
#include <fs.h>
-#include <optional.h>
+#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
@@ -23,11 +25,13 @@
#include <txdb.h>
#include <versionbits.h>
#include <serialize.h>
+#include <util/check.h>
#include <util/hasher.h>
#include <atomic>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <stdint.h>
#include <string>
@@ -46,12 +50,12 @@ class CConnman;
class CScriptCheck;
class CTxMemPool;
class ChainstateManager;
-class TxValidationState;
struct ChainTxData;
struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
+struct AssumeutxoData;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
@@ -144,8 +148,6 @@ extern const std::vector<std::string> CHECKLEVEL_DOC;
FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
fs::path GetBlockPosFilename(const FlatFilePos &pos);
-/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
-bool LoadGenesisBlock(const CChainParams& chainparams);
/** Unload database information */
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
/** Run instances of script checking worker threads */
@@ -179,23 +181,47 @@ uint64_t CalculateCurrentUsage();
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Prune block files up to a given height */
-void PruneBlockFilesManual(int nManualPruneHeight);
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
-/** (try to) add transaction to memory pool
- * plTxnReplaced will be appended to with all transactions replaced from mempool
- * @param[out] fee_out optional argument to return tx fee to the caller **/
-bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
-/** Get the BIP9 state for a given deployment at the current tip. */
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
-/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos);
+/**
+* Validation result for a single transaction mempool acceptance.
+*/
+struct MempoolAcceptResult {
+ /** Used to indicate the results of mempool validation,
+ * including the possibility of unfinished validation.
+ */
+ enum class ResultType {
+ VALID, //!> Fully validated, valid.
+ INVALID, //!> Invalid.
+ };
+ ResultType m_result_type;
+ TxValidationState m_state;
+
+ // The following fields are only present when m_result_type = ResultType::VALID
+ /** Mempool transactions replaced by the tx per BIP 125 rules. */
+ std::optional<std::list<CTransactionRef>> m_replaced_transactions;
+ /** Raw base fees. */
+ std::optional<CAmount> m_base_fees;
+
+ /** Constructor for failure case */
+ explicit MempoolAcceptResult(TxValidationState state)
+ : m_result_type(ResultType::INVALID), m_state(state) {
+ Assume(!state.IsValid()); // Can be invalid or error
+ }
+
+ /** Constructor for success case */
+ explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, CAmount fees)
+ : m_result_type(ResultType::VALID), m_state(TxValidationState{}),
+ m_replaced_transactions(std::move(replaced_txns)), m_base_fees(fees) {}
+};
-/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
+/**
+ * (Try to) add a transaction to the memory pool.
+ * @param[in] bypass_limits When true, don't enforce mempool fee limits.
+ * @param[in] test_accept When true, run validation checks but don't submit to mempool.
+ */
+MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
+ bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Apply the effects of this transaction on the UTXO set represented by view */
@@ -210,12 +236,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckFinalTx(const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Test whether the LockPoints height and time are still valid on the current chain
*/
-bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Check if transaction will be BIP 68 final in the next block to be created.
@@ -228,7 +254,12 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
+bool CheckSequenceLocks(CChainState& active_chainstate,
+ const CTxMemPool& pool,
+ const CTransaction& tx,
+ int flags,
+ LockPoints* lp = nullptr,
+ bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
/**
* Closure representing one script verification
@@ -306,7 +337,7 @@ class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
- bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
+ bool VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
enum DisconnectResult
@@ -361,7 +392,7 @@ private:
*
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
*/
- void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd);
+ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
public:
BlockMap m_block_index GUARDED_BY(cs_main);
@@ -678,13 +709,20 @@ public:
bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
+ /** Mark a block as precious and reorganize.
+ *
+ * May not be called in a validationinterface callback.
+ */
bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ /** Mark a block as invalid. */
bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ /** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params);
bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
+ /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
void PruneBlockIndexCandidates();
@@ -733,21 +771,13 @@ private:
//! Mark a block as not having block data
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- friend ChainstateManager;
-};
-
-/** Mark a block as precious and reorganize.
- *
- * May not be called in a
- * validationinterface callback.
- */
-bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
+ void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Mark a block as invalid. */
-bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Remove invalidity status from a block and its descendants. */
-void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ friend ChainstateManager;
+};
/**
* Provides an interface for creating and interacting with one or two
@@ -795,35 +825,29 @@ private:
//! using this pointer (e.g. net_processing).
//!
//! Once this pointer is set to a corresponding chainstate, it will not
- //! be reset until init.cpp:Shutdown(). This means it is safe to acquire
- //! the contents of this pointer with ::cs_main held, release the lock,
- //! and then use the reference without concern of it being deconstructed.
+ //! be reset until init.cpp:Shutdown().
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_ibd_chainstate;
+ std::unique_ptr<CChainState> m_ibd_chainstate GUARDED_BY(::cs_main);
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
//! non-null, it is always our active chainstate.
//!
//! Once this pointer is set to a corresponding chainstate, it will not
- //! be reset until init.cpp:Shutdown(). This means it is safe to acquire
- //! the contents of this pointer with ::cs_main held, release the lock,
- //! and then use the reference without concern of it being deconstructed.
+ //! be reset until init.cpp:Shutdown().
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_snapshot_chainstate;
+ std::unique_ptr<CChainState> m_snapshot_chainstate GUARDED_BY(::cs_main);
//! Points to either the ibd or snapshot chainstate; indicates our
//! most-work chain.
//!
//! Once this pointer is set to a corresponding chainstate, it will not
- //! be reset until init.cpp:Shutdown(). This means it is safe to acquire
- //! the contents of this pointer with ::cs_main held, release the lock,
- //! and then use the reference without concern of it being deconstructed.
+ //! be reset until init.cpp:Shutdown().
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
@@ -834,6 +858,12 @@ private:
//! by the background validation chainstate.
bool m_snapshot_validated{false};
+ //! Internal helper for ActivateSnapshot().
+ [[nodiscard]] bool PopulateAndValidateSnapshot(
+ CChainState& snapshot_chainstate,
+ CAutoFile& coins_file,
+ const SnapshotMetadata& metadata);
+
// For access to m_active_chainstate.
friend CChainState& ChainstateActive();
friend CChain& ChainActive();
@@ -864,6 +894,22 @@ public:
//! Get all chainstates currently being used.
std::vector<CChainState*> GetAll();
+ //! Construct and activate a Chainstate on the basis of UTXO snapshot data.
+ //!
+ //! Steps:
+ //!
+ //! - Initialize an unused CChainState.
+ //! - Load its `CoinsViews` contents from `coins_file`.
+ //! - Verify that the hash of the resulting coinsdb matches the expected hash
+ //! per assumeutxo chain parameters.
+ //! - Wait for our headers chain to include the base block of the snapshot.
+ //! - "Fast forward" the tip of the new chainstate to the base of the snapshot,
+ //! faking nTx* block index data along the way.
+ //! - Move the new chainstate to `m_snapshot_chainstate` and make it our
+ //! ChainstateActive().
+ [[nodiscard]] bool ActivateSnapshot(
+ CAutoFile& coins_file, const SnapshotMetadata& metadata, bool in_memory);
+
//! The most-work chain.
CChainState& ActiveChainstate() const;
CChain& ActiveChain() const { return ActiveChainstate().m_chain; }
@@ -877,7 +923,7 @@ public:
bool IsSnapshotActive() const;
- Optional<uint256> SnapshotBlockhash() const;
+ std::optional<uint256> SnapshotBlockhash() const;
//! Is there a snapshot in use and has it been fully validated?
bool IsSnapshotValidated() const { return m_snapshot_validated; }
@@ -966,11 +1012,13 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para
/** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n);
+using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
+
/** Dump the mempool to disk. */
-bool DumpMempool(const CTxMemPool& pool);
+bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false);
/** Load the mempool from disk. */
-bool LoadMempool(CTxMemPool& pool);
+bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen);
//! Check whether the block associated with this index entry is pruned or not.
inline bool IsBlockPruned(const CBlockIndex* pblockindex)
@@ -978,4 +1026,13 @@ inline bool IsBlockPruned(const CBlockIndex* pblockindex)
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
+/**
+ * Return the expected assumeutxo value for a given height, if one exists.
+ *
+ * @param height[in] Get the assumeutxo value for this height.
+ *
+ * @returns empty if no assumeutxo configuration exists for the given height.
+ */
+const AssumeutxoData* ExpectedAssumeutxo(const int height, const CChainParams& params);
+
#endif // BITCOIN_VALIDATION_H
diff --git a/src/versionbits.h b/src/versionbits.h
index b02f848b67..6df1db8814 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -79,8 +79,11 @@ struct VersionBitsCache
void Clear();
};
+/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index ad40e6da9a..1dc23374e3 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -331,7 +331,7 @@ void BerkeleyDatabase::Open()
if (m_db == nullptr) {
int ret;
- std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
+ std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
bool fMockDb = env->IsMock();
if (fMockDb) {
@@ -462,7 +462,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {}
BerkeleyBatch db(*this, true);
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+ std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
strFileRes.c_str(), // Filename
@@ -819,7 +819,7 @@ void BerkeleyDatabase::RemoveRef()
std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
{
- return MakeUnique<BerkeleyBatch>(*this, false, flush_on_close);
+ return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
}
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
@@ -835,7 +835,7 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr;
}
- db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
+ db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
}
if (options.verify && !db->Verify(error)) {
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index c499b0ff25..d25a3fb3fa 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -5,13 +5,14 @@
#ifndef BITCOIN_WALLET_COINCONTROL_H
#define BITCOIN_WALLET_COINCONTROL_H
-#include <optional.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
#include <script/standard.h>
+#include <optional>
+
const int DEFAULT_MIN_DEPTH = 0;
const int DEFAULT_MAX_DEPTH = 9999999;
@@ -25,7 +26,7 @@ public:
//! Custom change destination, if not set an address is generated
CTxDestination destChange;
//! Override the default change type if set, ignored if destChange is set
- Optional<OutputType> m_change_type;
+ std::optional<OutputType> m_change_type;
//! If false, only selected inputs are used
bool m_add_inputs;
//! If false, allows unselected inputs, but requires all selected inputs be used
@@ -35,11 +36,11 @@ public:
//! Override automatic min/max checks on fee, m_feerate must be set if true
bool fOverrideFeeRate;
//! Override the wallet's m_pay_tx_fee if set
- Optional<CFeeRate> m_feerate;
+ std::optional<CFeeRate> m_feerate;
//! Override the default confirmation target if set
- Optional<unsigned int> m_confirm_target;
+ std::optional<unsigned int> m_confirm_target;
//! Override the wallet's m_signal_rbf if set
- Optional<bool> m_signal_bip125_rbf;
+ std::optional<bool> m_signal_bip125_rbf;
//! Avoid partial use of funds sent to a given address
bool m_avoid_partial_spends;
//! Forbids inclusion of dirty (previously used) addresses
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 10f89e3a6f..5a18308a73 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -4,11 +4,12 @@
#include <wallet/coinselection.h>
-#include <optional.h>
#include <policy/feerate.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <optional>
+
// Descending order comparator
struct {
bool operator()(const OutputGroup& a, const OutputGroup& b) const
@@ -222,7 +223,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
nValueRet = 0;
// List of values less than target
- Optional<OutputGroup> lowest_larger;
+ std::optional<OutputGroup> lowest_larger;
std::vector<OutputGroup> applicable_groups;
CAmount nTotalLower = 0;
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 2c75486a44..7a0d3d2e07 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -8,13 +8,12 @@
#include <clientversion.h>
#include <fs.h>
-#include <optional.h>
#include <streams.h>
#include <support/allocators/secure.h>
-#include <util/memory.h>
#include <atomic>
#include <memory>
+#include <optional>
#include <string>
struct bilingual_str;
@@ -193,7 +192,7 @@ public:
void ReloadDbEnv() override {}
std::string Filename() override { return "dummy"; }
std::string Format() override { return "dummy"; }
- std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
+ std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
};
enum class DatabaseFormat {
@@ -204,7 +203,7 @@ enum class DatabaseFormat {
struct DatabaseOptions {
bool require_existing = false;
bool require_create = false;
- Optional<DatabaseFormat> require_format;
+ std::optional<DatabaseFormat> require_format;
uint64_t create_flags = 0;
SecureString create_passphrase;
bool verify = true;
diff --git a/src/wallet/external_signer.cpp b/src/wallet/external_signer.cpp
new file mode 100644
index 0000000000..3396111760
--- /dev/null
+++ b/src/wallet/external_signer.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) 2018-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <core_io.h>
+#include <psbt.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <wallet/external_signer.h>
+
+ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, std::string chain, std::string name): m_command(command), m_fingerprint(fingerprint), m_chain(chain), m_name(name) {}
+
+const std::string ExternalSigner::NetworkArg() const
+{
+ return " --chain " + m_chain;
+}
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, std::string chain, bool ignore_errors)
+{
+ // Call <command> enumerate
+ const UniValue result = RunCommandParseJSON(command + " enumerate");
+ if (!result.isArray()) {
+ if (ignore_errors) return false;
+ throw ExternalSignerException(strprintf("'%s' received invalid response, expected array of signers", command));
+ }
+ for (UniValue signer : result.getValues()) {
+ // Check for error
+ const UniValue& error = find_value(signer, "error");
+ if (!error.isNull()) {
+ if (ignore_errors) return false;
+ if (!error.isStr()) {
+ throw ExternalSignerException(strprintf("'%s' error", command));
+ }
+ throw ExternalSignerException(strprintf("'%s' error: %s", command, error.getValStr()));
+ }
+ // Check if fingerprint is present
+ const UniValue& fingerprint = find_value(signer, "fingerprint");
+ if (fingerprint.isNull()) {
+ if (ignore_errors) return false;
+ throw ExternalSignerException(strprintf("'%s' received invalid response, missing signer fingerprint", command));
+ }
+ std::string fingerprintStr = fingerprint.get_str();
+ // Skip duplicate signer
+ bool duplicate = false;
+ for (ExternalSigner signer : signers) {
+ if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
+ }
+ if (duplicate) break;
+ std::string name = "";
+ const UniValue& model_field = find_value(signer, "model");
+ if (model_field.isStr() && model_field.getValStr() != "") {
+ name += model_field.getValStr();
+ }
+ signers.push_back(ExternalSigner(command, fingerprintStr, chain, name));
+ }
+ return true;
+}
+
+UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
+{
+ return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
+}
+
+UniValue ExternalSigner::GetDescriptors(int account)
+{
+ return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
+}
+
+bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error)
+{
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+
+ // Check if signer fingerprint matches any input master key fingerprint
+ bool match = false;
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const PSBTInput& input = psbtx.inputs[i];
+ for (auto entry : input.hd_keypaths) {
+ if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true;
+ }
+ }
+
+ if (!match) {
+ error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
+ return false;
+ }
+
+ std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
+ std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";
+
+ const UniValue signer_result = RunCommandParseJSON(command, stdinStr);
+
+ if (find_value(signer_result, "error").isStr()) {
+ error = find_value(signer_result, "error").get_str();
+ return false;
+ }
+
+ if (!find_value(signer_result, "psbt").isStr()) {
+ error = "Unexpected result from signer";
+ return false;
+ }
+
+ PartiallySignedTransaction signer_psbtx;
+ std::string signer_psbt_error;
+ if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) {
+ error = strprintf("TX decode failed %s", signer_psbt_error);
+ return false;
+ }
+
+ psbtx = signer_psbtx;
+
+ return true;
+}
+
+#endif
diff --git a/src/wallet/external_signer.h b/src/wallet/external_signer.h
new file mode 100644
index 0000000000..4b9711107b
--- /dev/null
+++ b/src/wallet/external_signer.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2018-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_EXTERNAL_SIGNER_H
+#define BITCOIN_WALLET_EXTERNAL_SIGNER_H
+
+#include <stdexcept>
+#include <string>
+#include <univalue.h>
+#include <util/system.h>
+
+struct PartiallySignedTransaction;
+
+class ExternalSignerException : public std::runtime_error {
+public:
+ using std::runtime_error::runtime_error;
+};
+
+//! Enables interaction with an external signing device or service, such as
+//! a hardware wallet. See doc/external-signer.md
+class ExternalSigner
+{
+private:
+ //! The command which handles interaction with the external signer.
+ std::string m_command;
+
+public:
+ //! @param[in] command the command which handles interaction with the external signer
+ //! @param[in] fingerprint master key fingerprint of the signer
+ //! @param[in] chain "main", "test", "regtest" or "signet"
+ //! @param[in] name device name
+ ExternalSigner(const std::string& command, const std::string& fingerprint, std::string chain, std::string name);
+
+ //! Master key fingerprint of the signer
+ std::string m_fingerprint;
+
+ //! Bitcoin mainnet, testnet, etc
+ std::string m_chain;
+
+ //! Name of signer
+ std::string m_name;
+
+ const std::string NetworkArg() const;
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+ //! Obtain a list of signers. Calls `<command> enumerate`.
+ //! @param[in] command the command which handles interaction with the external signer
+ //! @param[in,out] signers vector to which new signers (with a unique master key fingerprint) are added
+ //! @param chain "main", "test", "regtest" or "signet"
+ //! @param[out] success Boolean
+ static bool Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, std::string chain, bool ignore_errors = false);
+
+ //! Display address on the device. Calls `<command> displayaddress --desc <descriptor>`.
+ //! @param[in] descriptor Descriptor specifying which address to display.
+ //! Must include a public key or xpub, as well as key origin.
+ UniValue DisplayAddress(const std::string& descriptor) const;
+
+ //! Get receive and change Descriptor(s) from device for a given account.
+ //! Calls `<command> getdescriptors --account <account>`
+ //! @param[in] account which BIP32 account to use (e.g. `m/44'/0'/account'`)
+ //! @param[out] UniValue see doc/external-signer.md
+ UniValue GetDescriptors(int account);
+
+ //! Sign PartiallySignedTransaction on the device.
+ //! Calls `<command> signtransaction` and passes the PSBT via stdin.
+ //! @param[in,out] psbt PartiallySignedTransaction to be signed
+ bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error);
+
+#endif
+};
+
+#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_H
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
new file mode 100644
index 0000000000..a2071e521a
--- /dev/null
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <wallet/external_signer.h>
+#include <wallet/external_signer_scriptpubkeyman.h>
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
+{
+ LOCK(cs_desc_man);
+ assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
+ assert(m_storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
+
+ int64_t creation_time = GetTime();
+
+ // Make the descriptor
+ WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
+ m_wallet_descriptor = w_desc;
+
+ // Store the descriptor
+ WalletBatch batch(m_storage.GetDatabase());
+ if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) {
+ throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");
+ }
+
+ // TopUp
+ TopUp();
+
+ m_storage.UnsetBlankWalletFlag(batch);
+ return true;
+}
+
+ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>");
+ std::vector<ExternalSigner> signers;
+ ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+ if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found");
+ // TODO: add fingerprint argument in case of multiple signers
+ return signers[0];
+}
+
+bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const
+{
+ // TODO: avoid the need to infer a descriptor from inside a descriptor wallet
+ auto provider = GetSolvingProvider(scriptPubKey);
+ auto descriptor = InferDescriptor(scriptPubKey, *provider);
+
+ signer.DisplayAddress(descriptor->ToString());
+ // TODO inspect result
+ return true;
+}
+
+// If sign is true, transaction must previously have been filled
+TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+{
+ if (!sign) {
+ return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
+ }
+
+ // Already complete if every input is now signed
+ bool complete = true;
+ for (const auto& input : psbt.inputs) {
+ // TODO: for multisig wallets, we should only care if all _our_ inputs are signed
+ complete &= PSBTInputSigned(input);
+ }
+ if (complete) return TransactionError::OK;
+
+ std::string strFailReason;
+ if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
+ tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
+ return TransactionError::EXTERNAL_SIGNER_FAILED;
+ }
+ FinalizePSBT(psbt); // This won't work in a multisig setup
+ return TransactionError::OK;
+}
+
+#endif
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
new file mode 100644
index 0000000000..e60d7b8004
--- /dev/null
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2019-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
+#define BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+#include <wallet/scriptpubkeyman.h>
+
+class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
+{
+ public:
+ ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
+ : DescriptorScriptPubKeyMan(storage, descriptor)
+ {}
+ ExternalSignerScriptPubKeyMan(WalletStorage& storage, bool internal)
+ : DescriptorScriptPubKeyMan(storage, internal)
+ {}
+
+ /** Provide a descriptor at setup time
+ * Returns false if already setup or setup fails, true if setup is successful
+ */
+ bool SetupDescriptor(std::unique_ptr<Descriptor>desc);
+
+ static ExternalSigner GetExternalSigner();
+
+ bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
+
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+};
+#endif
+
+#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 5e319d4f95..08adf09df4 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -190,7 +190,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
// We calculate this here to avoid compiler warning on the cs_wallet lock
- const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet);
+ const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet).first;
Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors);
if (res != Result::OK) {
return res;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 0d2be64dfb..fdeead1fa5 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -61,13 +61,16 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.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())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+#ifdef ENABLE_EXTERNAL_SIGNER
+ argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+#endif
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.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);
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
- argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID, %w is replaced by wallet name, %b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included) and %h is replaced by the block height (-1 if not included). %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index e4e8c50f4f..ada586119a 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -23,6 +23,7 @@
#include <wallet/fees.h>
#include <wallet/ismine.h>
#include <wallet/load.h>
+#include <wallet/rpcsigner.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
@@ -518,6 +519,15 @@ public:
}, command.argNames, command.unique_id);
m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
}
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+ for (const CRPCCommand& command : GetSignerRPCCommands()) {
+ m_rpc_commands.emplace_back(command.category, command.name, [this, &command](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
+ return command.actor({request, m_context}, result, last_handler);
+ }, command.argNames, command.unique_id);
+ m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
+ }
+#endif
}
bool verify() override { return VerifyWallets(*m_context.chain); }
bool load() override { return LoadWallets(*m_context.chain); }
@@ -578,10 +588,10 @@ public:
} // namespace wallet
namespace interfaces {
-std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<wallet::WalletImpl>(wallet) : nullptr; }
+std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(wallet) : nullptr; }
std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args)
{
- return MakeUnique<wallet::WalletClientImpl>(chain, args);
+ return std::make_unique<wallet::WalletClientImpl>(chain, args);
}
} // namespace interfaces
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 30832f983b..4543f6fb4c 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -154,7 +154,7 @@ void UnloadWallets()
auto wallet = wallets.back();
wallets.pop_back();
std::vector<bilingual_str> warnings;
- RemoveWallet(wallet, nullopt, warnings);
+ RemoveWallet(wallet, std::nullopt, warnings);
UnloadWallet(std::move(wallet));
}
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 8505ddc309..0737e68cf3 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -56,13 +56,13 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet& wallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
bool fLabelFound = false;
CKey key;
spk_man->GetKey(keyid, key);
for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
- const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(dest);
if (address_book_entry) {
if (!strAddr.empty()) {
strAddr += ",";
@@ -73,7 +73,7 @@ static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWall
}
}
if (!fLabelFound) {
- strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), pwallet->m_default_address_type));
+ strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), wallet.m_default_address_type));
}
return fLabelFound;
}
@@ -118,22 +118,21 @@ RPCHelpMan importprivkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
WalletRescanReserver reserver(*pwallet);
bool fRescan = true;
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strSecret = request.params[0].get_str();
std::string strLabel = "";
@@ -210,9 +209,8 @@ RPCHelpMan abortrescan()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
@@ -249,9 +247,8 @@ RPCHelpMan importaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -335,9 +332,8 @@ RPCHelpMan importprunedfunds()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
@@ -397,9 +393,8 @@ RPCHelpMan removeprunedfunds()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -445,11 +440,10 @@ RPCHelpMan importpubkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel;
if (!request.params[1].isNull())
@@ -527,11 +521,10 @@ RPCHelpMan importwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->chain().havePruned()) {
// Exit early and print an error.
@@ -550,7 +543,7 @@ RPCHelpMan importwallet()
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file;
file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
@@ -684,15 +677,14 @@ RPCHelpMan dumpprivkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
- LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str();
CTxDestination dest = DecodeDestination(strAddress);
@@ -747,7 +739,7 @@ RPCHelpMan dumpwallet()
LOCK2(wallet.cs_wallet, spk_man.cs_KeyStore);
- EnsureWalletIsUnlocked(&wallet);
+ EnsureWalletIsUnlocked(wallet);
fs::path filepath = request.params[0].get_str();
filepath = fs::absolute(filepath);
@@ -809,7 +801,7 @@ RPCHelpMan dumpwallet()
CKey key;
if (spk_man.GetKey(keyid, key)) {
file << strprintf("%s %s ", EncodeSecret(key), strTime);
- if (GetWalletAddressesForKey(&spk_man, &wallet, keyid, strAddr, strLabel)) {
+ if (GetWalletAddressesForKey(&spk_man, wallet, keyid, strAddr, strLabel)) {
file << strprintf("label=%s", strLabel);
} else if (keyid == seed_id) {
file << "hdseed=1";
@@ -934,9 +926,9 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
case TxoutType::NONSTANDARD:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
- default:
return "unrecognized script";
- }
+ } // no default case, so the compiler can warn about missing cases
+ CHECK_NONFATAL(false);
}
static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
@@ -990,14 +982,14 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
}
auto parsed_redeemscript = ParseHex(strRedeemScript);
- import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
+ import_data.redeemscript = std::make_unique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
}
if (witness_script_hex.size()) {
if (!IsHex(witness_script_hex)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
}
auto parsed_witnessscript = ParseHex(witness_script_hex);
- import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ import_data.witnessscript = std::make_unique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
}
for (size_t i = 0; i < pubKeys.size(); ++i) {
const auto& str = pubKeys[i].get_str();
@@ -1169,7 +1161,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
return warnings;
}
-static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ);
@@ -1184,7 +1176,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
// Add to keypool only works with privkeys disabled
- if (add_keypool && !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (add_keypool && !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
}
@@ -1206,29 +1198,29 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
}
// If private keys are disabled, abort if private keys are being imported
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
// Check whether we have any work to do
for (const CScript& script : script_pub_keys) {
- if (pwallet->IsMine(script) & ISMINE_SPENDABLE) {
+ if (wallet.IsMine(script) & ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
}
}
// All good, time to import
- pwallet->MarkDirty();
- if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) {
+ wallet.MarkDirty();
+ if (!wallet.ImportScripts(import_data.import_scripts, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
- if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
+ if (!wallet.ImportPrivKeys(privkey_map, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
+ if (!wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
+ if (!wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
@@ -1336,13 +1328,12 @@ RPCHelpMan importmulti()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
- EnsureLegacyScriptPubKeyMan(*wallet, true);
+ EnsureLegacyScriptPubKeyMan(*pwallet, true);
const UniValue& requests = mainRequest.params[0];
@@ -1368,7 +1359,7 @@ RPCHelpMan importmulti()
UniValue response(UniValue::VARR);
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
// Verify all timestamps are present before importing any keys.
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
@@ -1380,7 +1371,7 @@ RPCHelpMan importmulti()
for (const UniValue& data : requests.getValues()) {
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
- const UniValue result = ProcessImport(pwallet, data, timestamp);
+ const UniValue result = ProcessImport(*pwallet, data, timestamp);
response.push_back(result);
if (!fRescan) {
@@ -1447,7 +1438,7 @@ RPCHelpMan importmulti()
};
}
-static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ);
@@ -1516,7 +1507,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// If the wallet disabled private keys, abort if private keys exist
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
@@ -1540,7 +1531,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// If private keys are enabled, check some things.
- if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
}
@@ -1552,7 +1543,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
// Check if the wallet already contains the descriptor
- auto existing_spk_manager = pwallet->GetDescriptorScriptPubKeyMan(w_desc);
+ auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
if (existing_spk_manager) {
LOCK(existing_spk_manager->cs_desc_man);
if (range_start > existing_spk_manager->GetWalletDescriptor().range_start) {
@@ -1561,7 +1552,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
}
// Add descriptor to the wallet
- auto spk_manager = pwallet->AddWalletDescriptor(w_desc, keys, label, internal);
+ auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
if (spk_manager == nullptr) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
}
@@ -1571,7 +1562,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else {
- pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
+ wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
}
}
@@ -1641,9 +1632,8 @@ RPCHelpMan importdescriptors()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
+ if (!pwallet) return NullUniValue;
// Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
@@ -1665,7 +1655,7 @@ RPCHelpMan importdescriptors()
UniValue response(UniValue::VARR);
{
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
@@ -1673,7 +1663,7 @@ RPCHelpMan importdescriptors()
for (const UniValue& request : requests.getValues()) {
// This throws an error if "timestamp" doesn't exist
const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
- const UniValue result = ProcessDescriptorImport(pwallet, request, timestamp);
+ const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
response.push_back(result);
if (lowest_timestamp > timestamp ) {
@@ -1775,6 +1765,8 @@ RPCHelpMan listdescriptors()
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
}
+ EnsureWalletIsUnlocked(*wallet);
+
LOCK(wallet->cs_wallet);
UniValue response(UniValue::VARR);
@@ -1787,12 +1779,16 @@ RPCHelpMan listdescriptors()
UniValue spk(UniValue::VOBJ);
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
- spk.pushKV("desc", wallet_descriptor.descriptor->ToString());
+ std::string descriptor;
+ if (!desc_spk_man->GetDescriptorString(descriptor, false)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string.");
+ }
+ spk.pushKV("desc", descriptor);
spk.pushKV("timestamp", wallet_descriptor.creation_time);
const bool active = active_spk_mans.count(desc_spk_man) != 0;
spk.pushKV("active", active);
const auto& type = wallet_descriptor.descriptor->GetOutputType();
- if (active && type != nullopt) {
+ if (active && type) {
spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man);
}
if (wallet_descriptor.descriptor->IsRange()) {
diff --git a/src/wallet/rpcsigner.cpp b/src/wallet/rpcsigner.cpp
new file mode 100644
index 0000000000..696c74d665
--- /dev/null
+++ b/src/wallet/rpcsigner.cpp
@@ -0,0 +1,108 @@
+// Copyright (c) 2018-2021 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 <chainparamsbase.h>
+#include <key_io.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <util/strencodings.h>
+#include <wallet/rpcsigner.h>
+#include <wallet/rpcwallet.h>
+#include <wallet/wallet.h>
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+static RPCHelpMan enumeratesigners()
+{
+ return RPCHelpMan{
+ "enumeratesigners",
+ "Returns a list of external signers from -signer.",
+ {},
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "signers", /* optional */ false, "",
+ {
+ {RPCResult::Type::STR_HEX, "masterkeyfingerprint", "Master key fingerprint"},
+ {RPCResult::Type::STR, "name", "Device name"},
+ },
+ }
+ }
+ },
+ RPCExamples{""},
+ [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") throw JSONRPCError(RPC_WALLET_ERROR, "Error: restart bitcoind with -signer=<cmd>");
+ std::string chain = gArgs.GetChainName();
+ UniValue signers_res = UniValue::VARR;
+ try {
+ std::vector<ExternalSigner> signers;
+ ExternalSigner::Enumerate(command, signers, chain);
+ for (ExternalSigner signer : signers) {
+ UniValue signer_res = UniValue::VOBJ;
+ signer_res.pushKV("fingerprint", signer.m_fingerprint);
+ signer_res.pushKV("name", signer.m_name);
+ signers_res.push_back(signer_res);
+ }
+ } catch (const ExternalSignerException& e) {
+ throw JSONRPCError(RPC_WALLET_ERROR, e.what());
+ }
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("signers", signers_res);
+ return result;
+ }
+ };
+}
+
+static RPCHelpMan signerdisplayaddress()
+{
+ return RPCHelpMan{
+ "signerdisplayaddress",
+ "Display address on an external signer for verification.\n",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"},
+ },
+ RPCResult{RPCResult::Type::NONE,"",""},
+ RPCExamples{""},
+ [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
+ LOCK(pwallet->cs_wallet);
+
+ CTxDestination dest = DecodeDestination(request.params[0].get_str());
+
+ // Make sure the destination is valid
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
+ }
+
+ if (!pwallet->DisplayAddress(dest)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Failed to display address");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("address", request.params[0].get_str());
+ return result;
+ }
+ };
+}
+
+Span<const CRPCCommand> GetSignerRPCCommands()
+{
+
+// clang-format off
+static const CRPCCommand commands[] =
+{ // category actor (function)
+ // --------------------- ------------------------
+ { "signer", &enumeratesigners, },
+ { "signer", &signerdisplayaddress, },
+};
+// clang-format on
+ return MakeSpan(commands);
+}
+
+
+#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/wallet/rpcsigner.h b/src/wallet/rpcsigner.h
new file mode 100644
index 0000000000..f3ab83c428
--- /dev/null
+++ b/src/wallet/rpcsigner.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2018-2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_RPCSIGNER_H
+#define BITCOIN_WALLET_RPCSIGNER_H
+
+#include <span.h>
+#include <util/system.h>
+#include <vector>
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+class CRPCCommand;
+
+namespace interfaces {
+class Chain;
+class Handler;
+}
+
+Span<const CRPCCommand> GetSignerRPCCommands();
+
+#endif // ENABLE_EXTERNAL_SIGNER
+
+#endif //BITCOIN_WALLET_RPCSIGNER_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 46de273d63..6dc8d1de42 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -8,7 +8,6 @@
#include <interfaces/chain.h>
#include <key_io.h>
#include <node/context.h>
-#include <optional.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
@@ -38,6 +37,7 @@
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
+#include <optional>
#include <stdint.h>
#include <univalue.h>
@@ -48,8 +48,8 @@ using interfaces::FoundBlock;
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
-static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
- bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+static inline bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
+ bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
if (avoid_reuse && !can_avoid_reuse) {
@@ -64,11 +64,11 @@ static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValu
* We default to true for watchonly wallets if include_watchonly isn't
* explicitly set.
*/
-static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet)
+static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
{
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);
+ return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
}
// otherwise return whatever include_watchonly was set to
@@ -96,7 +96,7 @@ bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string&
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
{
- CHECK_NONFATAL(!request.fHelp);
+ CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
@@ -117,9 +117,9 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
}
-void EnsureWalletIsUnlocked(const CWallet* pwallet)
+void EnsureWalletIsUnlocked(const CWallet& wallet)
{
- if (pwallet->IsLocked()) {
+ if (wallet.IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
}
}
@@ -219,7 +219,7 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un
cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN);
if (override_min_fee) cc.fOverrideFeeRate = true;
// Default RBF to true for explicit fee_rate, if unset.
- if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true;
+ if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true;
return;
}
if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
@@ -249,9 +249,8 @@ static RPCHelpMan getnewaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -299,9 +298,8 @@ static RPCHelpMan getrawchangeaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -342,9 +340,8 @@ static RPCHelpMan setlabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -396,9 +393,15 @@ void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_f
}
}
-UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
+UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
{
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(wallet);
+
+ // This function is only used by sendtoaddress and sendmany.
+ // This should always try to sign, if we don't have private keys, don't try to do anything here.
+ if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
+ }
// Shuffle recipient list
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
@@ -409,11 +412,11 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
bilingual_str error;
CTransactionRef tx;
FeeCalculation fee_calc_out;
- bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ const bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
- pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
if (verbose) {
UniValue entry(UniValue::VOBJ);
entry.pushKV("txid", tx->GetHash().GetHex());
@@ -474,9 +477,8 @@ static RPCHelpMan sendtoaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -501,13 +503,13 @@ static RPCHelpMan sendtoaddress()
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[8]);
+ coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[8]);
// We also enable partial spend avoidance if reuse avoidance is set.
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
UniValue address_amounts(UniValue::VOBJ);
const std::string address = request.params[0].get_str();
@@ -521,7 +523,7 @@ static RPCHelpMan sendtoaddress()
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
const bool verbose{request.params[10].isNull() ? false : request.params[10].get_bool()};
- return SendMoney(pwallet, coin_control, recipients, mapValue, verbose);
+ return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
},
};
}
@@ -553,9 +555,8 @@ static RPCHelpMan listaddressgroupings()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -611,20 +612,19 @@ static RPCHelpMan signmessage()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CTxDestination dest = DecodeDestination(strAddress);
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
const PKHash* pkhash = std::get_if<PKHash>(&dest);
@@ -714,9 +714,8 @@ static RPCHelpMan getreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -753,9 +752,8 @@ static RPCHelpMan getreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -794,9 +792,8 @@ static RPCHelpMan getbalance()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -816,7 +813,7 @@ static RPCHelpMan getbalance()
bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
- bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
+ bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
@@ -834,9 +831,8 @@ static RPCHelpMan getunconfirmedbalance()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -905,9 +901,8 @@ static RPCHelpMan sendmany()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -939,7 +934,7 @@ static RPCHelpMan sendmany()
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
const bool verbose{request.params[9].isNull() ? false : request.params[9].get_bool()};
- return SendMoney(pwallet, coin_control, recipients, std::move(mapValue), verbose);
+ return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
},
};
}
@@ -979,9 +974,8 @@ static RPCHelpMan addmultisigaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
@@ -1039,7 +1033,7 @@ struct tallyitem
}
};
-static UniValue ListReceived(const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -1053,7 +1047,7 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
isminefilter filter = ISMINE_SPENDABLE;
- if (ParseIncludeWatchonly(params[2], *pwallet)) {
+ if (ParseIncludeWatchonly(params[2], wallet)) {
filter |= ISMINE_WATCH_ONLY;
}
@@ -1069,10 +1063,10 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
// Tally
std::map<CTxDestination, tallyitem> mapTally;
- for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
+ for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !pwallet->chain().checkFinalTx(*wtx.tx)) {
+ if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx)) {
continue;
}
@@ -1090,7 +1084,7 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
continue;
}
- isminefilter mine = pwallet->IsMine(address);
+ isminefilter mine = wallet.IsMine(address);
if(!(mine & filter))
continue;
@@ -1109,11 +1103,11 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
// Create m_address_book iterator
// If we aren't filtering, go from begin() to end()
- auto start = pwallet->m_address_book.begin();
- auto end = pwallet->m_address_book.end();
+ auto start = wallet.m_address_book.begin();
+ auto end = wallet.m_address_book.end();
// If we are filtering, find() the applicable entry
if (has_filtered_address) {
- start = pwallet->m_address_book.find(filtered_address);
+ start = wallet.m_address_book.find(filtered_address);
if (start != end) {
end = std::next(start);
}
@@ -1221,9 +1215,8 @@ static RPCHelpMan listreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1231,7 +1224,7 @@ static RPCHelpMan listreceivedbyaddress()
LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, false);
+ return ListReceived(*pwallet, request.params, false);
},
};
}
@@ -1264,9 +1257,8 @@ static RPCHelpMan listreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1274,7 +1266,7 @@ static RPCHelpMan listreceivedbylabel()
LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, true);
+ return ListReceived(*pwallet, request.params, true);
},
};
}
@@ -1297,7 +1289,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1313,20 +1305,20 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& s : listSent)
{
UniValue entry(UniValue::VOBJ);
- if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
+ if (involvesWatchonly || (wallet.IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
- const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
if (address_book_entry) {
entry.pushKV("label", address_book_entry->GetLabel());
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(pwallet->chain(), wtx, entry);
+ WalletTxToJSON(wallet.chain(), wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
@@ -1337,7 +1329,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& r : listReceived)
{
std::string label;
- const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
+ const auto* address_book_entry = wallet.FindAddressBookEntry(r.destination);
if (address_book_entry) {
label = address_book_entry->GetLabel();
}
@@ -1345,7 +1337,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
continue;
}
UniValue entry(UniValue::VOBJ);
- if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
+ if (involvesWatchonly || (wallet.IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
}
MaybePushAddress(entry, r.destination);
@@ -1368,7 +1360,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(pwallet->chain(), wtx, entry);
+ WalletTxToJSON(wallet.chain(), wtx, entry);
ret.push_back(entry);
}
}
@@ -1445,9 +1437,8 @@ static RPCHelpMan listtransactions()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1488,7 +1479,7 @@ static RPCHelpMan listtransactions()
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(pwallet, *pwtx, 0, true, ret, filter, filter_label);
+ ListTransactions(*pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1573,8 +1564,8 @@ static RPCHelpMan listsinceblock()
LOCK(wallet.cs_wallet);
- Optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
- Optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
+ std::optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
+ std::optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
@@ -1610,7 +1601,7 @@ static RPCHelpMan listsinceblock()
const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
- ListTransactions(&wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1627,7 +1618,7 @@ static RPCHelpMan listsinceblock()
if (it != wallet.mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(&wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
+ ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
}
}
blockId = block.hashPrevBlock;
@@ -1704,9 +1695,8 @@ static RPCHelpMan gettransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1743,7 +1733,7 @@ static RPCHelpMan gettransaction()
WalletTxToJSON(pwallet->chain(), wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
+ ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
@@ -1778,9 +1768,8 @@ static RPCHelpMan abandontransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1817,9 +1806,8 @@ static RPCHelpMan backupwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1853,9 +1841,8 @@ static RPCHelpMan keypoolrefill()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
@@ -1871,7 +1858,7 @@ static RPCHelpMan keypoolrefill()
kpSize = (unsigned int)request.params[0].get_int();
}
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
pwallet->TopUpKeyPool(kpSize);
if (pwallet->GetKeyPoolSize() < kpSize) {
@@ -1995,9 +1982,8 @@ static RPCHelpMan walletpassphrasechange()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2049,9 +2035,8 @@ static RPCHelpMan walletlock()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2094,9 +2079,8 @@ static RPCHelpMan encryptwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2168,9 +2152,8 @@ static RPCHelpMan lockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2283,9 +2266,8 @@ static RPCHelpMan listlockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2324,9 +2306,8 @@ static RPCHelpMan settxfee()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2454,9 +2435,8 @@ static RPCHelpMan getwalletinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2610,7 +2590,7 @@ static RPCHelpMan loadwallet()
options.require_existing = true;
bilingual_str error;
std::vector<bilingual_str> warnings;
- Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
if (!wallet) {
// Map bad format to not found, since bad format is returned when the
@@ -2666,9 +2646,8 @@ static RPCHelpMan setwalletflag()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
std::string flag_str = request.params[0].get_str();
bool value = request.params[1].isNull() || request.params[1].get_bool();
@@ -2720,6 +2699,7 @@ static RPCHelpMan createwallet()
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
{"descriptors", RPCArg::Type::BOOL, /* default */ "false", "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
+ {"external_signer", RPCArg::Type::BOOL, /* default */ "false", "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2764,6 +2744,13 @@ static RPCHelpMan createwallet()
flags |= WALLET_FLAG_DESCRIPTORS;
warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
+ if (!request.params[7].isNull() && request.params[7].get_bool()) {
+#ifdef ENABLE_EXTERNAL_SIGNER
+ flags |= WALLET_FLAG_EXTERNAL_SIGNER;
+#else
+ throw JSONRPCError(RPC_WALLET_ERROR, "Configure with --enable-external-signer to use this");
+#endif
+ }
#ifndef USE_BDB
if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
@@ -2777,7 +2764,7 @@ static RPCHelpMan createwallet()
options.create_flags = flags;
options.create_passphrase = passphrase;
bilingual_str error;
- Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool());
+ std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
if (!wallet) {
RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
@@ -2829,7 +2816,7 @@ static RPCHelpMan unloadwallet()
// Note that any attempt to load the same wallet would fail until the wallet
// is destroyed (see CheckUniqueFileid).
std::vector<bilingual_str> warnings;
- Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
if (!RemoveWallet(wallet, load_on_start, warnings)) {
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
}
@@ -2902,9 +2889,8 @@ static RPCHelpMan listunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
int nMinDepth = 1;
if (!request.params[0].isNull()) {
@@ -3066,11 +3052,11 @@ static RPCHelpMan listunspent()
};
}
-void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
+void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
{
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
- pwallet->BlockUntilSyncedToCurrentChain();
+ wallet.BlockUntilSyncedToCurrentChain();
change_position = -1;
bool lockUnspents = false;
@@ -3141,7 +3127,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, *pwallet);
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
@@ -3167,11 +3153,11 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- SetFeeEstimateMode(*pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
+ SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
}
} else {
// if options is null and not a bool
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet);
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
}
if (tx.vout.size() == 0)
@@ -3193,7 +3179,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
bilingual_str error;
- if (!pwallet->FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
+ if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
}
@@ -3269,9 +3255,8 @@ static RPCHelpMan fundrawtransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
@@ -3288,7 +3273,7 @@ static RPCHelpMan fundrawtransaction()
CCoinControl coin_control;
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
coin_control.m_add_inputs = true;
- FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
+ FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
@@ -3355,9 +3340,8 @@ RPCHelpMan signrawtransactionwithwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
@@ -3368,7 +3352,7 @@ RPCHelpMan signrawtransactionwithwallet()
// Sign the transaction
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
// Fetch previous transactions (inputs):
std::map<COutPoint, Coin> coins;
@@ -3455,9 +3439,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
},
[want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) mutable -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
@@ -3500,7 +3483,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
pwallet->BlockUntilSyncedToCurrentChain();
LOCK(pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
+
+ EnsureWalletIsUnlocked(*pwallet);
std::vector<bilingual_str> errors;
@@ -3594,9 +3578,8 @@ static RPCHelpMan rescanblockchain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) {
@@ -3604,7 +3587,7 @@ static RPCHelpMan rescanblockchain()
}
int start_height = 0;
- Optional<int> stop_height;
+ std::optional<int> stop_height;
uint256 start_block;
{
LOCK(pwallet->cs_wallet);
@@ -3745,15 +3728,13 @@ public:
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
-static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDestination& dest)
+static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
CScript script = GetScriptForDestination(dest);
std::unique_ptr<SigningProvider> provider = nullptr;
- if (pwallet) {
- provider = pwallet->GetSolvingProvider(script);
- }
+ provider = wallet.GetSolvingProvider(script);
ret.pushKVs(detail);
ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
return ret;
@@ -3787,6 +3768,7 @@ RPCHelpMan getaddressinfo()
{RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
{RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
{RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."},
+ {RPCResult::Type::STR, "parent_desc", /* optional */ true, "The descriptor used to derive this address if this is a descriptor wallet"},
{RPCResult::Type::BOOL, "isscript", "If the key is a script."},
{RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
{RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
@@ -3825,9 +3807,8 @@ RPCHelpMan getaddressinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -3862,9 +3843,17 @@ RPCHelpMan getaddressinfo()
ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
}
+ DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey));
+ if (desc_spk_man) {
+ std::string desc_str;
+ if (desc_spk_man->GetDescriptorString(desc_str, false)) {
+ ret.pushKV("parent_desc", desc_str);
+ }
+ }
+
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
- UniValue detail = DescribeWalletAddress(pwallet, dest);
+ UniValue detail = DescribeWalletAddress(*pwallet, dest);
ret.pushKVs(detail);
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3920,9 +3909,8 @@ static RPCHelpMan getaddressesbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -3982,9 +3970,8 @@ static RPCHelpMan listlabels()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4107,9 +4094,8 @@ static RPCHelpMan send()
}, true
);
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
if (options.exists("conf_target") || options.exists("estimate_mode")) {
@@ -4162,7 +4148,7 @@ static RPCHelpMan send()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
+ FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
bool add_to_wallet = true;
if (options.exists("add_to_wallet")) {
@@ -4172,8 +4158,10 @@ static RPCHelpMan send()
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
- // Fill transaction with our data and sign
- bool complete = true;
+ // First fill transaction with our data without signing,
+ // so external signers are not asked sign more than once.
+ bool complete;
+ pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
@@ -4233,9 +4221,8 @@ static RPCHelpMan sethdseed()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -4250,7 +4237,7 @@ static RPCHelpMan sethdseed()
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
}
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
bool flush_key_pool = true;
if (!request.params[0].isNull()) {
@@ -4311,9 +4298,8 @@ static RPCHelpMan walletprocesspsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- const CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
@@ -4423,9 +4409,8 @@ static RPCHelpMan walletcreatefundedpsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -4449,7 +4434,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
+ FundTransaction(*pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
@@ -4499,13 +4484,12 @@ static RPCHelpMan upgradewallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
- CWallet* const pwallet = wallet.get();
+ std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
- EnsureWalletIsUnlocked(pwallet);
+ EnsureWalletIsUnlocked(*pwallet);
int version = 0;
if (!request.params[0].isNull()) {
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 184a16e91d..b82fe1ec76 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -30,7 +30,7 @@ Span<const CRPCCommand> GetWalletRPCCommands();
*/
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
-void EnsureWalletIsUnlocked(const CWallet*);
+void EnsureWalletIsUnlocked(const CWallet&);
WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index 09a9ec68cd..6d912be019 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -119,7 +119,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
return false;
}
- std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
+ std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
filename.c_str(), // Filename
"main", // Logical db name
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 15972fe7bb..54319ca662 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -13,8 +13,11 @@
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
+#include <wallet/external_signer.h>
#include <wallet/scriptpubkeyman.h>
+#include <optional>
+
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
@@ -94,8 +97,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
TxoutType whichType = Solver(scriptPubKey, vSolutions);
CKeyID keyID;
- switch (whichType)
- {
+ switch (whichType) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
@@ -194,7 +196,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
}
break;
}
- }
+ } // no default case, so the compiler can warn about missing cases
if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
ret = std::max(ret, IsMineResult::WATCH_ONLY);
@@ -378,7 +380,7 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
return;
}
- std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(m_storage.GetDatabase());
+ std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_storage.GetDatabase());
for (auto& meta_pair : mapKeyMetadata) {
CKeyMetadata& meta = meta_pair.second;
if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
@@ -551,7 +553,7 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const
{
- return MakeUnique<LegacySigningProvider>(*this);
+ return std::make_unique<LegacySigningProvider>(*this);
}
bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata)
@@ -651,14 +653,14 @@ std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestin
if (!key_id.IsNull()) {
auto it = mapKeyMetadata.find(key_id);
if (it != mapKeyMetadata.end()) {
- return MakeUnique<CKeyMetadata>(it->second);
+ return std::make_unique<CKeyMetadata>(it->second);
}
}
CScript scriptPubKey = GetScriptForDestination(dest);
auto it = m_script_metadata.find(CScriptID(scriptPubKey));
if (it != m_script_metadata.end()) {
- return MakeUnique<CKeyMetadata>(it->second);
+ return std::make_unique<CKeyMetadata>(it->second);
}
return nullptr;
@@ -1607,7 +1609,7 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest
{
LOCK(cs_desc_man);
assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor
- Optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType();
+ std::optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType();
assert(desc_addr_type);
if (type != *desc_addr_type) {
throw std::runtime_error(std::string(__func__) + ": Types are inconsistent");
@@ -1629,7 +1631,7 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest
return false;
}
- Optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType();
+ std::optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType();
if (out_script_type && out_script_type == type) {
ExtractDestination(scripts_temp[0], dest);
} else {
@@ -2026,7 +2028,7 @@ std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvid
{
AssertLockHeld(cs_desc_man);
// Get the scripts, keys, and key origins for this script
- std::unique_ptr<FlatSigningProvider> out_keys = MakeUnique<FlatSigningProvider>();
+ std::unique_ptr<FlatSigningProvider> out_keys = std::make_unique<FlatSigningProvider>();
std::vector<CScript> scripts_temp;
if (!m_wallet_descriptor.descriptor->ExpandFromCache(index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) return nullptr;
@@ -2051,7 +2053,7 @@ bool DescriptorScriptPubKeyMan::CanProvide(const CScript& script, SignatureData&
bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
{
- std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>();
+ std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>();
for (const auto& coin_pair : coins) {
std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(coin_pair.second.out.scriptPubKey, true);
if (!coin_keys) {
@@ -2115,13 +2117,13 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
SignatureData sigdata;
input.FillSignatureData(sigdata);
- std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>();
+ std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>();
std::unique_ptr<FlatSigningProvider> script_keys = GetSigningProvider(script, sign);
if (script_keys) {
*keys = Merge(*keys, *script_keys);
} else {
// Maybe there are pubkeys listed that we can sign for
- script_keys = MakeUnique<FlatSigningProvider>();
+ script_keys = std::make_unique<FlatSigningProvider>();
for (const auto& pk_pair : input.hd_keypaths) {
const CPubKey& pubkey = pk_pair.first;
std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey);
@@ -2162,7 +2164,7 @@ std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDe
CKeyID key_id = GetKeyForDestination(*provider, dest);
if (provider->GetKeyOrigin(key_id, orig)) {
LOCK(cs_desc_man);
- std::unique_ptr<CKeyMetadata> meta = MakeUnique<CKeyMetadata>();
+ std::unique_ptr<CKeyMetadata> meta = std::make_unique<CKeyMetadata>();
meta->key_origin = orig;
meta->has_key_origin = true;
meta->nCreateTime = m_wallet_descriptor.creation_time;
@@ -2265,3 +2267,16 @@ const std::vector<CScript> DescriptorScriptPubKeyMan::GetScriptPubKeys() const
}
return script_pub_keys;
}
+
+bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, bool priv) const
+{
+ LOCK(cs_desc_man);
+ if (m_storage.IsLocked()) {
+ return false;
+ }
+
+ FlatSigningProvider provider;
+ provider.keys = GetKeys();
+
+ return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, priv);
+}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 8f6b69bc78..b8e34fbac3 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -517,8 +517,6 @@ public:
class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
{
private:
- WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man);
-
using ScriptPubKeyMap = std::map<CScript, int32_t>; // Map of scripts to descriptor range index
using PubKeyMap = std::map<CPubKey, int32_t>; // Map of pubkeys involved in scripts to descriptor range index
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
@@ -547,6 +545,9 @@ private:
// Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions.
std::unique_ptr<FlatSigningProvider> GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
+protected:
+ WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man);
+
public:
DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
: ScriptPubKeyMan(storage),
@@ -581,6 +582,11 @@ public:
//! Setup descriptors based on the given CExtkey
bool SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type);
+ /** Provide a descriptor at setup time
+ * Returns false if already setup or setup fails, true if setup is successful
+ */
+ bool SetupDescriptor(std::unique_ptr<Descriptor>desc);
+
bool HavePrivateKeys() const override;
int64_t GetOldestKeyPoolTime() const override;
@@ -616,6 +622,8 @@ public:
const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
const std::vector<CScript> GetScriptPubKeys() const;
+
+ bool GetDescriptorString(std::string& out, bool priv) const;
};
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 0fb3b1d3c4..975974cb6a 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -8,7 +8,6 @@
#include <crypto/common.h>
#include <logging.h>
#include <sync.h>
-#include <util/memory.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
@@ -330,7 +329,7 @@ void SQLiteDatabase::Close()
std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close)
{
// We ignore flush_on_close because we don't do manual flushing for SQLite
- return MakeUnique<SQLiteBatch>(*this);
+ return std::make_unique<SQLiteBatch>(*this);
}
SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)
@@ -571,7 +570,7 @@ std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const D
{
try {
fs::path data_file = SQLiteDataFile(path);
- auto db = MakeUnique<SQLiteDatabase>(data_file.parent_path(), data_file);
+ auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file);
if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index ffac78d752..7eff6e592d 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -35,7 +35,10 @@ static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
CoinEligibilityFilter filter_confirmed(1, 1, 0);
CoinEligibilityFilter filter_standard_extra(6, 6, 0);
-CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0, false);
+CoinSelectionParams coin_selection_params(/* use_bnb= */ false, /* change_output_size= */ 0,
+ /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
@@ -269,7 +272,10 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
// Make sure that effective value is working in SelectCoinsMinConf when BnB is used
- CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0, false);
+ CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0,
+ /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
+ /* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
+ /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
CoinSet setCoinsRet;
CAmount nValueRet;
bool bnb_used;
@@ -290,7 +296,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that can use BnB when there are preset inputs
empty_wallet();
{
- std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
wallet->SetupLegacyScriptPubKeyMan();
@@ -301,7 +307,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
- coin_selection_params_bnb.effective_fee = CFeeRate(0);
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
BOOST_CHECK(bnb_used);
BOOST_CHECK(coin_selection_params_bnb.use_bnb);
@@ -639,8 +645,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CAmount target = rand.randrange(balance - 1000) + 1000;
// Perform selection
- CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0, false);
- CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0, false);
+ CoinSelectionParams coin_selection_params_knapsack(/* use_bnb= */ false, /* change_output_size= */ 34,
+ /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 34,
+ /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
CoinSet out_set;
CAmount out_value = 0;
bool bnb_used = false;
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index 27179839b7..b2eb8e4bca 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -30,8 +30,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
- BOOST_CHECK(filename == test_name);
- BOOST_CHECK(env->Directory() == datadir);
+ BOOST_CHECK_EQUAL(filename, test_name);
+ BOOST_CHECK_EQUAL(env->Directory(), datadir);
}
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
@@ -41,8 +41,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
- BOOST_CHECK(filename == expected_name);
- BOOST_CHECK(env->Directory() == datadir);
+ BOOST_CHECK_EQUAL(filename, expected_name);
+ BOOST_CHECK_EQUAL(env->Directory(), datadir);
}
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index e70b56c529..45e1b8c4b8 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
@@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 5480f3ab22..30cc452065 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -228,7 +228,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- RemoveWallet(wallet, nullopt);
+ RemoveWallet(wallet, std::nullopt);
}
}
@@ -271,7 +271,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
::dumpwallet().HandleRequest(request);
- RemoveWallet(wallet, nullopt);
+ RemoveWallet(wallet, std::nullopt);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
@@ -288,7 +288,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
- RemoveWallet(wallet, nullopt);
+ RemoveWallet(wallet, std::nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
@@ -485,7 +485,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index e91a6d40b9..b00fa851fd 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -13,11 +13,12 @@
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
-#include <optional.h>
+#include <outputtype.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
+#include <psbt.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/signingprovider.h>
@@ -32,11 +33,13 @@
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <wallet/external_signer_scriptpubkeyman.h>
#include <univalue.h>
#include <algorithm>
#include <assert.h>
+#include <optional>
#include <boost/algorithm/string/replace.hpp>
@@ -81,10 +84,10 @@ bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_nam
static void UpdateWalletSetting(interfaces::Chain& chain,
const std::string& wallet_name,
- Optional<bool> load_on_startup,
+ std::optional<bool> load_on_startup,
std::vector<bilingual_str>& warnings)
{
- if (load_on_startup == nullopt) return;
+ if (!load_on_startup) return;
if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) {
warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
} else if (!load_on_startup.value() && !RemoveWalletSetting(chain, wallet_name)) {
@@ -104,7 +107,7 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
{
assert(wallet);
@@ -124,7 +127,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start)
{
std::vector<bilingual_str> warnings;
return RemoveWallet(wallet, load_on_start, warnings);
@@ -201,7 +204,7 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
-std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
@@ -231,7 +234,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std:
}
} // namespace
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) {
@@ -244,7 +247,7 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
return wallet;
}
-std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
uint64_t wallet_creation_flags = options.create_flags;
const SecureString& passphrase = options.create_passphrase;
@@ -259,6 +262,20 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
}
+ // Private keys must be disabled for an external signer wallet
+ if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ error = Untranslated("Private keys must be disabled when using an external signer");
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
+ }
+
+ // Descriptor support must be enabled for an external signer wallet
+ if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS)) {
+ error = Untranslated("Descriptor support must be enabled when using an external signer");
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
+ }
+
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
@@ -774,6 +791,12 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
+ // Refresh mempool status without waiting for transactionRemovedFromMempool
+ // notification so the wallet is in an internally consistent state and
+ // immediately knows the old transaction should not be considered trusted
+ // and is eligible to be abandoned
+ wtx.fInMempool = chain().isInMempool(originalHash);
+
WalletBatch batch(GetDatabase());
bool success = true;
@@ -921,6 +944,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", hash.GetHex());
+ if (confirm.status == CWalletTx::Status::CONFIRMED)
+ {
+ boost::replace_all(strCmd, "%b", confirm.hashBlock.GetHex());
+ boost::replace_all(strCmd, "%h", ToString(confirm.block_height));
+ } else {
+ boost::replace_all(strCmd, "%b", "unconfirmed");
+ boost::replace_all(strCmd, "%h", "-1");
+ }
#ifndef WIN32
// Substituting the wallet name isn't currently supported on windows
// because windows shell escaping has not been implemented yet:
@@ -1594,14 +1625,15 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
return true;
}
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
+// Returns pair of vsize and weight
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
for (const CTxIn& input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.hash);
// Can not estimate size without knowing the input details
if (mi == wallet->mapWallet.end()) {
- return -1;
+ return std::make_pair(-1, -1);
}
assert(input.prevout.n < mi->second.tx->vout.size());
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
@@ -1610,13 +1642,16 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
}
// txouts needs to be in the order of tx.vin
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
{
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
- return -1;
+ return std::make_pair(-1, -1);
}
- return GetVirtualTransactionSize(CTransaction(txNew));
+ CTransaction ctx(txNew);
+ int64_t vsize = GetVirtualTransactionSize(ctx);
+ int64_t weight = GetTransactionWeight(ctx);
+ return std::make_pair(vsize, weight);
}
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
@@ -1736,7 +1771,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* the main chain after to the addition of any new keys you want to detect
* transactions for.
*/
-CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
+CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
{
int64_t nNow = GetTime();
int64_t start_time = GetTimeMillis();
@@ -2364,26 +2399,20 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
nValueRet = 0;
if (coin_selection_params.use_bnb) {
- // Get long term estimate
- FeeCalculation feeCalc;
- CCoinControl temp;
- temp.m_confirm_target = 1008;
- CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
-
// Get the feerate for effective value.
// When subtracting the fee from the outputs, we want the effective feerate to be 0
CFeeRate effective_feerate{0};
if (!coin_selection_params.m_subtract_fee_outputs) {
- effective_feerate = coin_selection_params.effective_fee;
+ effective_feerate = coin_selection_params.m_effective_feerate;
}
- std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, long_term_feerate, eligibility_filter, true /* positive_only */);
+ std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */);
// Calculate cost of change
- CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
+ CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
// Calculate the fees for things that aren't inputs
- CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
+ CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
bnb_used = true;
return SelectCoinsBnB(groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
} else {
@@ -2437,7 +2466,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (coin.m_input_bytes <= 0) {
return false; // Not solvable, can't estimate size for fee
}
- coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
if (coin_selection_params.use_bnb) {
value_to_select -= coin.effective_value;
} else {
@@ -2698,7 +2727,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
return locktime;
}
-OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const
+OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const
{
// If -changetype is specified, always use that change type.
if (change_type) {
@@ -2762,6 +2791,7 @@ bool CWallet::CreateTransactionInternal(
CMutableTransaction txNew;
FeeCalculation feeCalc;
CAmount nFeeNeeded;
+ std::pair<int64_t, int64_t> tx_sizes;
int nBytes;
{
std::set<CInputCoin> setCoins;
@@ -2804,17 +2834,28 @@ bool CWallet::CreateTransactionInternal(
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
- CFeeRate discard_rate = GetDiscardRate(*this);
+ // Set discard feerate
+ coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
- CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
// provided one
- if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
- error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), nFeeRateNeeded.ToString(FeeEstimateMode::SAT_VB));
+ if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
+ return false;
+ }
+ if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
}
+ // Get long term estimate
+ CCoinControl cc_temp;
+ cc_temp.m_confirm_target = chain().estimateMaxBlocks();
+ coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
+
nFeeRet = 0;
bool pick_new_inputs = true;
CAmount nValueIn = 0;
@@ -2888,7 +2929,6 @@ bool CWallet::CreateTransactionInternal(
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
- coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
// If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack.
@@ -2914,7 +2954,7 @@ bool CWallet::CreateTransactionInternal(
// Never create dust outputs; if we would, just
// add the dust to the fee.
// The nChange when BnB is used is always going to go to fees.
- if (IsDust(newTxOut, discard_rate) || bnb_used)
+ if (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used)
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2945,19 +2985,14 @@ bool CWallet::CreateTransactionInternal(
txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
}
- nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.first;
if (nBytes < 0) {
error = _("Signing transaction failed");
return false;
}
- nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
- if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
- // eventually allow a fallback fee
- error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
- }
-
+ nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes);
if (nFeeRet >= nFeeNeeded) {
// Reduce fee to only the needed amount if possible. This
// prevents potential overpayment in fees if the coins
@@ -2971,8 +3006,8 @@ bool CWallet::CreateTransactionInternal(
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
- CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
- CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
+ CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change);
+ CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
nFeeRet = fee_needed_with_change;
@@ -3055,7 +3090,8 @@ bool CWallet::CreateTransactionInternal(
tx = MakeTransactionRef(std::move(txNew));
// Limit size
- if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT))
{
error = _("Transaction too large");
return false;
@@ -3558,6 +3594,38 @@ void ReserveDestination::ReturnDestination()
address = CNoDestination();
}
+#ifdef ENABLE_EXTERNAL_SIGNER
+ExternalSigner CWallet::GetExternalSigner()
+{
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>");
+ std::vector<ExternalSigner> signers;
+ ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+ if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found");
+ // TODO: add fingerprint argument in case of multiple signers
+ return signers[0];
+}
+#endif
+
+bool CWallet::DisplayAddress(const CTxDestination& dest)
+{
+#ifdef ENABLE_EXTERNAL_SIGNER
+ CScript scriptPubKey = GetScriptForDestination(dest);
+ const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
+ if (spk_man == nullptr) {
+ return false;
+ }
+ auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man);
+ if (signer_spk_man == nullptr) {
+ return false;
+ }
+ ExternalSigner signer = GetExternalSigner(); // TODO: move signer in spk_man
+ return signer_spk_man->DisplayAddress(scriptPubKey, signer);
+#else
+ return false;
+#endif
+}
+
void CWallet::LockCoin(const COutPoint& output)
{
AssertLockHeld(cs_wallet);
@@ -3836,7 +3904,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
walletInstance->SetupLegacyScriptPubKeyMan();
}
- if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
+ if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
LOCK(walletInstance->cs_wallet);
if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
walletInstance->SetupDescriptorScriptPubKeyMans();
@@ -4005,13 +4073,13 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
if (batch.ReadBestBlock(locator)) {
- if (const Optional<int> fork_height = chain.findLocatorFork(locator)) {
+ if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {
rescan_height = *fork_height;
}
}
}
- const Optional<int> tip_height = chain.getHeight();
+ const std::optional<int> tip_height = chain.getHeight();
if (tip_height) {
walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
walletInstance->m_last_block_processed_height = *tip_height;
@@ -4045,7 +4113,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
- Optional<int64_t> time_first_key;
+ std::optional<int64_t> time_first_key;
for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
int64_t time = spk_man->GetTimeFirstKey();
if (!time_first_key || time < *time_first_key) time_first_key = time;
@@ -4443,40 +4511,82 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
{
- auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
- m_spk_managers[id] = std::move(spk_manager);
+ if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
+#ifdef ENABLE_EXTERNAL_SIGNER
+ auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
+ m_spk_managers[id] = std::move(spk_manager);
+#else
+ throw std::runtime_error(std::string(__func__) + ": Configure with --enable-external-signer to use external signer wallets");
+#endif
+ } else {
+ auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
+ m_spk_managers[id] = std::move(spk_manager);
+ }
}
void CWallet::SetupDescriptorScriptPubKeyMans()
{
AssertLockHeld(cs_wallet);
- // Make a seed
- CKey seed_key;
- seed_key.MakeNewKey(true);
- CPubKey seed = seed_key.GetPubKey();
- assert(seed_key.VerifyPubKey(seed));
+ if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
+ // Make a seed
+ CKey seed_key;
+ seed_key.MakeNewKey(true);
+ CPubKey seed = seed_key.GetPubKey();
+ assert(seed_key.VerifyPubKey(seed));
- // Get the extended key
- CExtKey master_key;
- master_key.SetSeed(seed_key.begin(), seed_key.size());
+ // Get the extended key
+ CExtKey master_key;
+ master_key.SetSeed(seed_key.begin(), seed_key.size());
- for (bool internal : {false, true}) {
- for (OutputType t : OUTPUT_TYPES) {
- auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, internal));
- if (IsCrypted()) {
- if (IsLocked()) {
- throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
+ for (bool internal : {false, true}) {
+ for (OutputType t : OUTPUT_TYPES) {
+ auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, internal));
+ if (IsCrypted()) {
+ if (IsLocked()) {
+ throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
+ }
+ if (!spk_manager->CheckDecryptionKey(vMasterKey) && !spk_manager->Encrypt(vMasterKey, nullptr)) {
+ throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
+ }
}
- if (!spk_manager->CheckDecryptionKey(vMasterKey) && !spk_manager->Encrypt(vMasterKey, nullptr)) {
- throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
+ spk_manager->SetupDescriptorGeneration(master_key, t);
+ uint256 id = spk_manager->GetID();
+ m_spk_managers[id] = std::move(spk_manager);
+ AddActiveScriptPubKeyMan(id, t, internal);
+ }
+ }
+ } else {
+#ifdef ENABLE_EXTERNAL_SIGNER
+ ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
+
+ // TODO: add account parameter
+ int account = 0;
+ UniValue signer_res = signer.GetDescriptors(account);
+
+ if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
+ for (bool internal : {false, true}) {
+ const UniValue& descriptor_vals = find_value(signer_res, internal ? "internal" : "receive");
+ if (!descriptor_vals.isArray()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
+ for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) {
+ std::string desc_str = desc_val.getValStr();
+ FlatSigningProvider keys;
+ std::string dummy_error;
+ std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, dummy_error, false);
+ if (!desc->GetOutputType()) {
+ continue;
}
+ OutputType t = *desc->GetOutputType();
+ auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, internal));
+ spk_manager->SetupDescriptor(std::move(desc));
+ uint256 id = spk_manager->GetID();
+ m_spk_managers[id] = std::move(spk_manager);
+ AddActiveScriptPubKeyMan(id, t, internal);
}
- spk_manager->SetupDescriptorGeneration(master_key, t);
- uint256 id = spk_manager->GetID();
- m_spk_managers[id] = std::move(spk_manager);
- AddActiveScriptPubKeyMan(id, t, internal);
}
+#else
+ throw std::runtime_error(std::string(__func__) + ": Wallets with external signers require Boost::Process library.");
+#endif
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4fc4466604..6031d36b97 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -22,6 +22,7 @@
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
#include <wallet/scriptpubkeyman.h>
+#include <wallet/external_signer.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -29,6 +30,7 @@
#include <atomic>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <stdexcept>
#include <stdint.h>
@@ -50,12 +52,12 @@ struct bilingual_str;
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
-std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
@@ -95,7 +97,6 @@ constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
constexpr CAmount HIGH_TX_FEE_PER_KB{COIN / 100};
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
constexpr CAmount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB};
-
//! Pre-calculated constants for input size estimation in *virtual size*
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
@@ -115,7 +116,8 @@ static constexpr uint64_t KNOWN_WALLET_FLAGS =
| WALLET_FLAG_BLANK_WALLET
| WALLET_FLAG_KEY_ORIGIN_METADATA
| WALLET_FLAG_DISABLE_PRIVATE_KEYS
- | WALLET_FLAG_DESCRIPTORS;
+ | WALLET_FLAG_DESCRIPTORS
+ | WALLET_FLAG_EXTERNAL_SIGNER;
static constexpr uint64_t MUTABLE_WALLET_FLAGS =
WALLET_FLAG_AVOID_REUSE;
@@ -126,6 +128,7 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
{"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA},
{"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS},
{"descriptor_wallet", WALLET_FLAG_DESCRIPTORS},
+ {"external_signer", WALLET_FLAG_EXTERNAL_SIGNER}
};
extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
@@ -606,17 +609,22 @@ struct CoinSelectionParams
bool use_bnb = true;
size_t change_output_size = 0;
size_t change_spend_size = 0;
- CFeeRate effective_fee = CFeeRate(0);
+ CFeeRate m_effective_feerate;
+ CFeeRate m_long_term_feerate;
+ CFeeRate m_discard_feerate;
size_t tx_noinputs_size = 0;
//! Indicate that we are subtracting the fee from outputs
bool m_subtract_fee_outputs = false;
bool m_avoid_partial_spends = false;
- CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size, bool avoid_partial) :
+ CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate,
+ CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial) :
use_bnb(use_bnb),
change_output_size(change_output_size),
change_spend_size(change_spend_size),
- effective_fee(effective_fee),
+ m_effective_feerate(effective_feerate),
+ m_long_term_feerate(long_term_feerate),
+ m_discard_feerate(discard_feerate),
tx_noinputs_size(tx_noinputs_size),
m_avoid_partial_spends(avoid_partial)
{}
@@ -837,6 +845,12 @@ public:
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const;
+#ifdef ENABLE_EXTERNAL_SIGNER
+ ExternalSigner GetExternalSigner() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+#endif
+ /** Display address on an external signer. Returns false if external signer support is not compiled */
+ bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -915,7 +929,7 @@ public:
//! Unset if no blocks were scanned due to read errors or the chain
//! being empty.
uint256 last_scanned_block;
- Optional<int> last_scanned_height;
+ std::optional<int> last_scanned_height;
//! Height of the most recent block that could not be scanned due to
//! read errors or pruning. Will be set if status is FAILURE, unset if
@@ -923,7 +937,7 @@ public:
//! USER_ABORT.
uint256 last_failed_block;
};
- ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
+ ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
@@ -938,7 +952,7 @@ public:
Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
- OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
+ OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
/**
* Insert additional inputs into the transaction by
@@ -1023,7 +1037,7 @@ public:
* (see -changetype option documentation and implementation in
* CWallet::TransactionChangeType for details).
*/
- Optional<OutputType> m_default_change_type{};
+ std::optional<OutputType> m_default_change_type{};
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
@@ -1326,8 +1340,8 @@ public:
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
//! Add wallet name to persistent configuration so it will be loaded on startup.
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4e6270220e..3d9248009f 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -23,6 +23,7 @@
#include <wallet/wallet.h>
#include <atomic>
+#include <optional>
#include <string>
namespace DBKeys {
@@ -551,13 +552,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CHDChain chain;
ssValue >> chain;
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
- } else if (strType == DBKeys::FLAGS) {
- uint64_t flags;
- ssValue >> flags;
- if (!pwallet->LoadWalletFlags(flags)) {
- 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;
@@ -662,7 +656,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fIsEncrypted = true;
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
- strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
+ strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
+ strType != DBKeys::FLAGS) {
wss.m_unknown_records++;
}
} catch (const std::exception& e) {
@@ -707,6 +702,16 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
pwallet->LoadMinVersion(nMinVersion);
}
+ // Load wallet flags, so they are known when processing other records.
+ // The FLAGS key is absent during wallet creation.
+ uint64_t flags;
+ if (m_batch->Read(DBKeys::FLAGS, flags)) {
+ if (!pwallet->LoadWalletFlags(flags)) {
+ pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
+ return DBErrors::CORRUPT;
+ }
+ }
+
// Get cursor
if (!m_batch->StartCursor())
{
@@ -1011,7 +1016,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
return nullptr;
}
- Optional<DatabaseFormat> format;
+ std::optional<DatabaseFormat> format;
if (exists) {
if (IsBDBFile(BDBDataFile(path))) {
format = DatabaseFormat::BERKELEY;
@@ -1082,15 +1087,15 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
{
- return MakeUnique<DummyDatabase>();
+ return std::make_unique<DummyDatabase>();
}
/** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{
#ifdef USE_BDB
- return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
+ return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
#elif USE_SQLITE
- return MakeUnique<SQLiteDatabase>("", "", true);
+ return std::make_unique<SQLiteDatabase>("", "", true);
#endif
}
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index bc90491a2c..b2cb0bf479 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -103,10 +103,8 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
-bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& name)
+bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
{
- const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name);
-
if (args.IsArgSet("-format") && command != "createfromdump") {
tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n");
return false;
@@ -119,6 +117,12 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command,
tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
return false;
}
+ if (command == "create" && !args.IsArgSet("-wallet")) {
+ tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
+ return false;
+ }
+ const std::string name = args.GetArg("-wallet", "");
+ const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name);
if (command == "create") {
DatabaseOptions options;
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index f544a6f727..f4516bb5bc 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -10,7 +10,7 @@
namespace WalletTool {
void WalletShowInfo(CWallet* wallet_instance);
-bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& file);
+bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command);
} // namespace WalletTool
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 67b2ee2b98..0713f768c1 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -60,6 +60,9 @@ enum WalletFlags : uint64_t {
//! Indicate that this wallet supports DescriptorScriptPubKeyMan
WALLET_FLAG_DESCRIPTORS = (1ULL << 34),
+
+ //! Indicates that the wallet needs an external signer
+ WALLET_FLAG_EXTERNAL_SIGNER = (1ULL << 35),
};
//! Get the path of the wallet directory.
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 6f0b202a18..49c1c2a07d 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
-#include <util/memory.h>
#include <memory>
#include <string>
@@ -27,7 +26,7 @@ public:
template <typename T>
static std::unique_ptr<CZMQAbstractNotifier> Create()
{
- return MakeUnique<T>();
+ return std::make_unique<T>();
}
std::string GetType() const { return type; }
diff --git a/test/config.ini.in b/test/config.ini.in
index 77c9a720c3..e3872181cd 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -23,3 +23,4 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true
@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true
+@ENABLE_EXTERNAL_SIGNER_TRUE@ENABLE_EXTERNAL_SIGNER=true
diff --git a/test/functional/README.md b/test/functional/README.md
index 2d04413eb2..d830ba0334 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -63,10 +63,13 @@ don't have test cases for.
- Avoid stop-starting the nodes multiple times during the test if possible. A
stop-start takes several seconds, so doing it several times blows up the
runtime of the test.
-- Set the `self.setup_clean_chain` variable in `set_test_params()` to control whether
- or not to use the cached data directories. The cached data directories
- contain a 200-block pre-mined blockchain and wallets for four nodes. Each node
- has 25 mature blocks (25x50=1250 BTC) in its wallet.
+- Set the `self.setup_clean_chain` variable in `set_test_params()` to `True` to
+ initialize an empty blockchain and start from the Genesis block, rather than
+ load a premined blockchain from cache with the default value of `False`. The
+ cached data directories contain a 200-block pre-mined blockchain with the
+ spendable mining rewards being split between four nodes. Each node has 25
+ mature block subsidies (25x50=1250 BTC) in its wallet. Using them is much more
+ efficient than mining blocks in your test.
- When calling RPCs with lots of arguments, consider using named keyword
arguments instead of positional arguments to make the intent of the call
clear to readers.
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index 6e72db1d96..fab921ef19 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -57,7 +57,7 @@ class BadTxTemplate:
__metaclass__ = abc.ABCMeta
# The expected error code given by bitcoind upon submission of the tx.
- reject_reason = "" # type: Optional[str]
+ reject_reason: Optional[str] = ""
# Only specified if it differs from mempool acceptance error.
block_reject_reason = ""
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 97f24e1b6e..a0eb213a78 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -76,6 +76,9 @@ class ExampleTest(BitcoinTestFramework):
"""Override test parameters for your individual test.
This method must be overridden and num_nodes must be explicitly set."""
+ # By default every test loads a pre-mined chain of 200 blocks from cache.
+ # Set setup_clean_chain to True to skip this and start from the Genesis
+ # block.
self.setup_clean_chain = True
self.num_nodes = 3
# Use self.extra_args to change command-line arguments for the nodes
diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py
index 2c6553fbe2..5fcecb4882 100755
--- a/test/functional/feature_asmap.py
+++ b/test/functional/feature_asmap.py
@@ -36,7 +36,6 @@ def expected_messages(filename):
class AsmapTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def test_without_asmap_arg(self):
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 603d7f5d3b..1a148f04f4 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -29,9 +29,11 @@ Start three nodes:
block 200. node2 will reject block 102 since it's assumed valid, but it
isn't buried by at least two weeks' work.
"""
-import time
-from test_framework.blocktools import (create_block, create_coinbase)
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
from test_framework.key import ECKey
from test_framework.messages import (
CBlockHeader,
@@ -79,24 +81,6 @@ class AssumeValidTest(BitcoinTestFramework):
assert not p2p_conn.is_connected
break
- def assert_blockchain_height(self, node, height):
- """Wait until the blockchain is no longer advancing and verify it's reached the expected height."""
- last_height = node.getblock(node.getbestblockhash())['height']
- timeout = 10
- while True:
- time.sleep(0.25)
- current_height = node.getblock(node.getbestblockhash())['height']
- if current_height != last_height:
- last_height = current_height
- if timeout < 0:
- assert False, "blockchain too short after timeout: %d" % current_height
- timeout - 0.25
- continue
- elif current_height > height:
- assert False, "blockchain too long: %d" % current_height
- elif current_height == height:
- break
-
def run_test(self):
p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
@@ -177,7 +161,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node0. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p0)
- self.assert_blockchain_height(self.nodes[0], 101)
+ self.wait_until(lambda: self.nodes[0].getblockcount() >= 101)
+ assert_equal(self.nodes[0].getblockcount(), 101)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
@@ -188,7 +173,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node2. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p2)
- self.assert_blockchain_height(self.nodes[2], 101)
+ self.wait_until(lambda: self.nodes[2].getblockcount() >= 101)
+ assert_equal(self.nodes[2].getblockcount(), 101)
if __name__ == '__main__':
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index b161c71a85..e6a53b52db 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -354,73 +354,75 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
hdkeypath = v17_info["hdkeypath"]
pubkey = v17_info["pubkey"]
- # Copy the 0.16 wallet to the last Bitcoin Core version and open it:
- shutil.copyfile(
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16"),
- os.path.join(node_master_wallets_dir, "u1_v16")
- )
- load_res = node_master.loadwallet("u1_v16")
- # Make sure this wallet opens without warnings. See https://github.com/bitcoin/bitcoin/pull/19054
- assert_equal(load_res['warning'], '')
- wallet = node_master.get_wallet_rpc("u1_v16")
- info = wallet.getaddressinfo(v16_addr)
- descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + v16_pubkey + ")"
- assert_equal(info["desc"], descsum_create(descriptor))
-
- # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it
- os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16"))
- shutil.copyfile(
- os.path.join(node_master_wallets_dir, "u1_v16"),
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16")
- )
- self.start_node(-1, extra_args=["-wallet=u1_v16"])
- wallet = node_v16.get_wallet_rpc("u1_v16")
- info = wallet.validateaddress(v16_addr)
- assert_equal(info, v16_info)
-
- # Copy the 0.17 wallet to the last Bitcoin Core version and open it:
- node_v17.unloadwallet("u1_v17")
- shutil.copytree(
- os.path.join(node_v17_wallets_dir, "u1_v17"),
- os.path.join(node_master_wallets_dir, "u1_v17")
- )
- node_master.loadwallet("u1_v17")
- wallet = node_master.get_wallet_rpc("u1_v17")
- info = wallet.getaddressinfo(address)
- descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + pubkey + ")"
- assert_equal(info["desc"], descsum_create(descriptor))
-
- # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("u1_v17")
- shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "u1_v17"),
- os.path.join(node_v17_wallets_dir, "u1_v17")
- )
- node_v17.loadwallet("u1_v17")
- wallet = node_v17.get_wallet_rpc("u1_v17")
- info = wallet.getaddressinfo(address)
- assert_equal(info, v17_info)
-
- # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
- shutil.copytree(
- os.path.join(node_v19_wallets_dir, "w1_v19"),
- os.path.join(node_master_wallets_dir, "w1_v19")
- )
- node_master.loadwallet("w1_v19")
- wallet = node_master.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
+ if self.is_bdb_compiled():
+ # Old wallets are BDB and will only work if BDB is compiled
+ # Copy the 0.16 wallet to the last Bitcoin Core version and open it:
+ shutil.copyfile(
+ os.path.join(node_v16_wallets_dir, "wallets/u1_v16"),
+ os.path.join(node_master_wallets_dir, "u1_v16")
+ )
+ load_res = node_master.loadwallet("u1_v16")
+ # Make sure this wallet opens without warnings. See https://github.com/bitcoin/bitcoin/pull/19054
+ assert_equal(load_res['warning'], '')
+ wallet = node_master.get_wallet_rpc("u1_v16")
+ info = wallet.getaddressinfo(v16_addr)
+ descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + v16_pubkey + ")"
+ assert_equal(info["desc"], descsum_create(descriptor))
+
+ # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it
+ os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16"))
+ shutil.copyfile(
+ os.path.join(node_master_wallets_dir, "u1_v16"),
+ os.path.join(node_v16_wallets_dir, "wallets/u1_v16")
+ )
+ self.start_node(-1, extra_args=["-wallet=u1_v16"])
+ wallet = node_v16.get_wallet_rpc("u1_v16")
+ info = wallet.validateaddress(v16_addr)
+ assert_equal(info, v16_info)
- # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("w1_v19")
- shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "w1_v19"),
- os.path.join(node_v19_wallets_dir, "w1_v19")
- )
- node_v19.loadwallet("w1_v19")
- wallet = node_v19.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
+ # Copy the 0.17 wallet to the last Bitcoin Core version and open it:
+ node_v17.unloadwallet("u1_v17")
+ shutil.copytree(
+ os.path.join(node_v17_wallets_dir, "u1_v17"),
+ os.path.join(node_master_wallets_dir, "u1_v17")
+ )
+ node_master.loadwallet("u1_v17")
+ wallet = node_master.get_wallet_rpc("u1_v17")
+ info = wallet.getaddressinfo(address)
+ descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + pubkey + ")"
+ assert_equal(info["desc"], descsum_create(descriptor))
+
+ # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("u1_v17")
+ shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17"))
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, "u1_v17"),
+ os.path.join(node_v17_wallets_dir, "u1_v17")
+ )
+ node_v17.loadwallet("u1_v17")
+ wallet = node_v17.get_wallet_rpc("u1_v17")
+ info = wallet.getaddressinfo(address)
+ assert_equal(info, v17_info)
+
+ # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
+ shutil.copytree(
+ os.path.join(node_v19_wallets_dir, "w1_v19"),
+ os.path.join(node_master_wallets_dir, "w1_v19")
+ )
+ node_master.loadwallet("w1_v19")
+ wallet = node_master.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+
+ # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("w1_v19")
+ shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19"))
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, "w1_v19"),
+ os.path.join(node_v19_wallets_dir, "w1_v19")
+ )
+ node_v19.loadwallet("w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
if __name__ == '__main__':
BackwardsCompatibilityTest().main()
diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py
new file mode 100755
index 0000000000..d13d191b20
--- /dev/null
+++ b/test/functional/feature_blockfilterindex_prune.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 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 blockfilterindex in conjunction with prune."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+)
+
+
+class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.extra_args = [["-fastprune", "-prune=1", "-blockfilterindex=1"]]
+
+ def sync_index(self, height):
+ expected = {'basic block filter index': {'synced': True, 'best_block_height': height}}
+ self.wait_until(lambda: self.nodes[0].getindexinfo() == expected)
+
+ def run_test(self):
+ self.log.info("check if we can access a blockfilter when pruning is enabled but no blocks are actually pruned")
+ self.sync_index(height=200)
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
+ # Mine two batches of blocks to avoid hitting NODE_NETWORK_LIMITED_MIN_BLOCKS disconnection
+ self.nodes[0].generate(250)
+ self.sync_all()
+ self.nodes[0].generate(250)
+ self.sync_all()
+ self.sync_index(height=700)
+
+ self.log.info("prune some blocks")
+ pruneheight = self.nodes[0].pruneblockchain(400)
+ assert_equal(pruneheight, 250)
+
+ self.log.info("check if we can access the tips blockfilter when we have pruned some blocks")
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
+
+ self.log.info("check if we can access the blockfilter of a pruned block")
+ assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getblockhash(2))['filter']), 0)
+
+ self.log.info("start node without blockfilterindex")
+ self.restart_node(0, extra_args=["-fastprune", "-prune=1"])
+
+ self.log.info("make sure accessing the blockfilters throws an error")
+ assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[0].getblockfilter, self.nodes[0].getblockhash(2))
+ self.nodes[0].generate(1000)
+
+ self.log.info("prune below the blockfilterindexes best block while blockfilters are disabled")
+ pruneheight_new = self.nodes[0].pruneblockchain(1000)
+ assert_greater_than(pruneheight_new, pruneheight)
+ self.stop_node(0)
+
+ self.log.info("make sure we get an init error when starting the node again with block filters")
+ with self.nodes[0].assert_debug_log(["basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"]):
+ self.nodes[0].assert_start_raises_init_error(extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"])
+
+ self.log.info("make sure the node starts again with the -reindex arg")
+ self.start_node(0, extra_args = ["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"])
+
+
+if __name__ == '__main__':
+ FeatureBlockfilterindexPruneTest().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 2445b6d977..a0bcd9f12a 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -5,6 +5,7 @@
"""Test various command line arguments and configuration file parameters."""
import os
+import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework import util
@@ -18,7 +19,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.wallet_names = []
def test_config_file_parser(self):
- # Assume node is stopped
+ self.stop_node(0)
inc_conf_file_path = os.path.join(self.nodes[0].datadir, 'include.conf')
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
@@ -88,11 +89,12 @@ class ConfArgsTest(BitcoinTestFramework):
)
def test_log_buffer(self):
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0\n']):
self.start_node(0, extra_args=['-noconnect=0'])
- self.stop_node(0)
def test_args_log(self):
+ self.stop_node(0)
self.log.info('Test config args logging')
with self.nodes[0].assert_debug_log(
expected_msgs=[
@@ -119,39 +121,102 @@ class ConfArgsTest(BitcoinTestFramework):
'-rpcuser=secret-rpcuser',
'-torpassword=secret-torpassword',
])
- self.stop_node(0)
def test_networkactive(self):
self.log.info('Test -networkactive option')
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0)
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0, extra_args=['-networkactive'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.start_node(0, extra_args=['-networkactive=1'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-networkactive=0'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-nonetworkactive'])
- self.stop_node(0)
+ self.stop_node(0)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.start_node(0, extra_args=['-nonetworkactive=1'])
+
+ def test_seed_peers(self):
+ self.log.info('Test seed peers')
+ default_data_dir = self.nodes[0].datadir
+ # Only regtest has no fixed seeds. To avoid connections to random
+ # nodes, regtest is the only network where it is safe to enable
+ # -fixedseeds in tests
+ util.assert_equal(self.nodes[0].getblockchaininfo()['chain'],'regtest')
self.stop_node(0)
- def run_test(self):
+ # No peers.dat exists and -dnsseed=1
+ # We expect the node will use DNS Seeds, but Regtest mode has 0 DNS seeds
+ # So after 60 seconds, the node should fallback to fixed seeds (this is a slow test)
+ assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
+ start = int(time.time())
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Loaded 0 addresses from peers.dat",
+ "0 addresses found from DNS seeds",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=1', '-fixedseeds=1', f'-mocktime={start}'])
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty",
+ ]):
+ self.nodes[0].setmocktime(start + 65)
self.stop_node(0)
+ # No peers.dat exists and -dnsseed=0
+ # We expect the node will fallback immediately to fixed seeds
+ assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
+ start = time.time()
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Loaded 0 addresses from peers.dat",
+ "DNS seeding disabled",
+ "Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1'])
+ assert time.time() - start < 60
+ self.stop_node(0)
+
+ # No peers.dat exists and dns seeds are disabled.
+ # We expect the node will not add fixed seeds when explicitly disabled.
+ assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
+ start = time.time()
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Loaded 0 addresses from peers.dat",
+ "DNS seeding disabled",
+ "Fixed seeds are disabled",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=0'])
+ assert time.time() - start < 60
+ self.stop_node(0)
+
+ # No peers.dat exists and -dnsseed=0, but a -addnode is provided
+ # We expect the node will allow 60 seconds prior to using fixed seeds
+ assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
+ start = int(time.time())
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Loaded 0 addresses from peers.dat",
+ "DNS seeding disabled",
+ ]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1', '-addnode=fakenodeaddr', f'-mocktime={start}'])
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty",
+ ]):
+ self.nodes[0].setmocktime(start + 65)
+
+ def run_test(self):
self.test_log_buffer()
self.test_args_log()
+ self.test_seed_peers()
self.test_networkactive()
self.test_config_file_parser()
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index f9ece244fb..2b56bc78f5 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -49,7 +49,6 @@ from test_framework.util import (
class ChainstateWriteCrashTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
- self.setup_clean_chain = False
self.rpc_timeout = 480
self.supports_cli = False
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index 7de9a589be..2798d11b0a 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Check that it's not possible to start a second bitcoind instance using the same datadir or wallet."""
import os
+import random
+import string
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
@@ -27,11 +29,21 @@ class FilelockTest(BitcoinTestFramework):
self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
if self.is_wallet_compiled():
- self.nodes[0].createwallet(self.default_wallet_name)
- wallet_dir = os.path.join(datadir, 'wallets')
- self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
- expected_msg = "Error: Error initializing wallet database environment"
- self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + self.default_wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
+ def check_wallet_filelock(descriptors):
+ wallet_name = ''.join([random.choice(string.ascii_lowercase) for _ in range(6)])
+ self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=descriptors)
+ wallet_dir = os.path.join(datadir, 'wallets')
+ self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
+ if descriptors:
+ expected_msg = "Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ else:
+ expected_msg = "Error: Error initializing wallet database environment"
+ self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
+
+ if self.is_bdb_compiled():
+ check_wallet_filelock(False)
+ if self.is_sqlite_compiled():
+ check_wallet_filelock(True)
if __name__ == '__main__':
FilelockTest().main()
diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py
index 6f1a0cd348..f22b7f266a 100755
--- a/test/functional/feature_includeconf.py
+++ b/test/functional/feature_includeconf.py
@@ -20,7 +20,6 @@ from test_framework.test_framework import BitcoinTestFramework
class IncludeConfTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def setup_chain(self):
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index f2313bac13..4e2de1daf4 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -5,11 +5,11 @@
"""Test the -alertnotify, -blocknotify and -walletnotify options."""
import os
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, keyhash_to_p2pkh
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- hex_str_to_bytes,
)
# Linux allow all characters other than \x00
@@ -17,7 +17,7 @@ from test_framework.util import (
FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128
FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/'
-
+UNCONFIRMED_HASH_STRING = 'unconfirmed'
def notify_outputname(walletname, txid):
return txid if os.name == 'nt' else '{}_{}'.format(walletname, txid)
@@ -43,12 +43,37 @@ class NotificationsTest(BitcoinTestFramework):
"-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s')),
], [
"-rescan",
- "-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))),
+ "-walletnotify=echo %h_%b > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))),
]]
self.wallet_names = [self.default_wallet_name, self.wallet]
super().setup_network()
def run_test(self):
+ if self.is_wallet_compiled():
+ # Setup the descriptors to be imported to the wallet
+ seed = "cTdGmKFWpbvpKQ7ejrdzqYT2hhjyb3GPHnLAK7wdi5Em67YLwSm9"
+ xpriv = "tprv8ZgxMBicQKsPfHCsTwkiM1KT56RXbGGTqvc2hgqzycpwbHqqpcajQeMRZoBD35kW4RtyCemu6j34Ku5DEspmgjKdt2qe4SvRch5Kk8B8A2v"
+ desc_imports = [{
+ "desc": descsum_create("wpkh(" + xpriv + "/0/*)"),
+ "timestamp": 0,
+ "active": True,
+ "keypool": True,
+ },{
+ "desc": descsum_create("wpkh(" + xpriv + "/1/*)"),
+ "timestamp": 0,
+ "active": True,
+ "keypool": True,
+ "internal": True,
+ }]
+ # Make the wallets and import the descriptors
+ # Ensures that node 0 and node 1 share the same wallet for the conflicting transaction tests below.
+ for i, name in enumerate(self.wallet_names):
+ self.nodes[i].createwallet(wallet_name=name, descriptors=self.options.descriptors, blank=True, load_on_startup=True)
+ if self.options.descriptors:
+ self.nodes[i].importdescriptors(desc_imports)
+ else:
+ self.nodes[i].sethdseed(True, seed)
+
self.log.info("test -blocknotify")
block_count = 10
blocks = self.nodes[1].generatetoaddress(block_count, self.nodes[1].getnewaddress() if self.is_wallet_compiled() else ADDRESS_BCRT1_UNSPENDABLE)
@@ -65,11 +90,9 @@ class NotificationsTest(BitcoinTestFramework):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
- txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
- assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
+ tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
self.stop_node(1)
- for tx_file in os.listdir(self.walletnotify_dir):
- os.remove(os.path.join(self.walletnotify_dir, tx_file))
+ self.expect_wallet_notify(tx_details)
self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications
@@ -79,16 +102,13 @@ class NotificationsTest(BitcoinTestFramework):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
- txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
- assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
- for tx_file in os.listdir(self.walletnotify_dir):
- os.remove(os.path.join(self.walletnotify_dir, tx_file))
+ tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
+ self.expect_wallet_notify(tx_details)
- # Conflicting transactions tests. Give node 0 same wallet seed as
- # node 1, generate spends from node 0, and check notifications
+ # Conflicting transactions tests.
+ # Generate spends from node 0, and check notifications
# triggered by node 1
self.log.info("test -walletnotify with conflicting transactions")
- self.nodes[0].sethdseed(seed=self.nodes[1].dumpprivkey(keyhash_to_p2pkh(hex_str_to_bytes(self.nodes[1].getwalletinfo()['hdseedid'])[::-1])))
self.nodes[0].rescanblockchain()
self.nodes[0].generatetoaddress(100, ADDRESS_BCRT1_UNSPENDABLE)
self.sync_blocks()
@@ -98,7 +118,7 @@ class NotificationsTest(BitcoinTestFramework):
tx1 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True)
assert_equal(tx1 in self.nodes[0].getrawmempool(), True)
self.sync_mempools()
- self.expect_wallet_notify([tx1])
+ self.expect_wallet_notify([(tx1, -1, UNCONFIRMED_HASH_STRING)])
# Generate bump transaction, sync mempools, and check for bump1
# notification. In the future, per
@@ -107,39 +127,57 @@ class NotificationsTest(BitcoinTestFramework):
bump1 = self.nodes[0].bumpfee(tx1)["txid"]
assert_equal(bump1 in self.nodes[0].getrawmempool(), True)
self.sync_mempools()
- self.expect_wallet_notify([bump1])
+ self.expect_wallet_notify([(bump1, -1, UNCONFIRMED_HASH_STRING)])
# Add bump1 transaction to new block, checking for a notification
# and the correct number of confirmations.
- self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ blockhash1 = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ blockheight1 = self.nodes[0].getblockcount()
self.sync_blocks()
- self.expect_wallet_notify([bump1])
+ self.expect_wallet_notify([(bump1, blockheight1, blockhash1)])
assert_equal(self.nodes[1].gettransaction(bump1)["confirmations"], 1)
# Generate a second transaction to be bumped.
tx2 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True)
assert_equal(tx2 in self.nodes[0].getrawmempool(), True)
self.sync_mempools()
- self.expect_wallet_notify([tx2])
+ self.expect_wallet_notify([(tx2, -1, UNCONFIRMED_HASH_STRING)])
# Bump tx2 as bump2 and generate a block on node 0 while
# disconnected, then reconnect and check for notifications on node 1
# about newly confirmed bump2 and newly conflicted tx2.
self.disconnect_nodes(0, 1)
bump2 = self.nodes[0].bumpfee(tx2)["txid"]
- self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ blockhash2 = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ blockheight2 = self.nodes[0].getblockcount()
assert_equal(self.nodes[0].gettransaction(bump2)["confirmations"], 1)
assert_equal(tx2 in self.nodes[1].getrawmempool(), True)
self.connect_nodes(0, 1)
self.sync_blocks()
- self.expect_wallet_notify([bump2, tx2])
+ self.expect_wallet_notify([(bump2, blockheight2, blockhash2), (tx2, -1, UNCONFIRMED_HASH_STRING)])
assert_equal(self.nodes[1].gettransaction(bump2)["confirmations"], 1)
# TODO: add test for `-alertnotify` large fork notifications
- def expect_wallet_notify(self, tx_ids):
- self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10)
- assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id in tx_ids), sorted(os.listdir(self.walletnotify_dir)))
+ def expect_wallet_notify(self, tx_details):
+ self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_details), timeout=10)
+ # Should have no more and no less files than expected
+ assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id, _, _ in tx_details), sorted(os.listdir(self.walletnotify_dir)))
+ # Should now verify contents of each file
+ for tx_id, blockheight, blockhash in tx_details:
+ fname = os.path.join(self.walletnotify_dir, notify_outputname(self.wallet, tx_id))
+ with open(fname, 'rt', encoding='utf-8') as f:
+ text = f.read()
+ # Universal newline ensures '\n' on 'nt'
+ assert_equal(text[-1], '\n')
+ text = text[:-1]
+ if os.name == 'nt':
+ # On Windows, echo as above will append a whitespace
+ assert_equal(text[-1], ' ')
+ text = text[:-1]
+ expected = str(blockheight) + '_' + blockhash
+ assert_equal(text, expected)
+
for tx_file in os.listdir(self.walletnotify_dir):
os.remove(os.path.join(self.walletnotify_dir, tx_file))
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index cd5eff9184..8bee43b8ad 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -44,14 +44,15 @@ from test_framework.netutil import test_ipv6_local
RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
-# Networks returned by RPC getpeerinfo, defined in src/netbase.cpp::GetNetworkName()
-NET_UNROUTABLE = "unroutable"
+# Networks returned by RPC getpeerinfo.
+NET_UNROUTABLE = "not_publicly_routable"
NET_IPV4 = "ipv4"
NET_IPV6 = "ipv6"
NET_ONION = "onion"
+NET_I2P = "i2p"
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
-NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION})
+NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P})
class ProxyTest(BitcoinTestFramework):
@@ -90,11 +91,15 @@ class ProxyTest(BitcoinTestFramework):
self.serv3 = Socks5Server(self.conf3)
self.serv3.start()
+ # We will not try to connect to this.
+ self.i2p_sam = ('127.0.0.1', 7656)
+
# Note: proxies are not used to connect to local nodes. This is because the proxy to
# use is based on CService.GetNetwork(), which returns NET_UNROUTABLE for localhost.
args = [
['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
- ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
+ ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),
+ '-i2psam=%s:%i' % (self.i2p_sam), '-i2pacceptincoming=0', '-proxyrandomize=0'],
['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
[]
]
@@ -199,9 +204,16 @@ class ProxyTest(BitcoinTestFramework):
n0 = networks_dict(self.nodes[0].getnetworkinfo())
assert_equal(NETWORKS, n0.keys())
for net in NETWORKS:
- assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr))
- assert_equal(n0[net]['proxy_randomize_credentials'], True)
+ if net == NET_I2P:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = '%s:%i' % (self.conf1.addr)
+ expected_randomize = True
+ assert_equal(n0[net]['proxy'], expected_proxy)
+ assert_equal(n0[net]['proxy_randomize_credentials'], expected_randomize)
assert_equal(n0['onion']['reachable'], True)
+ assert_equal(n0['i2p']['reachable'], False)
n1 = networks_dict(self.nodes[1].getnetworkinfo())
assert_equal(NETWORKS, n1.keys())
@@ -211,21 +223,36 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr))
assert_equal(n1['onion']['proxy_randomize_credentials'], False)
assert_equal(n1['onion']['reachable'], True)
+ assert_equal(n1['i2p']['proxy'], '%s:%i' % (self.i2p_sam))
+ assert_equal(n1['i2p']['proxy_randomize_credentials'], False)
+ assert_equal(n1['i2p']['reachable'], True)
n2 = networks_dict(self.nodes[2].getnetworkinfo())
assert_equal(NETWORKS, n2.keys())
for net in NETWORKS:
- assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr))
- assert_equal(n2[net]['proxy_randomize_credentials'], True)
+ if net == NET_I2P:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = '%s:%i' % (self.conf2.addr)
+ expected_randomize = True
+ assert_equal(n2[net]['proxy'], expected_proxy)
+ assert_equal(n2[net]['proxy_randomize_credentials'], expected_randomize)
assert_equal(n2['onion']['reachable'], True)
+ assert_equal(n2['i2p']['reachable'], False)
if self.have_ipv6:
n3 = networks_dict(self.nodes[3].getnetworkinfo())
assert_equal(NETWORKS, n3.keys())
for net in NETWORKS:
- assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
+ if net == NET_I2P:
+ expected_proxy = ''
+ else:
+ expected_proxy = '[%s]:%i' % (self.conf3.addr)
+ assert_equal(n3[net]['proxy'], expected_proxy)
assert_equal(n3[net]['proxy_randomize_credentials'], False)
assert_equal(n3['onion']['reachable'], False)
+ assert_equal(n3['i2p']['reachable'], False)
if __name__ == '__main__':
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 6ee2b72c11..183a43abd4 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -177,17 +177,17 @@ def default_negflag(ctx):
"""Default expression for "negflag": tap.negflag."""
return get(ctx, "tap").negflag
-def default_pubkey_inner(ctx):
- """Default expression for "pubkey_inner": tap.inner_pubkey."""
- return get(ctx, "tap").inner_pubkey
+def default_pubkey_internal(ctx):
+ """Default expression for "pubkey_internal": tap.internal_pubkey."""
+ return get(ctx, "tap").internal_pubkey
def default_merklebranch(ctx):
"""Default expression for "merklebranch": tapleaf.merklebranch."""
return get(ctx, "tapleaf").merklebranch
def default_controlblock(ctx):
- """Default expression for "controlblock": combine leafversion, negflag, pubkey_inner, merklebranch."""
- return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_inner") + get(ctx, "merklebranch")
+ """Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch."""
+ return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch")
def default_sighash(ctx):
"""Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash."""
@@ -341,9 +341,9 @@ DEFAULT_CONTEXT = {
"tapleaf": default_tapleaf,
# The script to push, and include in the sighash, for a taproot script path spend.
"script_taproot": default_script_taproot,
- # The inner pubkey for a taproot script path spend (32 bytes).
- "pubkey_inner": default_pubkey_inner,
- # The negation flag of the inner pubkey for a taproot script path spend.
+ # The internal pubkey for a taproot script path spend (32 bytes).
+ "pubkey_internal": default_pubkey_internal,
+ # The negation flag of the internal pubkey for a taproot script path spend.
"negflag": default_negflag,
# The leaf version to include in the sighash (this does not affect the one in the control block).
"leafversion": default_leafversion,
@@ -517,7 +517,6 @@ def add_spender(spenders, *args, **kwargs):
def random_checksig_style(pubkey):
"""Creates a random CHECKSIG* tapscript that would succeed with only the valid signature on witness stack."""
- return bytes(CScript([pubkey, OP_CHECKSIG]))
opcode = random.choice([OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKSIGADD])
if (opcode == OP_CHECKSIGVERIFY):
ret = CScript([pubkey, opcode, OP_1])
@@ -781,8 +780,8 @@ def spenders_taproot_active():
add_spender(spenders, "spendpath/negflag", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"negflag": lambda ctx: 1 - default_negflag(ctx)}, **ERR_WITNESS_PROGRAM_MISMATCH)
# Test that bitflips in the Merkle branch invalidate it.
add_spender(spenders, "spendpath/bitflipmerkle", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"merklebranch": bitflipper(default_merklebranch)}, **ERR_WITNESS_PROGRAM_MISMATCH)
- # Test that bitflips in the inner pubkey invalidate it.
- add_spender(spenders, "spendpath/bitflippubkey", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"pubkey_inner": bitflipper(default_pubkey_inner)}, **ERR_WITNESS_PROGRAM_MISMATCH)
+ # Test that bitflips in the internal pubkey invalidate it.
+ add_spender(spenders, "spendpath/bitflippubkey", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"pubkey_internal": bitflipper(default_pubkey_internal)}, **ERR_WITNESS_PROGRAM_MISMATCH)
# Test that empty witnesses are invalid.
add_spender(spenders, "spendpath/emptywit", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"witness": []}, **ERR_EMPTY_WITNESS)
# Test that adding garbage to the control block invalidates it.
diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py
new file mode 100755
index 0000000000..6e6046d84d
--- /dev/null
+++ b/test/functional/feature_utxo_set_hash.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020-2021 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 UTXO set hash value calculation in gettxoutsetinfo."""
+
+import struct
+
+from test_framework.blocktools import create_transaction
+from test_framework.messages import (
+ CBlock,
+ COutPoint,
+ FromHex,
+)
+from test_framework.muhash import MuHash3072
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class UTXOSetHashTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def test_deterministic_hash_results(self):
+ self.log.info("Test deterministic UTXO set hash results")
+
+ # These depend on the setup_clean_chain option, the chain loaded from the cache
+ assert_equal(self.nodes[0].gettxoutsetinfo()['hash_serialized_2'], "b32ec1dda5a53cd025b95387aad344a801825fe46a60ff952ce26528f01d3be8")
+ assert_equal(self.nodes[0].gettxoutsetinfo("muhash")['muhash'], "dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8")
+
+ def test_muhash_implementation(self):
+ self.log.info("Test MuHash implementation consistency")
+
+ node = self.nodes[0]
+
+ # Generate 100 blocks and remove the first since we plan to spend its
+ # coinbase
+ block_hashes = node.generate(100)
+ blocks = list(map(lambda block: FromHex(CBlock(), node.getblock(block, False)), block_hashes))
+ spending = blocks.pop(0)
+
+ # Create a spending transaction and mine a block which includes it
+ tx = create_transaction(node, spending.vtx[0].rehash(), node.getnewaddress(), amount=49)
+ txid = node.sendrawtransaction(hexstring=tx.serialize_with_witness().hex(), maxfeerate=0)
+
+ tx_block = node.generateblock(output=node.getnewaddress(), transactions=[txid])
+ blocks.append(FromHex(CBlock(), node.getblock(tx_block['hash'], False)))
+
+ # Serialize the outputs that should be in the UTXO set and add them to
+ # a MuHash object
+ muhash = MuHash3072()
+
+ for height, block in enumerate(blocks):
+ # The Genesis block coinbase is not part of the UTXO set and we
+ # spent the first mined block
+ height += 2
+
+ for tx in block.vtx:
+ for n, tx_out in enumerate(tx.vout):
+ coinbase = 1 if not tx.vin[0].prevout.hash else 0
+
+ # Skip witness commitment
+ if (coinbase and n > 0):
+ continue
+
+ data = COutPoint(int(tx.rehash(), 16), n).serialize()
+ data += struct.pack("<i", height * 2 + coinbase)
+ data += tx_out.serialize()
+
+ muhash.insert(data)
+
+ finalized = muhash.digest()
+ node_muhash = node.gettxoutsetinfo("muhash")['muhash']
+
+ assert_equal(finalized[::-1].hex(), node_muhash)
+
+ def run_test(self):
+ self.test_deterministic_hash_results()
+ self.test_muhash_implementation()
+
+
+if __name__ == '__main__':
+ UTXOSetHashTest().main()
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 1257dff1ae..2cf0ef2251 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -29,6 +29,8 @@ class TestBitcoinCli(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ if self.is_wallet_compiled():
+ self.requires_wallet = True
def skip_test_if_missing_module(self):
self.skip_if_no_cli()
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index 9c877aaeae..4d5666f414 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -8,6 +8,9 @@ import os
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than_or_equal
+from threading import Thread
+import subprocess
+
def expect_http_status(expected_http_status, expected_rpc_code,
fcn, *args):
@@ -18,6 +21,16 @@ def expect_http_status(expected_http_status, expected_rpc_code,
assert_equal(exc.error["code"], expected_rpc_code)
assert_equal(exc.http_status, expected_http_status)
+
+def test_work_queue_getblock(node, got_exceeded_error):
+ while not got_exceeded_error:
+ try:
+ node.cli('getrpcinfo').send_cli()
+ except subprocess.CalledProcessError as e:
+ assert_equal(e.output, 'error: Server response: Work queue depth exceeded\n')
+ got_exceeded_error.append(True)
+
+
class RPCInterfaceTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -67,10 +80,23 @@ class RPCInterfaceTest(BitcoinTestFramework):
expect_http_status(404, -32601, self.nodes[0].invalidmethod)
expect_http_status(500, -8, self.nodes[0].getblockhash, 42)
+ def test_work_queue_exceeded(self):
+ self.log.info("Testing work queue exceeded...")
+ self.restart_node(0, ['-rpcworkqueue=1', '-rpcthreads=1'])
+ got_exceeded_error = []
+ threads = []
+ for _ in range(3):
+ t = Thread(target=test_work_queue_getblock, args=(self.nodes[0], got_exceeded_error))
+ t.start()
+ threads.append(t)
+ for t in threads:
+ t.join()
+
def run_test(self):
self.test_getrpcinfo()
self.test_batch_request()
self.test_http_status_codes()
+ self.test_work_queue_exceeded()
if __name__ == '__main__':
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 946bfa51d4..94e162b748 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -27,28 +27,31 @@ def hash256_reversed(byte_str):
class ZMQSubscriber:
def __init__(self, socket, topic):
- self.sequence = 0
+ self.sequence = None # no sequence number received yet
self.socket = socket
self.topic = topic
self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
- def receive(self):
+ # Receive message from publisher and verify that topic and sequence match
+ def _receive_from_publisher_and_check(self):
topic, body, seq = self.socket.recv_multipart()
# Topic should match the subscriber topic.
assert_equal(topic, self.topic)
# Sequence should be incremental.
- assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ received_seq = struct.unpack('<I', seq)[-1]
+ if self.sequence is None:
+ self.sequence = received_seq
+ else:
+ assert_equal(received_seq, self.sequence)
self.sequence += 1
return body
+ def receive(self):
+ return self._receive_from_publisher_and_check()
+
def receive_sequence(self):
- topic, body, seq = self.socket.recv_multipart()
- # Topic should match the subscriber topic.
- assert_equal(topic, self.topic)
- # Sequence should be incremental.
- assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
- self.sequence += 1
+ body = self._receive_from_publisher_and_check()
hash = body[:32].hex()
label = chr(body[32])
mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
@@ -59,9 +62,39 @@ class ZMQSubscriber:
return (hash, label, mempool_sequence)
+class ZMQTestSetupBlock:
+ """Helper class for setting up a ZMQ test via the "sync up" procedure.
+ Generates a block on the specified node on instantiation and provides a
+ method to check whether a ZMQ notification matches, i.e. the event was
+ caused by this generated block. Assumes that a notification either contains
+ the generated block's hash, it's (coinbase) transaction id, the raw block or
+ raw transaction data.
+ """
+
+ def __init__(self, node):
+ self.block_hash = node.generate(1)[0]
+ coinbase = node.getblock(self.block_hash, 2)['tx'][0]
+ self.tx_hash = coinbase['txid']
+ self.raw_tx = coinbase['hex']
+ self.raw_block = node.getblock(self.block_hash, 0)
+
+ def caused_notification(self, notification):
+ return (
+ self.block_hash in notification
+ or self.tx_hash in notification
+ or self.raw_block in notification
+ or self.raw_tx in notification
+ )
+
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ if self.is_wallet_compiled():
+ self.requires_wallet = True
+ # This test isn't testing txn relay/timing, so set whitelist on the
+ # peers for instant txn relay. This speeds up the test run time 2-3x.
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_py3_zmq()
@@ -82,23 +115,47 @@ class ZMQTest (BitcoinTestFramework):
# Restart node with the specified zmq notifications enabled, subscribe to
# all of them and return the corresponding ZMQSubscriber objects.
- def setup_zmq_test(self, services, recv_timeout=60, connect_nodes=False):
+ def setup_zmq_test(self, services, *, recv_timeout=60, sync_blocks=True):
subscribers = []
for topic, address in services:
socket = self.ctx.socket(zmq.SUB)
- socket.set(zmq.RCVTIMEO, recv_timeout*1000)
subscribers.append(ZMQSubscriber(socket, topic.encode()))
- self.restart_node(0, ["-zmqpub%s=%s" % (topic, address) for topic, address in services])
-
- if connect_nodes:
- self.connect_nodes(0, 1)
+ self.restart_node(0, ["-zmqpub%s=%s" % (topic, address) for topic, address in services] +
+ self.extra_args[0])
for i, sub in enumerate(subscribers):
sub.socket.connect(services[i][1])
- # Relax so that the subscribers are ready before publishing zmq messages
- sleep(0.2)
+ # Ensure that all zmq publisher notification interfaces are ready by
+ # running the following "sync up" procedure:
+ # 1. Generate a block on the node
+ # 2. Try to receive the corresponding notification on all subscribers
+ # 3. If all subscribers get the message within the timeout (1 second),
+ # we are done, otherwise repeat starting from step 1
+ for sub in subscribers:
+ sub.socket.set(zmq.RCVTIMEO, 1000)
+ while True:
+ test_block = ZMQTestSetupBlock(self.nodes[0])
+ recv_failed = False
+ for sub in subscribers:
+ try:
+ while not test_block.caused_notification(sub.receive().hex()):
+ self.log.debug("Ignoring sync-up notification for previously generated block.")
+ except zmq.error.Again:
+ self.log.debug("Didn't receive sync-up notification, trying again.")
+ recv_failed = True
+ if not recv_failed:
+ self.log.debug("ZMQ sync-up completed, all subscribers are ready.")
+ break
+
+ # set subscriber's desired timeout for the test
+ for sub in subscribers:
+ sub.socket.set(zmq.RCVTIMEO, recv_timeout*1000)
+
+ self.connect_nodes(0, 1)
+ if sync_blocks:
+ self.sync_blocks()
return subscribers
@@ -108,9 +165,7 @@ class ZMQTest (BitcoinTestFramework):
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
address = 'tcp://127.0.0.1:28332'
- subs = self.setup_zmq_test(
- [(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]],
- connect_nodes=True)
+ subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]])
hashblock = subs[0]
hashtx = subs[1]
@@ -187,6 +242,7 @@ class ZMQTest (BitcoinTestFramework):
hashblock, hashtx = self.setup_zmq_test(
[(topic, address) for topic in ["hashblock", "hashtx"]],
recv_timeout=2) # 2 second timeout to check end of notifications
+ self.disconnect_nodes(0, 1)
# Generate 1 block in nodes[0] with 1 mempool tx and receive all notifications
payment_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
@@ -235,6 +291,7 @@ class ZMQTest (BitcoinTestFramework):
"""
self.log.info("Testing 'sequence' publisher")
[seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
+ self.disconnect_nodes(0, 1)
# Mempool sequence number starts at 1
seq_num = 1
@@ -309,7 +366,7 @@ class ZMQTest (BitcoinTestFramework):
block_count = self.nodes[0].getblockcount()
best_hash = self.nodes[0].getbestblockhash()
self.nodes[0].invalidateblock(best_hash)
- sleep(2) # Bit of room to make sure transaction things happened
+ sleep(2) # Bit of room to make sure transaction things happened
# Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
# of the time they were gathered.
@@ -358,8 +415,8 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(label, "A")
# More transactions to be simply mined
for i in range(len(more_tx)):
- assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
+ assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
# Bumped by rbf
assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
mempool_seq += 1
@@ -374,7 +431,7 @@ class ZMQTest (BitcoinTestFramework):
assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
mempool_seq += 1
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
- self.sync_all() # want to make sure we didn't break "consensus" for other tests
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
def test_mempool_sync(self):
"""
@@ -385,7 +442,7 @@ class ZMQTest (BitcoinTestFramework):
return
self.log.info("Testing 'mempool sync' usage of sequence notifier")
- [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")], connect_nodes=True)
+ [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
# In-memory counter, should always start at 1
next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"]
@@ -485,10 +542,13 @@ class ZMQTest (BitcoinTestFramework):
def test_multiple_interfaces(self):
# Set up two subscribers with different addresses
+ # (note that after the reorg test, syncing would fail due to different
+ # chain lengths on node0 and node1; for this test we only need node0, so
+ # we can disable syncing blocks on the setup)
subscribers = self.setup_zmq_test([
("hashblock", "tcp://127.0.0.1:28334"),
("hashblock", "tcp://127.0.0.1:28335"),
- ])
+ ], sync_blocks=False)
# Generate 1 block in nodes[0] and receive all notifications
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 70cd4ebb3b..752b925b92 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -69,6 +69,8 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getrawmempool()), 5)
assert_equal(len(self.nodes[1].getrawmempool()), 5)
+ total_fee_old = self.nodes[0].getmempoolinfo()['total_fee']
+
self.log.debug("Prioritize a transaction on node0")
fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
assert_equal(fees['base'], fees['modified'])
@@ -76,6 +78,10 @@ class MempoolPersistTest(BitcoinTestFramework):
fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified'])
+ self.log.info('Check the total base fee is unchanged after prioritisetransaction')
+ assert_equal(total_fee_old, self.nodes[0].getmempoolinfo()['total_fee'])
+ assert_equal(total_fee_old, sum(v['fees']['base'] for k, v in self.nodes[0].getrawmempool(verbose=True).items()))
+
tx_creation_time = self.nodes[0].getmempoolentry(txid=last_txid)['time']
assert_greater_than_or_equal(tx_creation_time, tx_creation_time_lower)
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
diff --git a/test/functional/mocks/signer.py b/test/functional/mocks/signer.py
new file mode 100755
index 0000000000..676d0a0a4d
--- /dev/null
+++ b/test/functional/mocks/signer.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+import os
+import sys
+import argparse
+import json
+
+def perform_pre_checks():
+ mock_result_path = os.path.join(os.getcwd(), "mock_result")
+ if(os.path.isfile(mock_result_path)):
+ with open(mock_result_path, "r", encoding="utf8") as f:
+ mock_result = f.read()
+ if mock_result[0]:
+ sys.stdout.write(mock_result[2:])
+ sys.exit(int(mock_result[0]))
+
+def enumerate(args):
+ sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))
+
+def getdescriptors(args):
+ xpub = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B"
+
+ sys.stdout.write(json.dumps({
+ "receive": [
+ "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j",
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#r0grqw5x",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#x30uthjs"
+ ],
+ "internal": [
+ "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2",
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#kwx4c3pe",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#h92akzzg"
+ ]
+ }))
+
+
+def displayaddress(args):
+ # Several descriptor formats are acceptable, so allowing for potential
+ # changes to InferDescriptor:
+ if args.fingerprint != "00000001":
+ return sys.stdout.write(json.dumps({"error": "Unexpected fingerprint", "fingerprint": args.fingerprint}))
+
+ expected_desc = [
+ "wpkh([00000001/84'/1'/0'/0/0]02c97dc3f4420402e01a113984311bf4a1b8de376cac0bdcfaf1b3ac81f13433c7)#0yneg42r"
+ ]
+ if args.desc not in expected_desc:
+ return sys.stdout.write(json.dumps({"error": "Unexpected descriptor", "desc": args.desc}))
+
+ return sys.stdout.write(json.dumps({"address": "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g"}))
+
+def signtx(args):
+ if args.fingerprint != "00000001":
+ return sys.stdout.write(json.dumps({"error": "Unexpected fingerprint", "fingerprint": args.fingerprint}))
+
+ with open(os.path.join(os.getcwd(), "mock_psbt"), "r", encoding="utf8") as f:
+ mock_psbt = f.read()
+
+ if args.fingerprint == "00000001" :
+ sys.stdout.write(json.dumps({
+ "psbt": mock_psbt,
+ "complete": True
+ }))
+ else:
+ sys.stdout.write(json.dumps({"psbt": args.psbt}))
+
+parser = argparse.ArgumentParser(prog='./signer.py', description='External signer mock')
+parser.add_argument('--fingerprint')
+parser.add_argument('--chain', default='main')
+parser.add_argument('--stdin', action='store_true')
+
+subparsers = parser.add_subparsers(description='Commands', dest='command')
+subparsers.required = True
+
+parser_enumerate = subparsers.add_parser('enumerate', help='list available signers')
+parser_enumerate.set_defaults(func=enumerate)
+
+parser_getdescriptors = subparsers.add_parser('getdescriptors')
+parser_getdescriptors.set_defaults(func=getdescriptors)
+parser_getdescriptors.add_argument('--account', metavar='account')
+
+parser_displayaddress = subparsers.add_parser('displayaddress', help='display address on signer')
+parser_displayaddress.add_argument('--desc', metavar='desc')
+parser_displayaddress.set_defaults(func=displayaddress)
+
+parser_signtx = subparsers.add_parser('signtx')
+parser_signtx.add_argument('psbt', metavar='psbt')
+
+parser_signtx.set_defaults(func=signtx)
+
+if not sys.stdin.isatty():
+ buffer = sys.stdin.read()
+ if buffer and buffer.rstrip() != "":
+ sys.argv.extend(buffer.rstrip().split(" "))
+
+args = parser.parse_args()
+
+perform_pre_checks()
+
+args.func(args)
diff --git a/test/functional/p2p_add_connections.py b/test/functional/p2p_add_connections.py
index a63c3a3287..a04ba5db2d 100755
--- a/test/functional/p2p_add_connections.py
+++ b/test/functional/p2p_add_connections.py
@@ -17,7 +17,6 @@ def check_node_connections(*, node, num_in, num_out):
class P2PAddConnections(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 2
def setup_network(self):
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 91fbd722cf..69821763bd 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -46,7 +46,6 @@ class AddrReceiver(P2PInterface):
class AddrTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def run_test(self):
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index c592ab52b1..6584efae79 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -15,7 +15,6 @@ from test_framework.util import assert_equal
class P2PBlocksOnly(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
self.extra_args = [["-blocksonly"]]
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 642a217047..8f64419138 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -19,7 +19,13 @@ from test_framework.messages import (
msg_mempool,
msg_version,
)
-from test_framework.p2p import P2PInterface, p2p_lock
+from test_framework.p2p import (
+ P2PInterface,
+ P2P_SERVICES,
+ P2P_SUBVERSION,
+ P2P_VERSION,
+ p2p_lock,
+)
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
from test_framework.test_framework import BitcoinTestFramework
@@ -81,7 +87,6 @@ class P2PBloomFilter(P2PInterface):
class FilterTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
self.extra_args = [[
'-peerbloomfilters',
@@ -217,9 +222,12 @@ class FilterTest(BitcoinTestFramework):
self.log.info('Test BIP 37 for a node with fRelay = False')
# Add peer but do not send version yet
filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False)
- # Send version with fRelay=False
+ # Send version with relay=False
version_without_fRelay = msg_version()
- version_without_fRelay.nRelay = 0
+ version_without_fRelay.nVersion = P2P_VERSION
+ version_without_fRelay.strSubVer = P2P_SUBVERSION
+ version_without_fRelay.nServices = P2P_SERVICES
+ version_without_fRelay.relay = 0
filter_peer_without_nrelay.send_message(version_without_fRelay)
filter_peer_without_nrelay.wait_for_verack()
assert not self.nodes[0].getpeerinfo()[0]['relaytxes']
diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py
index 2b75ad5175..d375af6fe1 100755
--- a/test/functional/p2p_getaddr_caching.py
+++ b/test/functional/p2p_getaddr_caching.py
@@ -41,7 +41,6 @@ class AddrReceiver(P2PInterface):
class AddrTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def run_test(self):
diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py
index e4fc9fd178..f884cf90ff 100755
--- a/test/functional/p2p_invalid_locator.py
+++ b/test/functional/p2p_invalid_locator.py
@@ -13,7 +13,6 @@ from test_framework.test_framework import BitcoinTestFramework
class InvalidLocatorTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.setup_clean_chain = False
def run_test(self):
node = self.nodes[0] # convenience reference to the node
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index cca7390ae3..8783c244c3 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -154,7 +154,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333)))
orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
- with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
+ with node.assert_debug_log(['orphanage overflow, removed 1 tx']):
node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
rejected_parent = CTransaction()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index ca8bf908a9..71d5ca92b3 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -4,8 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test message sending before handshake completion.
-A node should never send anything other than VERSION/VERACK until it's
-received a VERACK.
+Before receiving a VERACK, a node should not send anything but VERSION/VERACK
+and feature negotiation messages (WTXIDRELAY, SENDADDRV2).
This test connects to a node and sends it a few messages, trying to entice it
into sending us something it shouldn't."""
@@ -17,7 +17,12 @@ from test_framework.messages import (
msg_ping,
msg_version,
)
-from test_framework.p2p import P2PInterface
+from test_framework.p2p import (
+ P2PInterface,
+ P2P_SUBVERSION,
+ P2P_SERVICES,
+ P2P_VERSION_RELAY,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -30,10 +35,12 @@ class LazyPeer(P2PInterface):
super().__init__()
self.unexpected_msg = False
self.ever_connected = False
+ self.got_wtxidrelay = False
+ self.got_sendaddrv2 = False
def bad_message(self, message):
self.unexpected_msg = True
- self.log.info("should not have received message: %s" % message.msgtype)
+ print("should not have received message: %s" % message.msgtype)
def on_open(self):
self.ever_connected = True
@@ -59,6 +66,8 @@ class LazyPeer(P2PInterface):
def on_cmpctblock(self, message): self.bad_message(message)
def on_getblocktxn(self, message): self.bad_message(message)
def on_blocktxn(self, message): self.bad_message(message)
+ def on_wtxidrelay(self, message): self.got_wtxidrelay = True
+ def on_sendaddrv2(self, message): self.got_sendaddrv2 = True
# Peer that sends a version but not a verack.
@@ -89,32 +98,61 @@ class P2PVersionStore(P2PInterface):
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [['-peertimeout=4']]
+
+ def create_old_version(self, nversion):
+ old_version_msg = msg_version()
+ old_version_msg.nVersion = nversion
+ old_version_msg.strSubVer = P2P_SUBVERSION
+ old_version_msg.nServices = P2P_SERVICES
+ old_version_msg.relay = P2P_VERSION_RELAY
+ return old_version_msg
def run_test(self):
- # Another peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
+ self.log.info('Check that the node doesn\'t send unexpected messages before handshake completion')
+ # Peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)
# Peer that sends a version but not a verack.
no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)
- # Wait until we got the verack in response to the version. Though, don't wait for the node to receive the
- # verack, since we never sent one
+ # Pre-wtxidRelay peer that sends a version but not a verack and does not support feature negotiation
+ # messages which start at nVersion == 70016
+ pre_wtxidrelay_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), send_version=False, wait_for_verack=False)
+ pre_wtxidrelay_peer.send_message(self.create_old_version(70015))
+
+ # Wait until the peer gets the verack in response to the version. Though, don't wait for the node to receive the
+ # verack, since the peer never sent one
no_verack_idle_peer.wait_for_verack()
+ pre_wtxidrelay_peer.wait_for_verack()
no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)
no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)
+ pre_wtxidrelay_peer.wait_until(lambda: pre_wtxidrelay_peer.version_received)
# Mine a block and make sure that it's not sent to the connected peers
self.nodes[0].generate(nblocks=1)
- #Give the node enough time to possibly leak out a message
+ # Give the node enough time to possibly leak out a message
time.sleep(5)
- self.nodes[0].disconnect_p2ps()
+ # Make sure only expected messages came in
+ assert not no_version_idle_peer.unexpected_msg
+ assert not no_version_idle_peer.got_wtxidrelay
+ assert not no_version_idle_peer.got_sendaddrv2
- # Make sure no unexpected messages came in
- assert no_version_idle_peer.unexpected_msg == False
- assert no_verack_idle_peer.unexpected_msg == False
+ assert not no_verack_idle_peer.unexpected_msg
+ assert no_verack_idle_peer.got_wtxidrelay
+ assert no_verack_idle_peer.got_sendaddrv2
+
+ assert not pre_wtxidrelay_peer.unexpected_msg
+ assert not pre_wtxidrelay_peer.got_wtxidrelay
+ assert not pre_wtxidrelay_peer.got_sendaddrv2
+
+ # Expect peers to be disconnected due to timeout
+ assert not no_version_idle_peer.is_connected
+ assert not no_verack_idle_peer.is_connected
+ assert not pre_wtxidrelay_peer.is_connected
self.log.info('Check that the version message does not leak the local address of the node')
p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())
@@ -125,14 +163,12 @@ class P2PLeakTest(BitcoinTestFramework):
assert_equal(ver.addrFrom.port, 0)
assert_equal(ver.addrFrom.ip, '0.0.0.0')
assert_equal(ver.nStartingHeight, 201)
- assert_equal(ver.nRelay, 1)
+ assert_equal(ver.relay, 1)
self.log.info('Check that old peers are disconnected')
p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
- old_version_msg = msg_version()
- old_version_msg.nVersion = 31799
- with self.nodes[0].assert_debug_log(['peer=3 using obsolete version 31799; disconnecting']):
- p2p_old_peer.send_message(old_version_msg)
+ with self.nodes[0].assert_debug_log(['peer=4 using obsolete version 31799; disconnecting']):
+ p2p_old_peer.send_message(self.create_old_version(31799))
p2p_old_peer.wait_for_disconnect()
diff --git a/test/functional/p2p_message_capture.py b/test/functional/p2p_message_capture.py
index 113e26c425..080b2d93ad 100755
--- a/test/functional/p2p_message_capture.py
+++ b/test/functional/p2p_message_capture.py
@@ -42,14 +42,14 @@ def mini_parser(dat_file):
if not tmp_header_raw:
break
tmp_header = BytesIO(tmp_header_raw)
- int.from_bytes(tmp_header.read(TIME_SIZE), "little") # type: int
+ tmp_header.read(TIME_SIZE) # skip the timestamp field
raw_msgtype = tmp_header.read(MSGTYPE_SIZE)
- msgtype = raw_msgtype.split(b'\x00', 1)[0] # type: bytes
+ msgtype: bytes = raw_msgtype.split(b'\x00', 1)[0]
remainder = raw_msgtype.split(b'\x00', 1)[1]
assert(len(msgtype) > 0)
assert(msgtype in MESSAGEMAP)
assert(len(remainder) == 0 or not remainder.decode().isprintable())
- length = int.from_bytes(tmp_header.read(LENGTH_SIZE), "little") # type: int
+ length: int = int.from_bytes(tmp_header.read(LENGTH_SIZE), "little")
data = f_in.read(length)
assert_equal(len(data), length)
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 8a751c6b54..4bf96cb0e6 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -56,7 +56,6 @@ MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + INBOUND_PEER_TX_DELAY + TXID_RE
class TxDownloadTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 2
def test_tx_requests(self):
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 99be6b7b8e..e090030205 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -23,6 +23,7 @@ import http.client
import os
import subprocess
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.blocktools import (
create_block,
create_coinbase,
@@ -71,11 +72,10 @@ class BlockchainTest(BitcoinTestFramework):
def mine_chain(self):
self.log.info('Create some old blocks')
- address = self.nodes[0].get_deterministic_priv_key().address
for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
# ten-minute steps from genesis block time
self.nodes[0].setmocktime(t)
- self.nodes[0].generatetoaddress(1, address)
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
def _test_getblockchaininfo(self):
@@ -227,7 +227,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['transactions'], 200)
assert_equal(res['height'], 200)
assert_equal(res['txouts'], 200)
- assert_equal(res['bogosize'], 15000),
+ assert_equal(res['bogosize'], 16800),
assert_equal(res['bestblock'], node.getblockhash(200))
size = res['disk_size']
assert size > 6400
@@ -268,6 +268,18 @@ class BlockchainTest(BitcoinTestFramework):
res5 = node.gettxoutsetinfo(hash_type='none')
assert 'hash_serialized_2' not in res5
+ # hash_type muhash should return a different UTXO set hash.
+ res6 = node.gettxoutsetinfo(hash_type='muhash')
+ assert 'muhash' in res6
+ assert(res['hash_serialized_2'] != res6['muhash'])
+
+ # muhash should not be included in gettxoutset unless requested.
+ for r in [res, res2, res3, res4, res5]:
+ assert 'muhash' not in r
+
+ # Unknown hash_type raises an error
+ assert_raises_rpc_error(-8, "foohash is not a valid hash_type", node.gettxoutsetinfo, "foohash")
+
def _test_getblockheader(self):
node = self.nodes[0]
@@ -304,6 +316,9 @@ class BlockchainTest(BitcoinTestFramework):
header.calc_sha256()
assert_equal(header.hash, besthash)
+ assert 'previousblockhash' not in node.getblockheader(node.getblockhash(0))
+ assert 'nextblockhash' not in node.getblockheader(node.getbestblockhash())
+
def _test_getdifficulty(self):
difficulty = self.nodes[0].getdifficulty()
# 1 hash in 2 should be valid, so difficulty should be 1/2**31
@@ -317,12 +332,12 @@ class BlockchainTest(BitcoinTestFramework):
def _test_stopatheight(self):
assert_equal(self.nodes[0].getblockcount(), 200)
- self.nodes[0].generatetoaddress(6, self.nodes[0].get_deterministic_priv_key().address)
+ self.nodes[0].generatetoaddress(6, ADDRESS_BCRT1_P2WSH_OP_TRUE)
assert_equal(self.nodes[0].getblockcount(), 206)
self.log.debug('Node should not stop at this height')
assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
try:
- self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
except (ConnectionError, http.client.BadStatusLine):
pass # The node already shut down before response
self.log.debug('Node should stop at this height...')
@@ -372,8 +387,7 @@ class BlockchainTest(BitcoinTestFramework):
node = self.nodes[0]
miniwallet = MiniWallet(node)
- miniwallet.generate(5)
- node.generate(100)
+ miniwallet.scan_blocks(num=5)
fee_per_byte = Decimal('0.00000010')
fee_per_kb = 1000 * fee_per_byte
@@ -408,6 +422,9 @@ class BlockchainTest(BitcoinTestFramework):
# Restore chain state
move_block_file('rev_wrong', 'rev00000.dat')
+ assert 'previousblockhash' not in node.getblock(node.getblockhash(0))
+ assert 'nextblockhash' not in node.getblock(node.getbestblockhash())
+
if __name__ == '__main__':
BlockchainTest().main()
diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py
index 81862ac69e..51b7efb4c3 100755
--- a/test/functional/rpc_estimatefee.py
+++ b/test/functional/rpc_estimatefee.py
@@ -14,7 +14,6 @@ from test_framework.util import assert_raises_rpc_error
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def run_test(self):
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 569471dc87..8c9755cc8c 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -32,6 +32,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# This test isn't testing tx relay. Set whitelist on the peers for
# instant tx relay.
self.extra_args = [['-whitelist=noban@127.0.0.1']] * self.num_nodes
+ self.rpc_timeout = 90 # to prevent timeouts in `test_transaction_too_large`
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -94,6 +95,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_address_reuse()
self.test_option_subtract_fee_from_outputs()
self.test_subtract_fee_with_presets()
+ self.test_transaction_too_large()
def test_change_position(self):
"""Ensure setting changePosition in fundraw with an exact match is handled properly."""
@@ -907,5 +909,25 @@ class RawTransactionsTest(BitcoinTestFramework):
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex'])
self.nodes[0].sendrawtransaction(signedtx['hex'])
+ def test_transaction_too_large(self):
+ self.log.info("Test fundrawtx where BnB solution would result in a too large transaction, but Knapsack would not")
+ self.nodes[0].createwallet("large")
+ wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ recipient = self.nodes[0].get_wallet_rpc("large")
+ outputs = {}
+ rawtx = recipient.createrawtransaction([], {wallet.getnewaddress(): 147.99899260})
+
+ # Make 1500 0.1 BTC outputs. The amount that we target for funding is in
+ # the BnB range when these outputs are used. However if these outputs
+ # are selected, the transaction will end up being too large, so it
+ # shouldn't use BnB and instead fall back to Knapsack but that behavior
+ # is not implemented yet. For now we just check that we get an error.
+ for _ in range(1500):
+ outputs[recipient.getnewaddress()] = 0.1
+ wallet.sendmany("", outputs)
+ self.nodes[0].generate(10)
+ assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
+
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py
index 044dbd35bf..a99e50f29f 100755
--- a/test/functional/rpc_getblockfilter.py
+++ b/test/functional/rpc_getblockfilter.py
@@ -54,5 +54,11 @@ class GetBlockFilterTest(BitcoinTestFramework):
genesis_hash = self.nodes[0].getblockhash(0)
assert_raises_rpc_error(-5, "Unknown filtertype", self.nodes[0].getblockfilter, genesis_hash, "unknown")
+ # Test getblockfilter fails on node without compact block filter index
+ self.restart_node(0, extra_args=["-blockfilterindex=0"])
+ for filter_type in FILTER_TYPES:
+ assert_raises_rpc_error(-1, "Index is not enabled for filtertype {}".format(filter_type),
+ self.nodes[0].getblockfilter, genesis_hash, filter_type)
+
if __name__ == '__main__':
GetBlockFilterTest().main()
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index 1eefd109f8..de21f43747 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -105,10 +105,13 @@ class HelpRpcTest(BitcoinTestFramework):
if self.is_wallet_compiled():
components.append('Wallet')
+ if self.is_external_signer_compiled():
+ components.append('Signer')
+
if self.is_zmq_compiled():
components.append('Zmq')
- assert_equal(titles, components)
+ assert_equal(titles, sorted(components))
def dump_help(self):
dump_dir = os.path.join(self.options.tmpdir, 'rpc_help_dump')
diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py
index 469d6bdb05..e362642f0f 100755
--- a/test/functional/rpc_invalid_address_message.py
+++ b/test/functional/rpc_invalid_address_message.py
@@ -12,8 +12,12 @@ from test_framework.util import (
)
BECH32_VALID = 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
-BECH32_INVALID_SIZE = 'bcrt1sqqpl9r5c'
-BECH32_INVALID_PREFIX = 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
+BECH32_INVALID_BECH32 = 'bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqdmchcc'
+BECH32_INVALID_BECH32M = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7k35mrzd'
+BECH32_INVALID_VERSION = 'bcrt130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqynjegk'
+BECH32_INVALID_SIZE = 'bcrt1s0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav25430mtr'
+BECH32_INVALID_V0_SIZE = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kqqq5k3my'
+BECH32_INVALID_PREFIX = 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'
BASE58_VALID = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn'
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
@@ -40,6 +44,18 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
assert not info['isvalid']
assert_equal(info['error'], 'Invalid prefix for Bech32 address')
+ info = node.validateaddress(BECH32_INVALID_BECH32)
+ assert not info['isvalid']
+ assert_equal(info['error'], 'Version 1+ witness address must use Bech32m checksum')
+
+ info = node.validateaddress(BECH32_INVALID_BECH32M)
+ assert not info['isvalid']
+ assert_equal(info['error'], 'Version 0 witness address must use Bech32 checksum')
+
+ info = node.validateaddress(BECH32_INVALID_V0_SIZE)
+ assert not info['isvalid']
+ assert_equal(info['error'], 'Invalid Bech32 v0 address data size')
+
info = node.validateaddress(BECH32_VALID)
assert info['isvalid']
assert 'error' not in info
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index de0b7f303f..9adb32c3c5 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -25,6 +25,7 @@ from test_framework.util import (
assert_raises_rpc_error,
p2p_port,
)
+from test_framework.wallet import MiniWallet
def assert_net_servicesnames(servicesflag, servicenames):
@@ -48,6 +49,9 @@ class NetTest(BitcoinTestFramework):
self.supports_cli = False
def run_test(self):
+ # We need miniwallet to make a transaction
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.generate(1)
# Get out of IBD for the minfeefilter and getpeerinfo tests.
self.nodes[0].generate(101)
@@ -74,8 +78,7 @@ class NetTest(BitcoinTestFramework):
def test_getpeerinfo(self):
self.log.info("Test getpeerinfo")
# Create a few getpeerinfo last_block/last_transaction values.
- if self.is_wallet_compiled():
- self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+ self.wallet.send_self_transfer(from_node=self.nodes[0]) # Make a transaction so we can see it in the getpeerinfo results
self.nodes[1].generate(1)
self.sync_all()
time_now = int(time.time())
@@ -101,6 +104,9 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][0]['connection_type'], 'manual')
assert_equal(peer_info[1][1]['connection_type'], 'inbound')
+ # Check dynamically generated networks list in getpeerinfo help output.
+ assert "(ipv4, ipv6, onion, i2p, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
+
def test_getnettotals(self):
self.log.info("Test getnettotals")
# Test getnettotals and getpeerinfo by doing a ping. The bytes
@@ -149,6 +155,9 @@ class NetTest(BitcoinTestFramework):
for info in network_info:
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
+ # Check dynamically generated networks list in getnetworkinfo help output.
+ assert "(ipv4, ipv6, onion, i2p)" in self.nodes[0].help("getnetworkinfo")
+
def test_getaddednodeinfo(self):
self.log.info("Test getaddednodeinfo")
assert_equal(self.nodes[0].getaddednodeinfo(), [])
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index b364077a9a..ed6abaed78 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -24,7 +24,6 @@ MAX_BIP125_RBF_SEQUENCE = 0xfffffffd
class PSBTTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 3
self.extra_args = [
["-walletrbf=1"],
diff --git a/test/functional/rpc_signmessage.py b/test/functional/rpc_signmessage.py
index 0cb3ce4215..1c71732a61 100755
--- a/test/functional/rpc_signmessage.py
+++ b/test/functional/rpc_signmessage.py
@@ -5,7 +5,10 @@
"""Test RPC commands for signing and verifying messages."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
class SignMessagesTest(BitcoinTestFramework):
def set_test_params(self):
@@ -38,5 +41,22 @@ class SignMessagesTest(BitcoinTestFramework):
assert not self.nodes[0].verifymessage(other_address, signature, message)
assert not self.nodes[0].verifymessage(address, other_signature, message)
+ self.log.info('test parameter validity and error codes')
+ # signmessage(withprivkey) have two required parameters
+ for num_params in [0, 1, 3, 4, 5]:
+ param_list = ["dummy"]*num_params
+ assert_raises_rpc_error(-1, "signmessagewithprivkey", self.nodes[0].signmessagewithprivkey, *param_list)
+ assert_raises_rpc_error(-1, "signmessage", self.nodes[0].signmessage, *param_list)
+ # verifymessage has three required parameters
+ for num_params in [0, 1, 2, 4, 5]:
+ param_list = ["dummy"]*num_params
+ assert_raises_rpc_error(-1, "verifymessage", self.nodes[0].verifymessage, *param_list)
+ # invalid key or address provided
+ assert_raises_rpc_error(-5, "Invalid private key", self.nodes[0].signmessagewithprivkey, "invalid_key", message)
+ assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].signmessage, "invalid_addr", message)
+ assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].verifymessage, "invalid_addr", signature, message)
+ # malformed signature provided
+ assert_raises_rpc_error(-3, "Malformed base64 encoding", self.nodes[0].verifymessage, self.nodes[0].getnewaddress(), "invalid_sig", message)
+
if __name__ == '__main__':
SignMessagesTest().main()
diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py
index e86f91b1d0..6177970872 100755
--- a/test/functional/rpc_uptime.py
+++ b/test/functional/rpc_uptime.py
@@ -10,6 +10,7 @@ Test corresponds to code in rpc/server.cpp.
import time
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
class UptimeTest(BitcoinTestFramework):
@@ -18,8 +19,12 @@ class UptimeTest(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
+ self._test_negative_time()
self._test_uptime()
+ def _test_negative_time(self):
+ assert_raises_rpc_error(-8, "Mocktime can not be negative: -1.", self.nodes[0].setmocktime, -1)
+
def _test_uptime(self):
wait_time = 10
self.nodes[0].setmocktime(int(time.time() + wait_time))
diff --git a/test/functional/test-shell.md b/test/functional/test-shell.md
index f6ea9ef682..b8e899d675 100644
--- a/test/functional/test-shell.md
+++ b/test/functional/test-shell.md
@@ -178,7 +178,7 @@ can be called after the TestShell is shut down.
| `num_nodes` | `1` | Sets the number of initialized bitcoind processes. |
| `perf` | False | Profiles running nodes with `perf` for the duration of the test if set to `True`. |
| `rpc_timeout` | `60` | Sets the RPC server timeout for the underlying bitcoind processes. |
-| `setup_clean_chain` | `False` | Initializes an empty blockchain by default. A 199-block-long chain is initialized if set to `True`. |
+| `setup_clean_chain` | `False` | A 200-block-long chain is initialized from cache by default. Instead, `setup_clean_chain` initializes an empty blockchain if set to `True`. |
| `randomseed` | Random Integer | `TestShell.options.randomseed` is a member of `TestShell` which can be accessed during a test to seed a random generator. User can override default with a constant value for reproducible test runs. |
| `supports_cli` | `False` | Whether the bitcoin-cli utility is compiled and available for the test. |
| `tmpdir` | `"/var/folders/.../"` | Sets directory for test logs. Will be deleted upon a successful test run unless `nocleanup` is set to `True` |
diff --git a/test/functional/test_framework/bdb.py b/test/functional/test_framework/bdb.py
index 9de358aa0a..97b9c1d6d0 100644
--- a/test/functional/test_framework/bdb.py
+++ b/test/functional/test_framework/bdb.py
@@ -51,7 +51,6 @@ def dump_leaf_page(data):
page_info['pgno'] = pgno
page_info['prev_pgno'] = prev_pgno
page_info['next_pgno'] = next_pgno
- page_info['entries'] = entries
page_info['hf_offset'] = hf_offset
page_info['level'] = level
page_info['pg_type'] = pg_type
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index e0cbab45ce..26526e35fa 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -20,10 +20,6 @@ def TaggedHash(tag, data):
ss += data
return hashlib.sha256(ss).digest()
-def xor_bytes(b0, b1):
- assert len(b0) == len(b1)
- return bytes(x ^ y for (x, y) in zip(b0, b1))
-
def jacobi_symbol(n, k):
"""Compute the Jacobi symbol of n modulo k
@@ -510,7 +506,7 @@ class TestFrameworkKey(unittest.TestCase):
if pubkey is not None:
keys[privkey] = pubkey
for msg in byte_arrays: # test every combination of message, signing key, verification key
- for sign_privkey, sign_pubkey in keys.items():
+ for sign_privkey, _ in keys.items():
sig = sign_schnorr(sign_privkey, msg)
for verify_privkey, verify_pubkey in keys.items():
if verify_privkey == sign_privkey:
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 27a09ef86c..a18a9ec109 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -31,11 +31,6 @@ import time
from test_framework.siphash import siphash256
from test_framework.util import hex_str_to_bytes, assert_equal
-MIN_VERSION_SUPPORTED = 60001
-MY_VERSION = 70016 # past wtxid relay
-MY_SUBVERSION = b"/python-p2p-tester:0.0.3/"
-MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
-
MAX_LOCATOR_SZ = 101
MAX_BLOCK_BASE_SIZE = 1000000
MAX_BLOOM_FILTER_SIZE = 36000
@@ -326,22 +321,20 @@ class CBlockLocator:
__slots__ = ("nVersion", "vHave")
def __init__(self):
- self.nVersion = MY_VERSION
self.vHave = []
def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
+ struct.unpack("<i", f.read(4))[0] # Ignore version field.
self.vHave = deser_uint256_vector(f)
def serialize(self):
r = b""
- r += struct.pack("<i", self.nVersion)
+ r += struct.pack("<i", 0) # Bitcoin Core ignores version field. Set it to 0.
r += ser_uint256_vector(self.vHave)
return r
def __repr__(self):
- return "CBlockLocator(nVersion=%i vHave=%s)" \
- % (self.nVersion, repr(self.vHave))
+ return "CBlockLocator(vHave=%s)" % (repr(self.vHave))
class COutPoint:
@@ -1023,20 +1016,20 @@ class CMerkleBlock:
# Objects that correspond to messages on the wire
class msg_version:
- __slots__ = ("addrFrom", "addrTo", "nNonce", "nRelay", "nServices",
+ __slots__ = ("addrFrom", "addrTo", "nNonce", "relay", "nServices",
"nStartingHeight", "nTime", "nVersion", "strSubVer")
msgtype = b"version"
def __init__(self):
- self.nVersion = MY_VERSION
- self.nServices = NODE_NETWORK | NODE_WITNESS
+ self.nVersion = 0
+ self.nServices = 0
self.nTime = int(time.time())
self.addrTo = CAddress()
self.addrFrom = CAddress()
self.nNonce = random.getrandbits(64)
- self.strSubVer = MY_SUBVERSION
+ self.strSubVer = ''
self.nStartingHeight = -1
- self.nRelay = MY_RELAY
+ self.relay = 0
def deserialize(self, f):
self.nVersion = struct.unpack("<i", f.read(4))[0]
@@ -1048,18 +1041,18 @@ class msg_version:
self.addrFrom = CAddress()
self.addrFrom.deserialize(f, with_time=False)
self.nNonce = struct.unpack("<Q", f.read(8))[0]
- self.strSubVer = deser_string(f)
+ self.strSubVer = deser_string(f).decode('utf-8')
self.nStartingHeight = struct.unpack("<i", f.read(4))[0]
if self.nVersion >= 70001:
# Relay field is optional for version 70001 onwards
try:
- self.nRelay = struct.unpack("<b", f.read(1))[0]
+ self.relay = struct.unpack("<b", f.read(1))[0]
except:
- self.nRelay = 0
+ self.relay = 0
else:
- self.nRelay = 0
+ self.relay = 0
def serialize(self):
r = b""
@@ -1069,16 +1062,16 @@ class msg_version:
r += self.addrTo.serialize(with_time=False)
r += self.addrFrom.serialize(with_time=False)
r += struct.pack("<Q", self.nNonce)
- r += ser_string(self.strSubVer)
+ r += ser_string(self.strSubVer.encode('utf-8'))
r += struct.pack("<i", self.nStartingHeight)
- r += struct.pack("<b", self.nRelay)
+ r += struct.pack("<b", self.relay)
return r
def __repr__(self):
- return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \
+ return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i relay=%i)' \
% (self.nVersion, self.nServices, time.ctime(self.nTime),
repr(self.addrTo), repr(self.addrFrom), self.nNonce,
- self.strSubVer, self.nStartingHeight, self.nRelay)
+ self.strSubVer, self.nStartingHeight, self.relay)
class msg_verack:
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index fa4a567aac..05099f3339 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -31,7 +31,6 @@ import threading
from test_framework.messages import (
CBlockHeader,
MAX_HEADERS_RESULTS,
- MIN_VERSION_SUPPORTED,
msg_addr,
msg_addrv2,
msg_block,
@@ -79,6 +78,18 @@ from test_framework.util import (
logger = logging.getLogger("TestFramework.p2p")
+# The minimum P2P version that this test framework supports
+MIN_P2P_VERSION_SUPPORTED = 60001
+# The P2P version that this test framework implements and sends in its `version` message
+# Version 70016 supports wtxid relay
+P2P_VERSION = 70016
+# The services that this test framework offers in its `version` message
+P2P_SERVICES = NODE_NETWORK | NODE_WITNESS
+# The P2P user agent string that this test framework sends in its `version` message
+P2P_SUBVERSION = "/python-p2p-tester:0.0.3/"
+# Value for relay that this test framework sends in its `version` message
+P2P_VERSION_RELAY = 1
+
MESSAGEMAP = {
b"addr": msg_addr,
b"addrv2": msg_addrv2,
@@ -327,6 +338,9 @@ class P2PInterface(P2PConnection):
def peer_connect_send_version(self, services):
# Send a version msg
vt = msg_version()
+ vt.nVersion = P2P_VERSION
+ vt.strSubVer = P2P_SUBVERSION
+ vt.relay = P2P_VERSION_RELAY
vt.nServices = services
vt.addrTo.ip = self.dstaddr
vt.addrTo.port = self.dstport
@@ -334,7 +348,7 @@ class P2PInterface(P2PConnection):
vt.addrFrom.port = 0
self.on_connection_send_msg = vt # Will be sent in connection_made callback
- def peer_connect(self, *args, services=NODE_NETWORK | NODE_WITNESS, send_version=True, **kwargs):
+ def peer_connect(self, *args, services=P2P_SERVICES, send_version=True, **kwargs):
create_conn = super().peer_connect(*args, **kwargs)
if send_version:
@@ -417,7 +431,7 @@ class P2PInterface(P2PConnection):
pass
def on_version(self, message):
- assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
+ assert message.nVersion >= MIN_P2P_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_P2P_VERSION_SUPPORTED)
if message.nVersion >= 70016 and self.wtxidrelay:
self.send_message(msg_wtxidrelay())
if self.support_addrv2:
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index be0e9f24e2..3c9b8a6e69 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -29,8 +29,6 @@ MAX_SCRIPT_ELEMENT_SIZE = 520
LOCKTIME_THRESHOLD = 500000000
ANNEX_TAG = 0x50
-OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
-
LEAF_VERSION_TAPSCRIPT = 0xc0
def hash160(s):
@@ -47,7 +45,6 @@ def bn2vch(v):
# Serialize to bytes
return encoded_v.to_bytes(n_bytes, 'little')
-_opcode_instances = [] # type: List[CScriptOp]
class CScriptOp(int):
"""A single script opcode"""
__slots__ = ()
@@ -111,6 +108,9 @@ class CScriptOp(int):
_opcode_instances.append(super().__new__(cls, n))
return _opcode_instances[n]
+OPCODE_NAMES: Dict[CScriptOp, str] = {}
+_opcode_instances: List[CScriptOp] = []
+
# Populate opcode instance table
for n in range(0xff + 1):
CScriptOp(n)
@@ -826,11 +826,11 @@ def taproot_tree_helper(scripts):
# A TaprootInfo object has the following fields:
# - scriptPubKey: the scriptPubKey (witness v1 CScript)
-# - inner_pubkey: the inner pubkey (32 bytes)
-# - negflag: whether the pubkey in the scriptPubKey was negated from inner_pubkey+tweak*G (bool).
+# - internal_pubkey: the internal pubkey (32 bytes)
+# - negflag: whether the pubkey in the scriptPubKey was negated from internal_pubkey+tweak*G (bool).
# - tweak: the tweak (32 bytes)
# - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves
-TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves")
+TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves")
# A TaprootLeafInfo object has the following fields:
# - script: the leaf script (CScript or bytes)
diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py
index 00c0d8a919..861ca2b949 100644
--- a/test/functional/test_framework/segwit_addr.py
+++ b/test/functional/test_framework/segwit_addr.py
@@ -2,10 +2,18 @@
# Copyright (c) 2017 Pieter Wuille
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Reference implementation for Bech32 and segwit addresses."""
+"""Reference implementation for Bech32/Bech32m and segwit addresses."""
import unittest
+from enum import Enum
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+BECH32_CONST = 1
+BECH32M_CONST = 0x2bc830a3
+
+class Encoding(Enum):
+ """Enumeration type to list the various supported encodings."""
+ BECH32 = 1
+ BECH32M = 2
def bech32_polymod(values):
@@ -27,38 +35,45 @@ def bech32_hrp_expand(hrp):
def bech32_verify_checksum(hrp, data):
"""Verify a checksum given HRP and converted data characters."""
- return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
-
+ check = bech32_polymod(bech32_hrp_expand(hrp) + data)
+ if check == BECH32_CONST:
+ return Encoding.BECH32
+ elif check == BECH32M_CONST:
+ return Encoding.BECH32M
+ else:
+ return None
-def bech32_create_checksum(hrp, data):
+def bech32_create_checksum(encoding, hrp, data):
"""Compute the checksum values given HRP and data."""
values = bech32_hrp_expand(hrp) + data
- polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
+ const = BECH32M_CONST if encoding == Encoding.BECH32M else BECH32_CONST
+ polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
-def bech32_encode(hrp, data):
- """Compute a Bech32 string given HRP and data values."""
- combined = data + bech32_create_checksum(hrp, data)
+def bech32_encode(encoding, hrp, data):
+ """Compute a Bech32 or Bech32m string given HRP and data values."""
+ combined = data + bech32_create_checksum(encoding, hrp, data)
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
def bech32_decode(bech):
- """Validate a Bech32 string, and determine HRP and data."""
+ """Validate a Bech32/Bech32m string, and determine HRP and data."""
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
(bech.lower() != bech and bech.upper() != bech)):
- return (None, None)
+ return (None, None, None)
bech = bech.lower()
pos = bech.rfind('1')
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
- return (None, None)
+ return (None, None, None)
if not all(x in CHARSET for x in bech[pos+1:]):
- return (None, None)
+ return (None, None, None)
hrp = bech[:pos]
data = [CHARSET.find(x) for x in bech[pos+1:]]
- if not bech32_verify_checksum(hrp, data):
- return (None, None)
- return (hrp, data[:-6])
+ encoding = bech32_verify_checksum(hrp, data)
+ if encoding is None:
+ return (None, None, None)
+ return (encoding, hrp, data[:-6])
def convertbits(data, frombits, tobits, pad=True):
@@ -86,7 +101,7 @@ def convertbits(data, frombits, tobits, pad=True):
def decode_segwit_address(hrp, addr):
"""Decode a segwit address."""
- hrpgot, data = bech32_decode(addr)
+ encoding, hrpgot, data = bech32_decode(addr)
if hrpgot != hrp:
return (None, None)
decoded = convertbits(data[1:], 5, 8, False)
@@ -96,12 +111,15 @@ def decode_segwit_address(hrp, addr):
return (None, None)
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
return (None, None)
+ if (data[0] == 0 and encoding != Encoding.BECH32) or (data[0] != 0 and encoding != Encoding.BECH32M):
+ return (None, None)
return (data[0], decoded)
def encode_segwit_address(hrp, witver, witprog):
"""Encode a segwit address."""
- ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
+ encoding = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
+ ret = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
if decode_segwit_address(hrp, ret) == (None, None):
return None
return ret
@@ -119,3 +137,5 @@ class TestFrameworkScript(unittest.TestCase):
# P2WSH
test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
test_python_bech32('bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85')
+ # P2TR
+ test_python_bech32('bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6')
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 4bda73599d..02eb10b5a4 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -19,6 +19,7 @@ import tempfile
import time
from typing import List
+from .address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from .authproxy import JSONRPCException
from . import coverage
from .p2p import NetworkThread
@@ -108,6 +109,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# skipped. If list is truncated, wallet creation is skipped and keys
# are not imported.
self.wallet_names = None
+ # By default the wallet is not required. Set to true by skip_if_no_wallet().
+ # When False, we ignore wallet_names regardless of what it is.
+ self.requires_wallet = False
self.set_test_params()
assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes
if self.options.timeout_factor == 0 :
@@ -184,15 +188,30 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser.add_argument('--timeout-factor', dest="timeout_factor", type=float, default=1.0, help='adjust test timeouts by a factor. Setting it to 0 disables all timeouts')
group = parser.add_mutually_exclusive_group()
- group.add_argument("--descriptors", default=False, action="store_true",
+ group.add_argument("--descriptors", action='store_const', const=True,
help="Run test using a descriptor wallet", dest='descriptors')
- group.add_argument("--legacy-wallet", default=False, action="store_false",
+ group.add_argument("--legacy-wallet", action='store_const', const=False,
help="Run test using legacy wallets", dest='descriptors')
self.add_options(parser)
self.options = parser.parse_args()
self.options.previous_releases_path = previous_releases_path
+ config = configparser.ConfigParser()
+ config.read_file(open(self.options.configfile))
+ self.config = config
+
+ if self.options.descriptors is None:
+ # Prefer BDB unless it isn't available
+ if self.is_bdb_compiled():
+ self.options.descriptors = False
+ elif self.is_sqlite_compiled():
+ self.options.descriptors = True
+ else:
+ # If neither are compiled, tests requiring a wallet will be skipped and the value of self.options.descriptors won't matter
+ # It still needs to exist and be None in order for tests to work however.
+ self.options.descriptors = None
+
def setup(self):
"""Call this method to start up the test framework object with options set."""
@@ -202,9 +221,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.cachedir = os.path.abspath(self.options.cachedir)
- config = configparser.ConfigParser()
- config.read_file(open(self.options.configfile))
- self.config = config
+ config = self.config
+
fname_bitcoind = os.path.join(
config["environment"]["BUILDDIR"],
"src",
@@ -377,7 +395,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args = self.extra_args
self.add_nodes(self.num_nodes, extra_args)
self.start_nodes()
- if self.is_wallet_compiled():
+ if self.requires_wallet:
self.import_deterministic_coinbase_privkeys()
if not self.setup_clean_chain:
for n in self.nodes:
@@ -715,16 +733,17 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Set a time in the past, so that blocks don't end up in the future
cache_node.setmocktime(cache_node.getblockheader(cache_node.getbestblockhash())['time'])
- # Create a 199-block-long chain; each of the 4 first nodes
+ # Create a 199-block-long chain; each of the 3 first nodes
# gets 25 mature blocks and 25 immature.
- # The 4th node gets only 24 immature blocks so that the very last
+ # The 4th address gets 25 mature and only 24 immature blocks so that the very last
# block in the cache does not age too much (have an old tip age).
# This is needed so that we are out of IBD when the test starts,
# see the tip age check in IsInitialBlockDownload().
+ gen_addresses = [k.address for k in TestNode.PRIV_KEYS] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
for i in range(8):
cache_node.generatetoaddress(
nblocks=25 if i != 7 else 24,
- address=TestNode.PRIV_KEYS[i % 4].address,
+ address=gen_addresses[i % 4],
)
assert_equal(cache_node.getblockchaininfo()["blocks"], 199)
@@ -769,10 +788,13 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def skip_if_no_wallet(self):
"""Skip the running test if wallet has not been compiled."""
+ self.requires_wallet = True
if not self.is_wallet_compiled():
raise SkipTest("wallet has not been compiled.")
if self.options.descriptors:
self.skip_if_no_sqlite()
+ else:
+ self.skip_if_no_bdb()
def skip_if_no_sqlite(self):
"""Skip the running test if sqlite has not been compiled."""
@@ -807,10 +829,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.previous_releases_path))
return self.options.prev_releases
+ def skip_if_no_external_signer(self):
+ """Skip the running test if external signer support has not been compiled."""
+ if not self.is_external_signer_compiled():
+ raise SkipTest("external signer support has not been compiled.")
+
def is_cli_compiled(self):
"""Checks whether bitcoin-cli was compiled."""
return self.config["components"].getboolean("ENABLE_CLI")
+ def is_external_signer_compiled(self):
+ """Checks whether external signer support was compiled."""
+ return self.config["components"].getboolean("ENABLE_EXTERNAL_SIGNER")
+
def is_wallet_compiled(self):
"""Checks whether the wallet module was compiled."""
return self.config["components"].getboolean("ENABLE_WALLET")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index b61d433652..ce9c1bc024 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -23,9 +23,10 @@ import sys
from .authproxy import JSONRPCException
from .descriptors import descsum_create
-from .messages import MY_SUBVERSION
+from .p2p import P2P_SUBVERSION
from .util import (
MAX_NODES,
+ assert_equal,
append_config,
delete_cookie_file,
get_auth_cookie,
@@ -114,6 +115,8 @@ class TestNode():
if self.version_is_at_least(190000):
self.args.append("-logthreadnames")
+ if self.version_is_at_least(219900):
+ self.args.append("-logsourcelocations")
self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
self.use_cli = use_cli
@@ -545,6 +548,11 @@ class TestNode():
# in comparison to the upside of making tests less fragile and unexpected intermittent errors less likely.
p2p_conn.sync_with_ping()
+ # Consistency check that the Bitcoin Core has received our user agent string. This checks the
+ # node's newest peer. It could be racy if another Bitcoin Core node has connected since we opened
+ # our connection, but we don't expect that to happen.
+ assert_equal(self.getpeerinfo()[-1]['subver'], P2P_SUBVERSION)
+
return p2p_conn
def add_outbound_p2p_connection(self, p2p_conn, *, p2p_idx, connection_type="outbound-full-relay", **kwargs):
@@ -572,7 +580,7 @@ class TestNode():
def num_test_p2p_connections(self):
"""Return number of test framework p2p connections to the node."""
- return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION.decode("utf-8")])
+ return len([peer for peer in self.getpeerinfo() if peer['subver'] == P2P_SUBVERSION])
def disconnect_p2ps(self):
"""Close all p2p connections to the node."""
@@ -670,10 +678,10 @@ class RPCOverloadWrapper():
def __getattr__(self, name):
return getattr(self.rpc, name)
- def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None):
+ def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None, external_signer=None):
if descriptors is None:
descriptors = self.descriptors
- return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup)
+ return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup, external_signer)
def importprivkey(self, privkey, label=None, rescan=None):
wallet_info = self.getwalletinfo()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 123c48852c..d335d4ea79 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -368,6 +368,7 @@ def write_config(config_path, *, n, chain, extra_config=""):
f.write("keypool=1\n")
f.write("discover=0\n")
f.write("dnsseed=0\n")
+ f.write("fixedseeds=0\n")
f.write("listenonion=0\n")
f.write("printtoconsole=0\n")
f.write("upnp=0\n")
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index edd7792608..38fbf3c1a6 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -32,6 +32,15 @@ class MiniWallet:
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ def scan_blocks(self, *, start=1, num):
+ """Scan the blocks for self._address outputs and add them to self._utxos"""
+ for i in range(start, start + num):
+ block = self._test_node.getblock(blockhash=self._test_node.getblockhash(i), verbosity=2)
+ for tx in block['tx']:
+ for out in tx['vout']:
+ if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
+ self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
+
def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
blocks = self._test_node.generatetoaddress(num_blocks, self._address)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0e6340b69d..79ad2cf161 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -86,51 +86,52 @@ EXTENDED_SCRIPTS = [
BASE_SCRIPTS = [
# Scripts that are run by default.
# Longest test should go first, to favor running tests in parallel
- 'wallet_hd.py',
+ 'wallet_hd.py --legacy-wallet',
'wallet_hd.py --descriptors',
- 'wallet_backup.py',
+ 'wallet_backup.py --legacy-wallet',
'wallet_backup.py --descriptors',
# vv Tests less than 5m vv
'mining_getblocktemplate_longpoll.py',
'feature_maxuploadtarget.py',
'feature_block.py',
- 'rpc_fundrawtransaction.py',
+ 'rpc_fundrawtransaction.py --legacy-wallet',
'rpc_fundrawtransaction.py --descriptors',
'p2p_compactblocks.py',
'feature_segwit.py --legacy-wallet',
# vv Tests less than 2m vv
- 'wallet_basic.py',
+ 'wallet_basic.py --legacy-wallet',
'wallet_basic.py --descriptors',
- 'wallet_labels.py',
+ 'wallet_labels.py --legacy-wallet',
'wallet_labels.py --descriptors',
'p2p_segwit.py',
'p2p_timeouts.py',
'p2p_tx_download.py',
'mempool_updatefromblock.py',
'wallet_dump.py --legacy-wallet',
- 'wallet_listtransactions.py',
+ 'wallet_listtransactions.py --legacy-wallet',
'wallet_listtransactions.py --descriptors',
'feature_taproot.py',
+ 'wallet_signer.py --descriptors',
# vv Tests less than 60s vv
'p2p_sendheaders.py',
'wallet_importmulti.py --legacy-wallet',
'mempool_limit.py',
'rpc_txoutproof.py',
- 'wallet_listreceivedby.py',
+ 'wallet_listreceivedby.py --legacy-wallet',
'wallet_listreceivedby.py --descriptors',
- 'wallet_abandonconflict.py',
+ 'wallet_abandonconflict.py --legacy-wallet',
'wallet_abandonconflict.py --descriptors',
'feature_csv_activation.py',
- 'rpc_rawtransaction.py',
+ 'rpc_rawtransaction.py --legacy-wallet',
'rpc_rawtransaction.py --descriptors',
- 'wallet_address_types.py',
+ 'wallet_address_types.py --legacy-wallet',
'wallet_address_types.py --descriptors',
'feature_bip68_sequence.py',
'p2p_feefilter.py',
'feature_reindex.py',
'feature_abortnode.py',
# vv Tests less than 30s vv
- 'wallet_keypool_topup.py',
+ 'wallet_keypool_topup.py --legacy-wallet',
'wallet_keypool_topup.py --descriptors',
'feature_fee_estimation.py',
'interface_zmq.py',
@@ -138,7 +139,7 @@ BASE_SCRIPTS = [
'interface_bitcoin_cli.py',
'mempool_resurrect.py',
'wallet_txn_doublespend.py --mineblock',
- 'tool_wallet.py',
+ 'tool_wallet.py --legacy-wallet',
'tool_wallet.py --descriptors',
'wallet_txn_clone.py',
'wallet_txn_clone.py --segwit',
@@ -146,14 +147,14 @@ BASE_SCRIPTS = [
'rpc_misc.py',
'interface_rest.py',
'mempool_spend_coinbase.py',
- 'wallet_avoidreuse.py',
+ 'wallet_avoidreuse.py --legacy-wallet',
'wallet_avoidreuse.py --descriptors',
'mempool_reorg.py',
'mempool_persist.py',
- 'wallet_multiwallet.py',
+ 'wallet_multiwallet.py --legacy-wallet',
'wallet_multiwallet.py --descriptors',
'wallet_multiwallet.py --usecli',
- 'wallet_createwallet.py',
+ 'wallet_createwallet.py --legacy-wallet',
'wallet_createwallet.py --usecli',
'wallet_createwallet.py --descriptors',
'wallet_watchonly.py --legacy-wallet',
@@ -161,27 +162,27 @@ BASE_SCRIPTS = [
'wallet_reorgsrestore.py',
'interface_http.py',
'interface_rpc.py',
- 'rpc_psbt.py',
+ 'rpc_psbt.py --legacy-wallet',
'rpc_psbt.py --descriptors',
'rpc_users.py',
'rpc_whitelist.py',
'feature_proxy.py',
- 'rpc_signrawtransaction.py',
+ 'rpc_signrawtransaction.py --legacy-wallet',
'rpc_signrawtransaction.py --descriptors',
- 'wallet_groups.py',
+ 'wallet_groups.py --legacy-wallet',
'p2p_addrv2_relay.py',
'wallet_groups.py --descriptors',
'p2p_disconnect_ban.py',
'rpc_decodescript.py',
'rpc_blockchain.py',
'rpc_deprecated.py',
- 'wallet_disable.py',
+ 'wallet_disable.py --legacy-wallet',
'wallet_disable.py --descriptors',
'p2p_addr_relay.py',
'p2p_getaddr_caching.py',
'p2p_getdata.py',
'rpc_net.py',
- 'wallet_keypool.py',
+ 'wallet_keypool.py --legacy-wallet',
'wallet_keypool.py --descriptors',
'wallet_descriptor.py --descriptors',
'p2p_nobloomfilter_messages.py',
@@ -195,70 +196,72 @@ BASE_SCRIPTS = [
'p2p_invalid_tx.py',
'feature_assumevalid.py',
'example_test.py',
- 'wallet_txn_doublespend.py',
+ 'wallet_txn_doublespend.py --legacy-wallet',
'wallet_txn_doublespend.py --descriptors',
- 'feature_backwards_compatibility.py',
+ 'feature_backwards_compatibility.py --legacy-wallet',
'feature_backwards_compatibility.py --descriptors',
'wallet_txn_clone.py --mineblock',
'feature_notifications.py',
'rpc_getblockfilter.py',
'rpc_invalidateblock.py',
+ 'feature_utxo_set_hash.py',
'feature_rbf.py',
'mempool_packages.py',
'mempool_package_onemore.py',
- 'rpc_createmultisig.py',
+ 'rpc_createmultisig.py --legacy-wallet',
'rpc_createmultisig.py --descriptors',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
- 'wallet_importprunedfunds.py',
+ 'wallet_importprunedfunds.py --legacy-wallet',
'wallet_importprunedfunds.py --descriptors',
'p2p_leak_tx.py',
'p2p_eviction.py',
'rpc_signmessage.py',
'rpc_generateblock.py',
'rpc_generate.py',
- 'wallet_balance.py',
+ 'wallet_balance.py --legacy-wallet',
'wallet_balance.py --descriptors',
- 'feature_nulldummy.py',
+ 'feature_nulldummy.py --legacy-wallet',
'feature_nulldummy.py --descriptors',
'mempool_accept.py',
'mempool_expiry.py',
'wallet_import_rescan.py --legacy-wallet',
'wallet_import_with_label.py --legacy-wallet',
'wallet_importdescriptors.py --descriptors',
- 'wallet_upgradewallet.py',
+ 'wallet_upgradewallet.py --legacy-wallet',
'rpc_bind.py --ipv4',
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
'mining_basic.py',
'feature_signet.py',
- 'wallet_bumpfee.py',
+ 'wallet_bumpfee.py --legacy-wallet',
'wallet_bumpfee.py --descriptors',
'wallet_implicitsegwit.py --legacy-wallet',
'rpc_named_arguments.py',
- 'wallet_listsinceblock.py',
+ 'wallet_listsinceblock.py --legacy-wallet',
'wallet_listsinceblock.py --descriptors',
'wallet_listdescriptors.py --descriptors',
'p2p_leak.py',
- 'wallet_encryption.py',
+ 'wallet_encryption.py --legacy-wallet',
'wallet_encryption.py --descriptors',
'feature_dersig.py',
'feature_cltv.py',
'rpc_uptime.py',
- 'wallet_resendwallettransactions.py',
+ 'wallet_resendwallettransactions.py --legacy-wallet',
'wallet_resendwallettransactions.py --descriptors',
- 'wallet_fallbackfee.py',
+ 'wallet_fallbackfee.py --legacy-wallet',
'wallet_fallbackfee.py --descriptors',
'rpc_dumptxoutset.py',
'feature_minchainwork.py',
'rpc_estimatefee.py',
'rpc_getblockstats.py',
- 'wallet_create_tx.py',
- 'wallet_send.py',
+ 'wallet_create_tx.py --legacy-wallet',
+ 'wallet_send.py --legacy-wallet',
+ 'wallet_send.py --descriptors',
'wallet_create_tx.py --descriptors',
'p2p_fingerprint.py',
'feature_uacomment.py',
- 'wallet_coinbase_category.py',
+ 'wallet_coinbase_category.py --legacy-wallet',
'wallet_coinbase_category.py --descriptors',
'feature_filelock.py',
'feature_loadblock.py',
@@ -287,6 +290,7 @@ BASE_SCRIPTS = [
'feature_help.py',
'feature_shutdown.py',
'p2p_ibd_txrelay.py',
+ 'feature_blockfilterindex_prune.py'
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 8a1af24dcf..28103793df 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -183,11 +183,13 @@ class ToolWalletTest(BitcoinTestFramework):
def test_invalid_tool_commands_and_args(self):
self.log.info('Testing that various invalid commands raise with specific error messages')
- self.assert_raises_tool_error('Invalid command: foo', 'foo')
+ self.assert_raises_tool_error("Error parsing command line arguments: Invalid command 'foo'", 'foo')
# `bitcoin-wallet help` raises an error. Use `bitcoin-wallet -help`.
- self.assert_raises_tool_error('Invalid command: help', 'help')
- self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
+ self.assert_raises_tool_error("Error parsing command line arguments: Invalid command 'help'", 'help')
+ self.assert_raises_tool_error('Error: Additional arguments provided (create). Methods do not take arguments. Please refer to `-help`.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
+ self.assert_raises_tool_error('No method provided. Run `bitcoin-wallet -help` for valid methods.')
+ self.assert_raises_tool_error('Wallet name must be provided when creating a new wallet.', 'create')
locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
error = 'Error initializing wallet database environment "{}"!'.format(locked_dir)
if self.options.descriptors:
@@ -348,7 +350,8 @@ class ToolWalletTest(BitcoinTestFramework):
self.log.info('Checking createfromdump')
self.do_tool_createfromdump("load", "wallet.dump")
- self.do_tool_createfromdump("load-bdb", "wallet.dump", "bdb")
+ if self.is_bdb_compiled():
+ self.do_tool_createfromdump("load-bdb", "wallet.dump", "bdb")
if self.is_sqlite_compiled():
self.do_tool_createfromdump("load-sqlite", "wallet.dump", "sqlite")
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 229c134a4b..bc4fa90e83 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -65,7 +65,6 @@ def assert_balances(node, mine):
class AvoidReuseTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 2
# This test isn't testing txn relay/timing, so set whitelist on the
# peers for instant txn relay. This speeds up the test run time 2-3x.
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index cf3317121f..16a0a50b07 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -17,7 +17,6 @@ from test_framework.wallet_util import bytes_to_wif, generate_wif_key
class CreateWalletTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 1de41a5f96..1e032bdd6c 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -23,11 +23,14 @@ class WalletDescriptorTest(BitcoinTestFramework):
self.skip_if_no_sqlite()
def run_test(self):
- # Make a legacy wallet and check it is BDB
- self.nodes[0].createwallet(wallet_name="legacy1", descriptors=False)
- wallet_info = self.nodes[0].getwalletinfo()
- assert_equal(wallet_info['format'], 'bdb')
- self.nodes[0].unloadwallet("legacy1")
+ if self.is_bdb_compiled():
+ # Make a legacy wallet and check it is BDB
+ self.nodes[0].createwallet(wallet_name="legacy1", descriptors=False)
+ wallet_info = self.nodes[0].getwalletinfo()
+ assert_equal(wallet_info['format'], 'bdb')
+ self.nodes[0].unloadwallet("legacy1")
+ else:
+ self.log.warning("Skipping BDB test")
# Make a descriptor wallet
self.log.info("Making a descriptor wallet")
@@ -148,5 +151,62 @@ class WalletDescriptorTest(BitcoinTestFramework):
nopriv_rpc = self.nodes[0].get_wallet_rpc('desc_no_priv')
assert_raises_rpc_error(-4, 'This wallet has no available keys', nopriv_rpc.getnewaddress)
+ self.log.info("Test descriptor exports")
+ self.nodes[0].createwallet(wallet_name='desc_export', descriptors=True)
+ exp_rpc = self.nodes[0].get_wallet_rpc('desc_export')
+ self.nodes[0].createwallet(wallet_name='desc_import', disable_private_keys=True, descriptors=True)
+ imp_rpc = self.nodes[0].get_wallet_rpc('desc_import')
+
+ addr_types = [('legacy', False, 'pkh(', '44\'/1\'/0\'', -13),
+ ('p2sh-segwit', False, 'sh(wpkh(', '49\'/1\'/0\'', -14),
+ ('bech32', False, 'wpkh(', '84\'/1\'/0\'', -13),
+ ('legacy', True, 'pkh(', '44\'/1\'/0\'', -13),
+ ('p2sh-segwit', True, 'sh(wpkh(', '49\'/1\'/0\'', -14),
+ ('bech32', True, 'wpkh(', '84\'/1\'/0\'', -13)]
+
+ for addr_type, internal, desc_prefix, deriv_path, int_idx in addr_types:
+ int_str = 'internal' if internal else 'external'
+
+ self.log.info("Testing descriptor address type for {} {}".format(addr_type, int_str))
+ if internal:
+ addr = exp_rpc.getrawchangeaddress(address_type=addr_type)
+ else:
+ addr = exp_rpc.getnewaddress(address_type=addr_type)
+ desc = exp_rpc.getaddressinfo(addr)['parent_desc']
+ assert_equal(desc_prefix, desc[0:len(desc_prefix)])
+ idx = desc.index('/') + 1
+ assert_equal(deriv_path, desc[idx:idx + 9])
+ if internal:
+ assert_equal('1', desc[int_idx])
+ else:
+ assert_equal('0', desc[int_idx])
+
+ self.log.info("Testing the same descriptor is returned for address type {} {}".format(addr_type, int_str))
+ for i in range(0, 10):
+ if internal:
+ addr = exp_rpc.getrawchangeaddress(address_type=addr_type)
+ else:
+ addr = exp_rpc.getnewaddress(address_type=addr_type)
+ test_desc = exp_rpc.getaddressinfo(addr)['parent_desc']
+ assert_equal(desc, test_desc)
+
+ self.log.info("Testing import of exported {} descriptor".format(addr_type))
+ imp_rpc.importdescriptors([{
+ 'desc': desc,
+ 'active': True,
+ 'next_index': 11,
+ 'timestamp': 'now',
+ 'internal': internal
+ }])
+
+ for i in range(0, 10):
+ if internal:
+ exp_addr = exp_rpc.getrawchangeaddress(address_type=addr_type)
+ imp_addr = imp_rpc.getrawchangeaddress(address_type=addr_type)
+ else:
+ exp_addr = exp_rpc.getnewaddress(address_type=addr_type)
+ imp_addr = imp_rpc.getnewaddress(address_type=addr_type)
+ assert_equal(exp_addr, imp_addr)
+
if __name__ == '__main__':
WalletDescriptorTest().main ()
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index e5c4f12f20..c0b76d960f 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -29,8 +29,9 @@ class WalletGroupTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
+ self.log.info("Setting up")
# Mine some coins
- self.nodes[0].generate(110)
+ self.nodes[0].generate(101)
# Get some addresses from the two nodes
addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
@@ -48,6 +49,7 @@ class WalletGroupTest(BitcoinTestFramework):
# - node[1] should pick one 0.5 UTXO and leave the rest
# - node[2] should pick one (1.0 + 0.5) UTXO group corresponding to a
# given address, and leave the rest
+ self.log.info("Test sending transactions picks one UTXO group and leaves the rest")
txid1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
tx1 = self.nodes[1].getrawtransaction(txid1, True)
# txid1 should have 1 input and 2 outputs
@@ -70,7 +72,7 @@ class WalletGroupTest(BitcoinTestFramework):
assert_approx(v[0], vexp=0.2, vspan=0.0001)
assert_approx(v[1], vexp=1.3, vspan=0.0001)
- # Test 'avoid partial if warranted, even if disabled'
+ self.log.info("Test avoiding partial spends if warranted, even if avoidpartialspends is disabled")
self.sync_all()
self.nodes[0].generate(1)
# Nodes 1-2 now have confirmed UTXOs (letters denote destinations):
@@ -104,7 +106,7 @@ class WalletGroupTest(BitcoinTestFramework):
assert_equal(input_addrs[0], input_addrs[1])
# Node 2 enforces avoidpartialspends so needs no checking here
- # Test wallet option maxapsfee with Node 3
+ self.log.info("Test wallet option maxapsfee")
addr_aps = self.nodes[3].getnewaddress()
self.nodes[0].sendtoaddress(addr_aps, 1.0)
self.nodes[0].sendtoaddress(addr_aps, 1.0)
@@ -131,6 +133,7 @@ class WalletGroupTest(BitcoinTestFramework):
# Test wallet option maxapsfee with node 4, which sets maxapsfee
# 1 sat higher, crossing the threshold from non-grouped to grouped.
+ self.log.info("Test wallet option maxapsfee threshold from non-grouped to grouped")
addr_aps3 = self.nodes[4].getnewaddress()
[self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)]
self.nodes[0].generate(1)
@@ -147,8 +150,7 @@ class WalletGroupTest(BitcoinTestFramework):
self.sync_all()
self.nodes[0].generate(1)
- # Fill node2's wallet with 10000 outputs corresponding to the same
- # scriptPubKey
+ self.log.info("Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey")
for _ in range(5):
raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}])
tx = FromHex(CTransaction(), raw_tx)
@@ -158,12 +160,12 @@ class WalletGroupTest(BitcoinTestFramework):
signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex'])
self.nodes[0].sendrawtransaction(signed_tx['hex'])
self.nodes[0].generate(1)
-
- self.sync_all()
+ self.sync_all()
# Check that we can create a transaction that only requires ~100 of our
# utxos, without pulling in all outputs and creating a transaction that
# is way too big.
+ self.log.info("Test creating txn that only requires ~100 of our UTXOs without pulling in all outputs")
assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5)
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index 883b97561e..551eb72720 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -138,13 +138,13 @@ class WalletLabelsTest(BitcoinTestFramework):
node.createwallet(wallet_name='watch_only', disable_private_keys=True)
wallet_watch_only = node.get_wallet_rpc('watch_only')
BECH32_VALID = {
- '✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn2cjv3',
- '✔️_VER16_PROG03': 'bcrt1sqqqqqjq8pdp',
- '✔️_VER16_PROB02': 'bcrt1sqqqqqjq8pv',
+ '✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn',
+ '✔️_VER16_PROG03': 'bcrt1sqqqqq8uhdgr',
+ '✔️_VER16_PROB02': 'bcrt1sqqqq4wstyw',
}
BECH32_INVALID = {
- '❌_VER15_PROG41': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzc7xyq',
- '❌_VER16_PROB01': 'bcrt1sqqpl9r5c',
+ '❌_VER15_PROG41': 'bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8',
+ '❌_VER16_PROB01': 'bcrt1sqq5r4036',
}
for l in BECH32_VALID:
ad = BECH32_VALID[l]
diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
index 9f8c341bc7..8d02949ff4 100755
--- a/test/functional/wallet_listdescriptors.py
+++ b/test/functional/wallet_listdescriptors.py
@@ -50,6 +50,22 @@ class ListDescriptorsTest(BitcoinTestFramework):
assert item['range'] == [0, 0]
assert item['timestamp'] is not None
+ self.log.info('Test descriptors with hardened derivations are listed in importable form.')
+ xprv = 'tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg'
+ xpub_acc = 'tpubDCMVLhErorrAGfApiJSJzEKwqeaf2z3NrkVMxgYQjZLzMjXMBeRw2muGNYbvaekAE8rUFLftyEar4LdrG2wXyyTJQZ26zptmeTEjPTaATts'
+ hardened_path = '/84\'/1\'/0\''
+ wallet = node.get_wallet_rpc('w2')
+ wallet.importdescriptors([{
+ 'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'),
+ 'timestamp': 1296688602,
+ }])
+ expected = {'desc': descsum_create('wpkh([80002067' + hardened_path + ']' + xpub_acc + '/0/*)'),
+ 'timestamp': 1296688602,
+ 'active': False,
+ 'range': [0, 0],
+ 'next': 0}
+ assert_equal([expected], wallet.listdescriptors())
+
self.log.info('Test non-active non-range combo descriptor')
node.createwallet(wallet_name='w4', blank=True, descriptors=True)
wallet = node.get_wallet_rpc('w4')
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 9835c5a2af..880341fdd9 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -8,6 +8,7 @@ from decimal import Decimal, getcontext
from itertools import product
from test_framework.authproxy import JSONRPCException
+from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -168,49 +169,91 @@ class WalletSendTest(BitcoinTestFramework):
self.nodes[1].createwallet(wallet_name="w1")
w1 = self.nodes[1].get_wallet_rpc("w1")
# w2 contains the private keys for w3
- self.nodes[1].createwallet(wallet_name="w2")
+ self.nodes[1].createwallet(wallet_name="w2", blank=True)
w2 = self.nodes[1].get_wallet_rpc("w2")
+ xpriv = "tprv8ZgxMBicQKsPfHCsTwkiM1KT56RXbGGTqvc2hgqzycpwbHqqpcajQeMRZoBD35kW4RtyCemu6j34Ku5DEspmgjKdt2qe4SvRch5Kk8B8A2v"
+ xpub = "tpubD6NzVbkrYhZ4YkEfMbRJkQyZe7wTkbTNRECozCtJPtdLRn6cT1QKb8yHjwAPcAr26eHBFYs5iLiFFnCbwPRsncCKUKCfubHDMGKzMVcN1Jg"
+ if self.options.descriptors:
+ w2.importdescriptors([{
+ "desc": descsum_create("wpkh(" + xpriv + "/0/0/*)"),
+ "timestamp": "now",
+ "range": [0, 100],
+ "active": True
+ },{
+ "desc": descsum_create("wpkh(" + xpriv + "/0/1/*)"),
+ "timestamp": "now",
+ "range": [0, 100],
+ "active": True,
+ "internal": True
+ }])
+ else:
+ w2.sethdseed(True)
+
# w3 is a watch-only wallet, based on w2
self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
w3 = self.nodes[1].get_wallet_rpc("w3")
- for _ in range(3):
- a2_receive = w2.getnewaddress()
- a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
- res = w3.importmulti([{
- "desc": w2.getaddressinfo(a2_receive)["desc"],
+ if self.options.descriptors:
+ # Match the privkeys in w2 for descriptors
+ res = w3.importdescriptors([{
+ "desc": descsum_create("wpkh(" + xpub + "/0/0/*)"),
"timestamp": "now",
+ "range": [0, 100],
"keypool": True,
+ "active": True,
"watchonly": True
},{
- "desc": w2.getaddressinfo(a2_change)["desc"],
+ "desc": descsum_create("wpkh(" + xpub + "/0/1/*)"),
"timestamp": "now",
+ "range": [0, 100],
"keypool": True,
+ "active": True,
"internal": True,
"watchonly": True
}])
assert_equal(res, [{"success": True}, {"success": True}])
- w0.sendtoaddress(a2_receive, 10) # fund w3
- self.nodes[0].generate(1)
- self.sync_blocks()
-
- # w4 has private keys enabled, but only contains watch-only keys (from w2)
- self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
- w4 = self.nodes[1].get_wallet_rpc("w4")
for _ in range(3):
a2_receive = w2.getnewaddress()
- res = w4.importmulti([{
- "desc": w2.getaddressinfo(a2_receive)["desc"],
- "timestamp": "now",
- "keypool": False,
- "watchonly": True
- }])
- assert_equal(res, [{"success": True}])
+ if not self.options.descriptors:
+ # Because legacy wallets use exclusively hardened derivation, we can't do a ranged import like we do for descriptors
+ a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
+ res = w3.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "watchonly": True
+ },{
+ "desc": w2.getaddressinfo(a2_change)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "internal": True,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}, {"success": True}])
- w0.sendtoaddress(a2_receive, 10) # fund w4
+ w0.sendtoaddress(a2_receive, 10) # fund w3
self.nodes[0].generate(1)
self.sync_blocks()
+ if not self.options.descriptors:
+ # w4 has private keys enabled, but only contains watch-only keys (from w2)
+ # This is legacy wallet behavior only as descriptor wallets don't allow watchonly and non-watchonly things in the same wallet.
+ self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
+ w4 = self.nodes[1].get_wallet_rpc("w4")
+ for _ in range(3):
+ a2_receive = w2.getnewaddress()
+ res = w4.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": False,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}])
+
+ w0.sendtoaddress(a2_receive, 10) # fund w4
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
self.log.info("Send to address...")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True)
@@ -241,11 +284,15 @@ class WalletSendTest(BitcoinTestFramework):
res = w2.walletprocesspsbt(res["psbt"])
assert res["complete"]
- self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
- self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
- res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
- res = w2.walletprocesspsbt(res["psbt"])
- assert res["complete"]
+ if not self.options.descriptors:
+ # Descriptor wallets do not allow mixed watch-only and non-watch-only things in the same wallet.
+ # This is specifically testing that w4 ignores its own private keys and creates a psbt with send
+ # which is not something that needs to be tested in descriptor wallets.
+ self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
+ self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
self.log.info("Create OP_RETURN...")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
new file mode 100755
index 0000000000..9dd080dca9
--- /dev/null
+++ b/test/functional/wallet_signer.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test external signer.
+
+Verify that a bitcoind node can use an external signer command
+"""
+import os
+import platform
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+
+class SignerTest(BitcoinTestFramework):
+ def mock_signer_path(self):
+ path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py')
+ if platform.system() == "Windows":
+ return "py " + path
+ else:
+ return path
+
+ def set_test_params(self):
+ self.num_nodes = 4
+
+ self.extra_args = [
+ [],
+ [f"-signer={self.mock_signer_path()}", '-keypool=10'],
+ [f"-signer={self.mock_signer_path()}", '-keypool=10'],
+ ["-signer=fake.py"],
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_external_signer()
+
+ def set_mock_result(self, node, res):
+ with open(os.path.join(node.cwd, "mock_result"), "w", encoding="utf8") as f:
+ f.write(res)
+
+ def clear_mock_result(self, node):
+ os.remove(os.path.join(node.cwd, "mock_result"))
+
+ def run_test(self):
+ self.log.debug(f"-signer={self.mock_signer_path()}")
+
+ assert_raises_rpc_error(-4, 'Error: restart bitcoind with -signer=<cmd>',
+ self.nodes[0].enumeratesigners
+ )
+
+ # Handle script missing:
+ assert_raises_rpc_error(-1, 'execve failed: No such file or directory',
+ self.nodes[3].enumeratesigners
+ )
+
+ # Handle error thrown by script
+ self.set_mock_result(self.nodes[1], "2")
+ assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
+ self.nodes[1].enumeratesigners
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "error": "fingerprint not found"}]')
+ assert_raises_rpc_error(-4, 'fingerprint not found',
+ self.nodes[1].enumeratesigners
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ # Create new wallets for an external signer.
+ # disable_private_keys and descriptors must be true:
+ assert_raises_rpc_error(-4, "Private keys must be disabled when using an external signer", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=False, descriptors=True, external_signer=True)
+ if self.is_bdb_compiled():
+ assert_raises_rpc_error(-4, "Descriptor support must be enabled when using an external signer", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=True, descriptors=False, external_signer=True)
+ else:
+ assert_raises_rpc_error(-4, "Compiled without bdb support (required for legacy wallets)", self.nodes[1].createwallet, wallet_name='not_hww', disable_private_keys=True, descriptors=False, external_signer=True)
+
+ self.nodes[1].createwallet(wallet_name='hww', disable_private_keys=True, descriptors=True, external_signer=True)
+ hww = self.nodes[1].get_wallet_rpc('hww')
+
+ result = hww.enumeratesigners()
+ assert_equal(len(result['signers']), 2)
+ assert_equal(result['signers'][0]["fingerprint"], "00000001")
+ assert_equal(result['signers'][0]["name"], "trezor_t")
+
+ # Flag can't be set afterwards (could be added later for non-blank descriptor based watch-only wallets)
+ self.nodes[1].createwallet(wallet_name='not_hww', disable_private_keys=True, descriptors=True, external_signer=False)
+ not_hww = self.nodes[1].get_wallet_rpc('not_hww')
+ assert_raises_rpc_error(-8, "Wallet flag is immutable: external_signer", not_hww.setwalletflag, "external_signer", True)
+
+ # assert_raises_rpc_error(-4, "Multiple signers found, please specify which to use", wallet_name='not_hww', disable_private_keys=True, descriptors=True, external_signer=True)
+
+ # TODO: Handle error thrown by script
+ # self.set_mock_result(self.nodes[1], "2")
+ # assert_raises_rpc_error(-1, 'Unable to parse JSON',
+ # self.nodes[1].createwallet, wallet_name='not_hww2', disable_private_keys=True, descriptors=True, external_signer=False
+ # )
+ # self.clear_mock_result(self.nodes[1])
+
+ assert_equal(hww.getwalletinfo()["keypoolsize"], 30)
+
+ address1 = hww.getnewaddress(address_type="bech32")
+ assert_equal(address1, "bcrt1qm90ugl4d48jv8n6e5t9ln6t9zlpm5th68x4f8g")
+ address_info = hww.getaddressinfo(address1)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/84'/1'/0'/0/0")
+
+ address2 = hww.getnewaddress(address_type="p2sh-segwit")
+ assert_equal(address2, "2N2gQKzjUe47gM8p1JZxaAkTcoHPXV6YyVp")
+ address_info = hww.getaddressinfo(address2)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/49'/1'/0'/0/0")
+
+ address3 = hww.getnewaddress(address_type="legacy")
+ assert_equal(address3, "n1LKejAadN6hg2FrBXoU1KrwX4uK16mco9")
+ address_info = hww.getaddressinfo(address3)
+ assert_equal(address_info['solvable'], True)
+ assert_equal(address_info['ismine'], True)
+ assert_equal(address_info['hdkeypath'], "m/44'/1'/0'/0/0")
+
+ self.log.info('Test signerdisplayaddress')
+ result = hww.signerdisplayaddress(address1)
+ assert_equal(result, {"address": address1})
+
+ # Handle error thrown by script
+ self.set_mock_result(self.nodes[1], "2")
+ assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
+ hww.signerdisplayaddress, address1
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ self.log.info('Prepare mock PSBT')
+ self.nodes[0].sendtoaddress(address1, 1)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # Load private key into wallet to generate a signed PSBT for the mock
+ self.nodes[1].createwallet(wallet_name="mock", disable_private_keys=False, blank=True, descriptors=True)
+ mock_wallet = self.nodes[1].get_wallet_rpc("mock")
+ assert mock_wallet.getwalletinfo()['private_keys_enabled']
+
+ result = mock_wallet.importdescriptors([{
+ "desc": "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0",
+ "timestamp": 0,
+ "range": [0,1],
+ "internal": False,
+ "active": True
+ },
+ {
+ "desc": "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh",
+ "timestamp": 0,
+ "range": [0, 0],
+ "internal": True,
+ "active": True
+ }])
+ assert_equal(result[0], {'success': True})
+ assert_equal(result[1], {'success': True})
+ assert_equal(mock_wallet.getwalletinfo()["txcount"], 1)
+ dest = self.nodes[0].getnewaddress(address_type='bech32')
+ mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {}, True)['psbt']
+ mock_psbt_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt, sign=True, sighashtype="ALL", bip32derivs=True)
+ mock_psbt_final = mock_wallet.finalizepsbt(mock_psbt_signed["psbt"])
+ mock_tx = mock_psbt_final["hex"]
+ assert(mock_wallet.testmempoolaccept([mock_tx])[0]["allowed"])
+
+ # # Create a new wallet and populate with specific public keys, in order
+ # # to work with the mock signed PSBT.
+ # self.nodes[1].createwallet(wallet_name="hww4", disable_private_keys=True, descriptors=True, external_signer=True)
+ # hww4 = self.nodes[1].get_wallet_rpc("hww4")
+ #
+ # descriptors = [{
+ # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/*)#x30uthjs",
+ # "timestamp": "now",
+ # "range": [0, 1],
+ # "internal": False,
+ # "watchonly": True,
+ # "active": True
+ # },
+ # {
+ # "desc": "wpkh([00000001/84'/1'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)#h92akzzg",
+ # "timestamp": "now",
+ # "range": [0, 0],
+ # "internal": True,
+ # "watchonly": True,
+ # "active": True
+ # }]
+
+ # result = hww4.importdescriptors(descriptors)
+ # assert_equal(result[0], {'success': True})
+ # assert_equal(result[1], {'success': True})
+ assert_equal(hww.getwalletinfo()["txcount"], 1)
+
+ assert(hww.testmempoolaccept([mock_tx])[0]["allowed"])
+
+ with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f:
+ f.write(mock_psbt_signed["psbt"])
+
+ self.log.info('Test send using hww1')
+
+ res = hww.send(outputs={dest:0.5},options={"add_to_wallet": False})
+ assert(res["complete"])
+ assert_equal(res["hex"], mock_tx)
+
+ # # Handle error thrown by script
+ # self.set_mock_result(self.nodes[4], "2")
+ # assert_raises_rpc_error(-1, 'Unable to parse JSON',
+ # hww4.signerprocesspsbt, psbt_orig, "00000001"
+ # )
+ # self.clear_mock_result(self.nodes[4])
+
+if __name__ == '__main__':
+ SignerTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 893a2d9617..6fc1d13c53 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -11,9 +11,10 @@ from test_framework.util import (
)
from test_framework.messages import CTransaction, COIN
+
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 3
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -38,9 +39,8 @@ class TxnMallTest(BitcoinTestFramework):
# All nodes should start with 1,250 BTC:
starting_balance = 1250
- for i in range(4):
+ for i in range(3):
assert_equal(self.nodes[i].getbalance(), starting_balance)
- self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress!
self.nodes[0].settxfee(.001)
@@ -139,5 +139,6 @@ class TxnMallTest(BitcoinTestFramework):
expected -= 50
assert_equal(self.nodes[0].getbalance(), expected)
+
if __name__ == '__main__':
TxnMallTest().main()
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index c7f7a8546a..0cb7328948 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -11,9 +11,10 @@ from test_framework.util import (
find_output,
)
+
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 3
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -39,9 +40,8 @@ class TxnMallTest(BitcoinTestFramework):
for n in self.nodes:
assert n.getblockchaininfo()["initialblockdownload"] == False
- for i in range(4):
+ for i in range(3):
assert_equal(self.nodes[i].getbalance(), starting_balance)
- self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
# Assign coins to foo and bar addresses:
node0_address_foo = self.nodes[0].getnewaddress()
@@ -136,5 +136,6 @@ class TxnMallTest(BitcoinTestFramework):
# Node1's balance should be its initial balance (1250 for 25 block rewards) plus the doublespend:
assert_equal(self.nodes[1].getbalance(), 1250 + 1240)
+
if __name__ == '__main__':
TxnMallTest().main()
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index d0bb6135a8..fbc0f995d2 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -57,6 +57,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ self.skip_if_no_bdb()
self.skip_if_no_previous_releases()
def setup_network(self):
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
index b0c41b2738..c345c382d0 100755
--- a/test/functional/wallet_watchonly.py
+++ b/test/functional/wallet_watchonly.py
@@ -2,7 +2,7 @@
# 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.
+"""Test createwallet watchonly arguments.
"""
from test_framework.test_framework import BitcoinTestFramework
@@ -14,7 +14,6 @@ from test_framework.util import (
class CreateWalletWatchonlyTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = False
self.num_nodes = 1
def skip_test_if_missing_module(self):
@@ -50,6 +49,11 @@ class CreateWalletWatchonlyTest(BitcoinTestFramework):
assert_equal(len(wo_wallet.listtransactions()), 1)
assert_equal(wo_wallet.getbalance(include_watchonly=False), 0)
+ self.log.info('Test sending from a watch-only wallet raises RPC error')
+ msg = "Error: Private keys are disabled for this wallet"
+ assert_raises_rpc_error(-4, msg, wo_wallet.sendtoaddress, a1, 0.1)
+ assert_raises_rpc_error(-4, msg, wo_wallet.sendmany, amounts={a1: 0.1})
+
self.log.info('Testing listreceivedbyaddress watch-only defaults')
result = wo_wallet.listreceivedbyaddress()
assert_equal(len(result), 1)
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index aa0aa11d15..eeff7a4515 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -27,7 +27,7 @@ def get_fuzz_env(*, target, source_dir):
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description='''Run the fuzz targets with all inputs from the seed_dir once.''',
+ description='''Run the fuzz targets with all inputs from the corpus_dir once.''',
)
parser.add_argument(
"-l",
@@ -54,8 +54,8 @@ def main():
help='How many targets to merge or execute in parallel.',
)
parser.add_argument(
- 'seed_dir',
- help='The seed corpus to run on (must contain subfolders for each fuzz target).',
+ 'corpus_dir',
+ help='The corpus to run on (must contain subfolders for each fuzz target).',
)
parser.add_argument(
'target',
@@ -64,15 +64,15 @@ def main():
)
parser.add_argument(
'--m_dir',
- help='Merge inputs from this directory into the seed_dir.',
+ help='Merge inputs from this directory into the corpus_dir.',
)
parser.add_argument(
'-g',
'--generate',
action='store_true',
- help='Create new corpus seeds (or extend the existing ones) by running'
+ help='Create new corpus (or extend the existing ones) by running'
' the given targets for a finite number of times. Outputs them to'
- ' the passed seed_dir.'
+ ' the passed corpus_dir.'
)
args = parser.parse_args()
@@ -119,19 +119,19 @@ def main():
logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))
if not args.generate:
- test_list_seedless = []
+ test_list_missing_corpus = []
for t in test_list_selection:
- corpus_path = os.path.join(args.seed_dir, t)
+ corpus_path = os.path.join(args.corpus_dir, t)
if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0:
- test_list_seedless.append(t)
- test_list_seedless.sort()
- if test_list_seedless:
+ test_list_missing_corpus.append(t)
+ test_list_missing_corpus.sort()
+ if test_list_missing_corpus:
logging.info(
- "Fuzzing harnesses lacking a seed corpus: {}".format(
- " ".join(test_list_seedless)
+ "Fuzzing harnesses lacking a corpus: {}".format(
+ " ".join(test_list_missing_corpus)
)
)
- logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets")
+ logging.info("Please consider adding a fuzz corpus at https://github.com/bitcoin-core/qa-assets")
try:
help_output = subprocess.run(
@@ -154,18 +154,18 @@ def main():
with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
if args.generate:
- return generate_corpus_seeds(
+ return generate_corpus(
fuzz_pool=fuzz_pool,
src_dir=config['environment']['SRCDIR'],
build_dir=config["environment"]["BUILDDIR"],
- seed_dir=args.seed_dir,
+ corpus_dir=args.corpus_dir,
targets=test_list_selection,
)
if args.m_dir:
merge_inputs(
fuzz_pool=fuzz_pool,
- corpus=args.seed_dir,
+ corpus=args.corpus_dir,
test_list=test_list_selection,
src_dir=config['environment']['SRCDIR'],
build_dir=config["environment"]["BUILDDIR"],
@@ -175,7 +175,7 @@ def main():
run_once(
fuzz_pool=fuzz_pool,
- corpus=args.seed_dir,
+ corpus=args.corpus_dir,
test_list=test_list_selection,
src_dir=config['environment']['SRCDIR'],
build_dir=config["environment"]["BUILDDIR"],
@@ -183,13 +183,13 @@ def main():
)
-def generate_corpus_seeds(*, fuzz_pool, src_dir, build_dir, seed_dir, targets):
- """Generates new corpus seeds.
+def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
+ """Generates new corpus.
- Run {targets} without input, and outputs the generated corpus seeds to
- {seed_dir}.
+ Run {targets} without input, and outputs the generated corpus to
+ {corpus_dir}.
"""
- logging.info("Generating corpus seeds to {}".format(seed_dir))
+ logging.info("Generating corpus to {}".format(corpus_dir))
def job(command, t):
logging.debug("Running '{}'\n".format(" ".join(command)))
@@ -205,12 +205,12 @@ def generate_corpus_seeds(*, fuzz_pool, src_dir, build_dir, seed_dir, targets):
futures = []
for target in targets:
- target_seed_dir = os.path.join(seed_dir, target)
- os.makedirs(target_seed_dir, exist_ok=True)
+ target_corpus_dir = os.path.join(corpus_dir, target)
+ os.makedirs(target_corpus_dir, exist_ok=True)
command = [
os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
"-runs=100000",
- target_seed_dir,
+ target_corpus_dir,
]
futures.append(fuzz_pool.submit(job, command, target))
@@ -219,12 +219,14 @@ def generate_corpus_seeds(*, fuzz_pool, src_dir, build_dir, seed_dir, targets):
def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir):
- logging.info("Merge the inputs from the passed dir into the seed_dir. Passed dir {}".format(merge_dir))
+ logging.info("Merge the inputs from the passed dir into the corpus_dir. Passed dir {}".format(merge_dir))
jobs = []
for t in test_list:
args = [
os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
'-merge=1',
+ '-shuffle=0',
+ '-prefer_small=1',
'-use_value_profile=1', # Also done by oss-fuzz https://github.com/google/oss-fuzz/issues/1406#issuecomment-387790487
os.path.join(corpus, t),
os.path.join(merge_dir, t),
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index c4ad00e954..0b15f99448 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -11,6 +11,7 @@ export LC_ALL=C
EXPECTED_CIRCULAR_DEPENDENCIES=(
"chainparamsbase -> util/system -> chainparamsbase"
"index/txindex -> validation -> index/txindex"
+ "index/blockfilterindex -> validation -> index/blockfilterindex"
"policy/fees -> txmempool -> policy/fees"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
"qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
@@ -20,6 +21,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"txmempool -> validation -> txmempool"
"wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
+ "node/coinstats -> validation -> node/coinstats"
)
EXIT_CODE=0
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index fc0b86a297..bf7aeb5b4f 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -65,8 +65,6 @@ EXPECTED_BOOST_INCLUDES=(
boost/signals2/optional_last_value.hpp
boost/signals2/signal.hpp
boost/test/unit_test.hpp
- boost/thread/lock_types.hpp
- boost/thread/shared_mutex.hpp
)
for BOOST_INCLUDE in $(git grep '^#include <boost/' -- "*.cpp" "*.h" | cut -f2 -d: | cut -f2 -d'<' | cut -f1 -d'>' | sort -u); do
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
new file mode 100755
index 0000000000..c3b6ff3c98
--- /dev/null
+++ b/test/lint/lint-python-dead-code.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Find dead Python code.
+
+export LC_ALL=C
+
+if ! command -v vulture > /dev/null; then
+ echo "Skipping Python dead code linting since vulture is not installed. Install by running \"pip3 install vulture\""
+ exit 0
+fi
+
+# --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files.
+# Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden.
+if ! vulture \
+ --min-confidence 100 \
+ $(git ls-files -- "*.py"); then
+ echo "Python dead code detection found some issues"
+ exit 1
+fi
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 3a04418e8b..3fc9fac25c 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -28,6 +28,7 @@ race:BerkeleyBatch
race:BerkeleyDatabase
race:DatabaseBatch
race:leveldb::DBImpl::DeleteObsoleteFiles
+race:validation_chainstatemanager_tests
race:zmq::*
race:bitcoin-qt