aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml6
-rw-r--r--.cirrus.yml75
-rw-r--r--.fuzzbuzz.yml16
-rw-r--r--.gitignore4
-rw-r--r--.tx/config6
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--Makefile.am4
-rw-r--r--REVIEWERS18
-rw-r--r--build-aux/m4/ax_boost_base.m46
-rw-r--r--build-aux/m4/bitcoin_qt.m472
-rw-r--r--build-aux/m4/l_atomic.m46
-rw-r--r--build_msvc/README.md40
-rw-r--r--build_msvc/bitcoind/bitcoind.vcxproj3
-rw-r--r--build_msvc/common.init.vcxproj1
-rw-r--r--build_msvc/common.qt.init.vcxproj6
-rw-r--r--build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj2
-rwxr-xr-x[-rw-r--r--]build_msvc/msvc-autogen.py0
-rw-r--r--build_msvc/testconsensus/testconsensus.cpp2
-rwxr-xr-xci/lint/04_install.sh2
-rwxr-xr-xci/test/00_setup_env.sh6
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_android.sh0
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_arm.sh2
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_i686_centos.sh2
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_mac.sh6
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_mac_host.sh5
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_asan.sh4
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_fuzz.sh2
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_msan.sh24
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_fuzz_with_valgrind.sh0
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_msan.sh6
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_multiprocess.sh7
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_nowallet.sh2
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_qt5.sh2
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_tsan.sh4
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_native_valgrind.sh0
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_s390x.sh2
-rwxr-xr-x[-rw-r--r--]ci/test/00_setup_env_win64.sh2
-rwxr-xr-xci/test/04_install.sh12
-rw-r--r--configure.ac32
-rw-r--r--contrib/bitcoin-qt.pro22
-rw-r--r--contrib/debian/copyright2
-rwxr-xr-xcontrib/devtools/security-check.py147
-rwxr-xr-xcontrib/devtools/symbol-check.py47
-rwxr-xr-xcontrib/devtools/test-security-check.py12
-rwxr-xr-xcontrib/gitian-build.py2
-rw-r--r--[-rwxr-xr-x]contrib/gitian-descriptors/assign_DISTNAME2
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml5
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml9
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml5
-rw-r--r--contrib/guix/README.md118
-rwxr-xr-xcontrib/guix/guix-attest207
-rwxr-xr-xcontrib/guix/guix-build (renamed from contrib/guix/guix-build.sh)207
-rwxr-xr-xcontrib/guix/guix-clean83
-rwxr-xr-xcontrib/guix/guix-codesign392
-rwxr-xr-xcontrib/guix/guix-verify113
-rwxr-xr-x[-rw-r--r--]contrib/guix/libexec/build.sh108
-rwxr-xr-xcontrib/guix/libexec/codesign.sh103
-rw-r--r--contrib/guix/libexec/prelude.bash66
-rw-r--r--contrib/guix/manifest.scm453
-rw-r--r--contrib/guix/patches/gcc-8-sort-libtool-find-output.patch392
-rw-r--r--contrib/guix/patches/oscrypto-hard-code-openssl.patch13
-rw-r--r--contrib/macdeploy/README.md18
-rwxr-xr-x[-rw-r--r--]contrib/qos/tc.sh0
-rwxr-xr-xcontrib/seeds/generate-seeds.py117
-rw-r--r--contrib/seeds/nodes_main.txt548
-rw-r--r--contrib/seeds/nodes_test.txt19
-rw-r--r--contrib/shell/git-utils.bash14
-rw-r--r--contrib/shell/realpath.bash71
-rw-r--r--contrib/verify-commits/trusted-keys1
-rwxr-xr-xcontrib/windeploy/detached-sig-create.sh2
-rw-r--r--contrib/windeploy/win-codesign.cert177
-rw-r--r--depends/Makefile60
-rw-r--r--depends/README.md21
-rw-r--r--depends/builders/darwin.mk8
-rwxr-xr-xdepends/config.guess329
-rw-r--r--depends/config.site.in10
-rwxr-xr-xdepends/config.sub649
-rw-r--r--depends/funcs.mk10
-rwxr-xr-xdepends/gen_id74
-rw-r--r--depends/hosts/darwin.mk23
-rw-r--r--depends/hosts/linux.mk2
-rw-r--r--depends/packages/boost.mk3
-rw-r--r--depends/packages/libevent.mk14
-rw-r--r--depends/packages/native_cctools.mk9
-rw-r--r--depends/packages/native_clang.mk10
-rw-r--r--depends/packages/native_libdmg-hfsplus.mk2
-rw-r--r--depends/packages/native_libmultiprocess.mk4
-rw-r--r--depends/packages/native_libtapi.mk4
-rw-r--r--depends/packages/native_mac_alias.mk4
-rw-r--r--depends/packages/qt.mk31
-rw-r--r--depends/patches/libevent/0001-fix-windows-getaddrinfo.patch15
-rw-r--r--depends/patches/native_cctools/ld64_disable_threading.patch26
-rw-r--r--depends/patches/qt/fix_android_jni_static.patch2
-rw-r--r--depends/patches/qt/fix_bigsur_drawing.patch31
-rw-r--r--depends/patches/qt/fix_qpainter_non_determinism.patch63
-rw-r--r--depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch17
-rw-r--r--doc/README.md3
-rw-r--r--doc/bips.md8
-rw-r--r--doc/build-freebsd.md2
-rw-r--r--doc/build-netbsd.md2
-rw-r--r--doc/build-openbsd.md2
-rw-r--r--doc/build-osx.md10
-rw-r--r--doc/build-unix.md4
-rw-r--r--doc/build-windows.md23
-rw-r--r--doc/dependencies.md4
-rw-r--r--doc/descriptors.md18
-rw-r--r--doc/developer-notes.md2
-rw-r--r--doc/external-signer.md4
-rw-r--r--doc/files.md3
-rw-r--r--doc/fuzzing.md32
-rw-r--r--doc/multiprocess.md41
-rw-r--r--doc/reduce-memory.md10
-rw-r--r--doc/release-notes-20833.md12
-rw-r--r--doc/release-notes-20867.md11
-rw-r--r--doc/release-notes.md21
-rw-r--r--doc/release-notes/release-notes-0.21.1.md203
-rw-r--r--doc/release-process.md4
-rw-r--r--doc/tor.md4
-rw-r--r--doc/translation_process.md3
-rw-r--r--share/examples/bitcoin.conf18
-rwxr-xr-xshare/qt/extract_strings_qt.py2
-rw-r--r--src/Makefile.am75
-rw-r--r--src/Makefile.qt.include24
-rw-r--r--src/Makefile.test.include11
-rw-r--r--src/addrdb.cpp4
-rw-r--r--src/addrman.cpp19
-rw-r--r--src/addrman.h272
-rw-r--r--src/banman.cpp2
-rw-r--r--src/bench/addrman.cpp3
-rw-r--r--src/bench/block_assemble.cpp17
-rw-r--r--src/bench/coin_selection.cpp8
-rw-r--r--src/bench/duplicate_inputs.cpp3
-rw-r--r--src/bench/nanobench.h239
-rw-r--r--src/bench/verify_script.cpp2
-rw-r--r--src/bench/wallet_balance.cpp3
-rw-r--r--src/bitcoin-cli.cpp80
-rw-r--r--src/bitcoind.cpp17
-rw-r--r--src/chain.h2
-rw-r--r--src/chainparams.cpp67
-rw-r--r--src/chainparams.h18
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/chainparamsseeds.h1874
-rw-r--r--src/coins.h2
-rw-r--r--src/compat/assumptions.h5
-rw-r--r--src/compressor.cpp6
-rw-r--r--src/compressor.h20
-rw-r--r--src/consensus/params.h10
-rw-r--r--src/consensus/tx_verify.h2
-rw-r--r--src/crypto/muhash.cpp2
-rw-r--r--src/dbwrapper.cpp7
-rw-r--r--src/dummywallet.cpp1
-rw-r--r--src/external_signer.cpp (renamed from src/wallet/external_signer.cpp)39
-rw-r--r--src/external_signer.h (renamed from src/wallet/external_signer.h)33
-rw-r--r--src/httprpc.cpp5
-rw-r--r--src/httpserver.cpp9
-rw-r--r--src/i2p.cpp4
-rw-r--r--src/index/base.cpp53
-rw-r--r--src/index/base.h12
-rw-r--r--src/index/blockfilterindex.cpp16
-rw-r--r--src/index/coinstatsindex.cpp472
-rw-r--r--src/index/coinstatsindex.h61
-rw-r--r--src/index/txindex.cpp22
-rw-r--r--src/init.cpp403
-rw-r--r--src/init.h2
-rw-r--r--src/init/bitcoin-node.cpp45
-rw-r--r--src/init/bitcoind.cpp29
-rw-r--r--src/init/common.cpp167
-rw-r--r--src/init/common.h28
-rw-r--r--src/interfaces/README.md6
-rw-r--r--src/interfaces/echo.cpp18
-rw-r--r--src/interfaces/echo.h26
-rw-r--r--src/interfaces/init.cpp17
-rw-r--r--src/interfaces/init.h52
-rw-r--r--src/interfaces/ipc.h71
-rw-r--r--src/interfaces/wallet.h11
-rw-r--r--src/ipc/capnp/.gitignore2
-rw-r--r--src/ipc/capnp/echo.capnp17
-rw-r--r--src/ipc/capnp/init-types.h10
-rw-r--r--src/ipc/capnp/init.capnp20
-rw-r--r--src/ipc/capnp/protocol.cpp90
-rw-r--r--src/ipc/capnp/protocol.h17
-rw-r--r--src/ipc/exception.h20
-rw-r--r--src/ipc/interfaces.cpp77
-rw-r--r--src/ipc/process.cpp61
-rw-r--r--src/ipc/process.h42
-rw-r--r--src/ipc/protocol.h39
-rw-r--r--src/key.cpp4
-rw-r--r--src/key.h2
-rw-r--r--src/key_io.cpp15
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/mapport.cpp3
-rw-r--r--src/miner.cpp13
-rw-r--r--src/miner.h3
-rw-r--r--src/net.cpp188
-rw-r--r--src/net.h121
-rw-r--r--src/net_permissions.cpp42
-rw-r--r--src/net_permissions.h48
-rw-r--r--src/net_processing.cpp420
-rw-r--r--src/net_processing.h8
-rw-r--r--src/netaddress.cpp155
-rw-r--r--src/netaddress.h21
-rw-r--r--src/netbase.h3
-rw-r--r--src/node/README.md3
-rw-r--r--src/node/blockstorage.cpp566
-rw-r--r--src/node/blockstorage.h81
-rw-r--r--src/node/coinstats.cpp103
-rw-r--r--src/node/coinstats.h28
-rw-r--r--src/node/context.h3
-rw-r--r--src/node/interfaces.cpp15
-rw-r--r--src/node/transaction.cpp2
-rw-r--r--src/node/utxo_snapshot.h9
-rw-r--r--src/policy/feerate.cpp21
-rw-r--r--src/policy/feerate.h12
-rw-r--r--src/policy/fees.cpp48
-rw-r--r--src/policy/packages.h34
-rw-r--r--src/policy/policy.cpp4
-rw-r--r--src/protocol.cpp12
-rw-r--r--src/protocol.h11
-rw-r--r--src/pubkey.cpp53
-rw-r--r--src/pubkey.h48
-rw-r--r--src/qt/addressbookpage.cpp34
-rw-r--r--src/qt/addressbookpage.h1
-rw-r--r--src/qt/android/.gitignore9
-rw-r--r--src/qt/bitcoin.cpp33
-rw-r--r--src/qt/bitcoin.h8
-rw-r--r--src/qt/bitcoinamountfield.cpp2
-rw-r--r--src/qt/bitcoingui.cpp167
-rw-r--r--src/qt/bitcoingui.h13
-rw-r--r--src/qt/bitcoinstrings.cpp88
-rw-r--r--src/qt/clientmodel.cpp14
-rw-r--r--src/qt/clientmodel.h3
-rw-r--r--src/qt/coincontroldialog.cpp37
-rw-r--r--src/qt/coincontroldialog.h3
-rw-r--r--src/qt/forms/debugwindow.ui71
-rw-r--r--src/qt/forms/intro.ui53
-rw-r--r--src/qt/forms/modaloverlay.ui18
-rw-r--r--src/qt/forms/optionsdialog.ui8
-rw-r--r--src/qt/forms/psbtoperationsdialog.ui2
-rw-r--r--src/qt/forms/receiverequestdialog.ui8
-rw-r--r--src/qt/forms/sendcoinsdialog.ui8
-rw-r--r--src/qt/guiutil.cpp80
-rw-r--r--src/qt/guiutil.h92
-rw-r--r--src/qt/intro.cpp39
-rw-r--r--src/qt/intro.h7
-rw-r--r--src/qt/locale/bitcoin_en.ts1768
-rw-r--r--src/qt/locale/bitcoin_en.xlf5690
-rw-r--r--src/qt/modaloverlay.cpp5
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/optionsmodel.cpp5
-rw-r--r--src/qt/overviewpage.cpp15
-rw-r--r--src/qt/overviewpage.h5
-rw-r--r--src/qt/paymentserver.cpp11
-rw-r--r--src/qt/peertablemodel.cpp72
-rw-r--r--src/qt/peertablemodel.h40
-rw-r--r--src/qt/peertablesortproxy.cpp43
-rw-r--r--src/qt/peertablesortproxy.h25
-rw-r--r--src/qt/platformstyle.cpp27
-rw-r--r--src/qt/platformstyle.h7
-rw-r--r--src/qt/psbtoperationsdialog.cpp3
-rw-r--r--src/qt/qrimagewidget.cpp12
-rw-r--r--src/qt/qvaluecombobox.cpp2
-rw-r--r--src/qt/receivecoinsdialog.cpp24
-rw-r--r--src/qt/recentrequeststablemodel.cpp21
-rw-r--r--src/qt/rpcconsole.cpp379
-rw-r--r--src/qt/rpcconsole.h13
-rw-r--r--src/qt/sendcoinsdialog.cpp44
-rw-r--r--src/qt/sendcoinsdialog.h5
-rw-r--r--src/qt/sendcoinsentry.cpp13
-rw-r--r--src/qt/sendcoinsentry.h3
-rw-r--r--src/qt/signverifymessagedialog.cpp16
-rw-r--r--src/qt/signverifymessagedialog.h1
-rw-r--r--src/qt/test/addressbooktests.cpp3
-rw-r--r--src/qt/test/apptests.cpp11
-rw-r--r--src/qt/test/rpcnestedtests.cpp29
-rw-r--r--src/qt/test/wallettests.cpp29
-rw-r--r--src/qt/transactiondesc.cpp14
-rw-r--r--src/qt/transactiontablemodel.cpp56
-rw-r--r--src/qt/transactionview.cpp83
-rw-r--r--src/qt/transactionview.h7
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/walletcontroller.cpp7
-rw-r--r--src/qt/walletframe.cpp31
-rw-r--r--src/qt/walletframe.h8
-rw-r--r--src/qt/walletmodel.cpp26
-rw-r--r--src/qt/walletmodel.h5
-rw-r--r--src/qt/walletview.cpp8
-rw-r--r--src/random.cpp2
-rw-r--r--src/rest.cpp76
-rw-r--r--src/rpc/blockchain.cpp406
-rw-r--r--src/rpc/blockchain.h11
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/external_signer.cpp76
-rw-r--r--src/rpc/mining.cpp121
-rw-r--r--src/rpc/misc.cpp54
-rw-r--r--src/rpc/net.cpp222
-rw-r--r--src/rpc/net.h15
-rw-r--r--src/rpc/rawtransaction.cpp219
-rw-r--r--src/rpc/register.h5
-rw-r--r--src/rpc/request.h13
-rw-r--r--src/rpc/server.cpp7
-rw-r--r--src/rpc/util.cpp168
-rw-r--r--src/rpc/util.h38
-rw-r--r--src/scheduler.cpp3
-rw-r--r--src/script/bitcoinconsensus.cpp2
-rw-r--r--src/script/descriptor.cpp379
-rw-r--r--src/script/interpreter.cpp36
-rw-r--r--src/script/interpreter.h48
-rw-r--r--src/script/sigcache.cpp4
-rw-r--r--src/script/sigcache.h2
-rw-r--r--src/script/sign.cpp19
-rw-r--r--src/script/standard.cpp171
-rw-r--r--src/script/standard.h156
-rw-r--r--src/serialize.h40
-rw-r--r--src/shutdown.cpp14
-rw-r--r--src/shutdown.h5
-rw-r--r--src/signet.cpp4
-rw-r--r--src/streams.h2
-rw-r--r--src/sync.h4
-rw-r--r--src/test/addrman_tests.cpp13
-rw-r--r--src/test/allocator_tests.cpp9
-rw-r--r--src/test/amount_tests.cpp7
-rw-r--r--src/test/arith_uint256_tests.cpp6
-rw-r--r--src/test/base32_tests.cpp3
-rw-r--r--src/test/base58_tests.cpp2
-rw-r--r--src/test/base64_tests.cpp3
-rw-r--r--src/test/bech32_tests.cpp5
-rw-r--r--src/test/bip32_tests.cpp18
-rw-r--r--src/test/blockfilter_index_tests.cpp2
-rw-r--r--src/test/bswap_tests.cpp3
-rw-r--r--src/test/coinstatsindex_tests.cpp79
-rw-r--r--src/test/compilerbug_tests.cpp3
-rw-r--r--src/test/compress_tests.cpp18
-rw-r--r--src/test/crypto_tests.cpp8
-rw-r--r--src/test/data/tx_invalid.json46
-rw-r--r--src/test/data/tx_valid.json4
-rw-r--r--src/test/dbwrapper_tests.cpp46
-rw-r--r--src/test/denialofservice_tests.cpp114
-rw-r--r--src/test/descriptor_tests.cpp47
-rw-r--r--src/test/flatfile_tests.cpp8
-rw-r--r--src/test/fs_tests.cpp2
-rw-r--r--src/test/fuzz/addrman.cpp5
-rw-r--r--src/test/fuzz/banman.cpp10
-rw-r--r--src/test/fuzz/coins_view.cpp15
-rw-r--r--src/test/fuzz/connman.cpp13
-rwxr-xr-xsrc/test/fuzz/danger_link_all.sh28
-rw-r--r--src/test/fuzz/fee_rate.cpp4
-rw-r--r--src/test/fuzz/float.cpp35
-rw-r--r--src/test/fuzz/fuzz.cpp30
-rw-r--r--src/test/fuzz/i2p.cpp4
-rw-r--r--src/test/fuzz/integer.cpp4
-rw-r--r--src/test/fuzz/net.cpp22
-rw-r--r--src/test/fuzz/net_permissions.cpp4
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp43
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp85
-rw-r--r--src/test/fuzz/parse_iso8601.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp1
-rw-r--r--src/test/fuzz/process_messages.cpp1
-rw-r--r--src/test/fuzz/psbt.cpp23
-rw-r--r--src/test/fuzz/rpc.cpp359
-rw-r--r--src/test/fuzz/script.cpp56
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp21
-rw-r--r--src/test/fuzz/script_flags.cpp6
-rw-r--r--src/test/fuzz/script_ops.cpp61
-rw-r--r--src/test/fuzz/strprintf.cpp61
-rw-r--r--src/test/fuzz/timedata.cpp4
-rw-r--r--src/test/fuzz/transaction.cpp14
-rw-r--r--src/test/fuzz/tx_pool.cpp90
-rw-r--r--src/test/fuzz/util.cpp207
-rw-r--r--src/test/fuzz/util.h210
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp87
-rw-r--r--src/test/fuzz/versionbits.cpp63
-rw-r--r--src/test/getarg_tests.cpp120
-rw-r--r--src/test/hash_tests.cpp2
-rw-r--r--src/test/i2p_tests.cpp2
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/logging_tests.cpp3
-rw-r--r--src/test/mempool_tests.cpp2
-rw-r--r--src/test/merkleblock_tests.cpp4
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/test/multisig_tests.cpp16
-rw-r--r--src/test/net_tests.cpp48
-rw-r--r--src/test/netbase_tests.cpp98
-rw-r--r--src/test/policy_fee_tests.cpp6
-rw-r--r--src/test/reverselock_tests.cpp4
-rw-r--r--src/test/rpc_tests.cpp58
-rw-r--r--src/test/script_p2sh_tests.cpp2
-rw-r--r--src/test/script_standard_tests.cpp107
-rw-r--r--src/test/script_tests.cpp59
-rw-r--r--src/test/serfloat_tests.cpp129
-rw-r--r--src/test/serialize_tests.cpp94
-rw-r--r--src/test/settings_tests.cpp2
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/sigopcount_tests.cpp2
-rw-r--r--src/test/sock_tests.cpp4
-rw-r--r--src/test/streams_tests.cpp11
-rw-r--r--src/test/sync_tests.cpp3
-rw-r--r--src/test/torcontrol_tests.cpp3
-rw-r--r--src/test/transaction_tests.cpp73
-rw-r--r--src/test/txindex_tests.cpp3
-rw-r--r--src/test/txvalidation_tests.cpp98
-rw-r--r--src/test/uint256_tests.cpp3
-rw-r--r--src/test/util/blockfilter.cpp1
-rw-r--r--src/test/util/mining.cpp33
-rw-r--r--src/test/util/mining.h5
-rw-r--r--src/test/util/net.h23
-rw-r--r--src/test/util/setup_common.cpp55
-rw-r--r--src/test/util/setup_common.h14
-rw-r--r--src/test/util_tests.cpp75
-rw-r--r--src/test/util_threadnames_tests.cpp4
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp30
-rw-r--r--src/test/validation_tests.cpp10
-rw-r--r--src/test/versionbits_tests.cpp339
-rw-r--r--src/torcontrol.cpp5
-rw-r--r--src/txdb.cpp34
-rw-r--r--src/txmempool.cpp19
-rw-r--r--src/txmempool.h22
-rw-r--r--src/txorphanage.h6
-rw-r--r--src/txrequest.cpp2
-rw-r--r--src/uint256.h2
-rw-r--r--src/util/asmap.cpp6
-rw-r--r--src/util/hash_type.h72
-rw-r--r--src/util/serfloat.cpp64
-rw-r--r--src/util/serfloat.h16
-rw-r--r--src/util/sock.cpp4
-rw-r--r--src/util/sock.h31
-rw-r--r--src/util/spanparsing.cpp6
-rw-r--r--src/util/strencodings.cpp11
-rw-r--r--src/util/system.cpp182
-rw-r--r--src/util/system.h70
-rw-r--r--src/util/thread.cpp27
-rw-r--r--src/util/thread.h18
-rw-r--r--src/util/time.cpp2
-rw-r--r--src/util/time.h4
-rw-r--r--src/validation.cpp948
-rw-r--r--src/validation.h168
-rw-r--r--src/versionbits.cpp25
-rw-r--r--src/versionbits.h3
-rw-r--r--src/wallet/coincontrol.cpp16
-rw-r--r--src/wallet/coincontrol.h25
-rw-r--r--src/wallet/coinselection.cpp52
-rw-r--r--src/wallet/coinselection.h83
-rw-r--r--src/wallet/crypter.cpp6
-rw-r--r--src/wallet/db.cpp7
-rw-r--r--src/wallet/dump.cpp3
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp9
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h2
-rw-r--r--src/wallet/feebumper.cpp2
-rw-r--r--src/wallet/init.cpp16
-rw-r--r--src/wallet/interfaces.cpp30
-rw-r--r--src/wallet/load.cpp5
-rw-r--r--src/wallet/receive.cpp471
-rw-r--r--src/wallet/receive.h20
-rw-r--r--src/wallet/rpcdump.cpp44
-rw-r--r--src/wallet/rpcsigner.cpp108
-rw-r--r--src/wallet/rpcsigner.h25
-rw-r--r--src/wallet/rpcwallet.cpp330
-rw-r--r--src/wallet/salvage.cpp4
-rw-r--r--src/wallet/scriptpubkeyman.cpp6
-rw-r--r--src/wallet/spend.cpp978
-rw-r--r--src/wallet/spend.h64
-rw-r--r--src/wallet/sqlite.cpp180
-rw-r--r--src/wallet/test/coinselector_tests.cpp148
-rw-r--r--src/wallet/test/db_tests.cpp12
-rw-r--r--src/wallet/test/init_test_fixture.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp3
-rw-r--r--src/wallet/test/wallet_tests.cpp51
-rw-r--r--src/wallet/transaction.cpp25
-rw-r--r--src/wallet/transaction.h358
-rw-r--r--src/wallet/wallet.cpp1653
-rw-r--r--src/wallet/wallet.h521
-rw-r--r--src/wallet/walletdb.cpp8
-rw-r--r--src/wallet/wallettool.cpp3
-rw-r--r--src/wallet/walletutil.cpp2
-rw-r--r--src/zmq/zmqpublishnotifier.cpp5
-rw-r--r--test/README.md4
-rwxr-xr-xtest/functional/feature_assumevalid.py9
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py5
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py10
-rwxr-xr-xtest/functional/feature_cltv.py178
-rwxr-xr-xtest/functional/feature_coinstatsindex.py314
-rwxr-xr-xtest/functional/feature_config_args.py2
-rwxr-xr-xtest/functional/feature_csv_activation.py217
-rwxr-xr-xtest/functional/feature_dersig.py26
-rwxr-xr-xtest/functional/feature_includeconf.py9
-rwxr-xr-xtest/functional/feature_loadblock.py3
-rwxr-xr-xtest/functional/feature_notifications.py2
-rwxr-xr-xtest/functional/feature_nulldummy.py17
-rwxr-xr-xtest/functional/feature_proxy.py4
-rwxr-xr-xtest/functional/feature_rbf.py3
-rwxr-xr-xtest/functional/feature_taproot.py3
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py6
-rwxr-xr-xtest/functional/mempool_accept.py3
-rwxr-xr-xtest/functional/mempool_compatibility.py3
-rwxr-xr-xtest/functional/mempool_expiry.py3
-rwxr-xr-xtest/functional/mempool_package_onemore.py3
-rwxr-xr-xtest/functional/mempool_packages.py3
-rwxr-xr-xtest/functional/mempool_reorg.py109
-rwxr-xr-xtest/functional/mempool_resurrect.py3
-rwxr-xr-xtest/functional/mempool_spend_coinbase.py35
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py3
-rwxr-xr-xtest/functional/p2p_addr_relay.py170
-rwxr-xr-xtest/functional/p2p_blocksonly.py33
-rwxr-xr-xtest/functional/p2p_compactblocks.py13
-rwxr-xr-xtest/functional/p2p_eviction.py8
-rwxr-xr-xtest/functional/p2p_feefilter.py3
-rwxr-xr-xtest/functional/p2p_filter.py3
-rwxr-xr-xtest/functional/p2p_invalid_messages.py10
-rwxr-xr-xtest/functional/p2p_leak.py6
-rwxr-xr-xtest/functional/p2p_leak_tx.py3
-rwxr-xr-xtest/functional/p2p_segwit.py46
-rwxr-xr-x[-rw-r--r--]test/functional/rpc_addresses_deprecation.py0
-rwxr-xr-xtest/functional/rpc_blockchain.py9
-rwxr-xr-xtest/functional/rpc_createmultisig.py3
-rwxr-xr-xtest/functional/rpc_dumptxoutset.py6
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py59
-rwxr-xr-xtest/functional/rpc_getblockstats.py4
-rwxr-xr-xtest/functional/rpc_misc.py20
-rwxr-xr-xtest/functional/rpc_net.py78
-rwxr-xr-xtest/functional/rpc_packages.py362
-rwxr-xr-xtest/functional/rpc_psbt.py24
-rwxr-xr-xtest/functional/rpc_rawtransaction.py4
-rwxr-xr-xtest/functional/rpc_signer.py79
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py88
-rwxr-xr-xtest/functional/rpc_txoutproof.py3
-rw-r--r--test/functional/test_framework/blocktools.py19
-rw-r--r--test/functional/test_framework/netutil.py2
-rwxr-xr-xtest/functional/test_framework/p2p.py10
-rwxr-xr-xtest/functional/test_framework/test_framework.py7
-rwxr-xr-xtest/functional/test_framework/test_node.py5
-rw-r--r--test/functional/test_framework/util.py23
-rw-r--r--test/functional/test_framework/wallet.py114
-rwxr-xr-xtest/functional/test_runner.py5
-rwxr-xr-xtest/functional/wallet_abandonconflict.py3
-rwxr-xr-xtest/functional/wallet_address_types.py5
-rwxr-xr-xtest/functional/wallet_avoidreuse.py46
-rwxr-xr-xtest/functional/wallet_backup.py5
-rwxr-xr-xtest/functional/wallet_balance.py3
-rwxr-xr-xtest/functional/wallet_basic.py54
-rwxr-xr-xtest/functional/wallet_bumpfee.py18
-rwxr-xr-xtest/functional/wallet_descriptor.py3
-rwxr-xr-xtest/functional/wallet_fallbackfee.py4
-rwxr-xr-xtest/functional/wallet_groups.py3
-rwxr-xr-xtest/functional/wallet_hd.py3
-rwxr-xr-xtest/functional/wallet_importdescriptors.py74
-rwxr-xr-xtest/functional/wallet_importmulti.py9
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py5
-rwxr-xr-xtest/functional/wallet_keypool_topup.py3
-rwxr-xr-xtest/functional/wallet_labels.py7
-rwxr-xr-xtest/functional/wallet_listsinceblock.py3
-rwxr-xr-xtest/functional/wallet_multiwallet.py3
-rwxr-xr-xtest/functional/wallet_orphanedreward.py61
-rwxr-xr-xtest/functional/wallet_send.py72
-rwxr-xr-xtest/functional/wallet_signer.py44
-rwxr-xr-xtest/functional/wallet_taproot.py272
-rwxr-xr-xtest/functional/wallet_upgradewallet.py7
-rwxr-xr-xtest/functional/wallet_watchonly.py3
-rwxr-xr-xtest/lint/commit-script-check.sh4
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh10
-rwxr-xr-xtest/lint/lint-filenames.sh24
-rwxr-xr-xtest/lint/lint-files.py203
-rwxr-xr-xtest/lint/lint-files.sh7
-rwxr-xr-xtest/lint/lint-include-guards.sh2
-rwxr-xr-xtest/lint/lint-locale-dependence.sh2
-rwxr-xr-xtest/lint/lint-shebang.sh24
-rw-r--r--test/lint/lint-spelling.ignore-words.txt1
-rw-r--r--test/sanitizer_suppressions/tsan26
-rw-r--r--test/sanitizer_suppressions/ubsan10
568 files changed, 26136 insertions, 12000 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 097874b17a..fb95876c36 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -7,9 +7,9 @@ clone_depth: 5
environment:
PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%'
PYTHONUTF8: 1
- 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'
+ QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt51210x64_vs2019_1694/Qt5.12.10_x64_static_vs2019_1694.zip'
+ QT_DOWNLOAD_HASH: '3035a1307e8302bb3a76eba9bb3102979f945ab4022cc3bc2e1583edd44bdc99'
+ QT_LOCAL_PATH: 'C:\Qt5.12.10_x64_static_vs2019_1694'
VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
diff --git a/.cirrus.yml b/.cirrus.yml
index d7d5ab6c5c..26bd27754f 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -3,10 +3,19 @@
env:
PACKAGE_MANAGER_INSTALL: "apt-get update && apt-get install -y"
MAKEJOBS: "-j4"
- DANGER_RUN_CI_ON_HOST: "1" # Containers will be discarded after the run, so there is no risk that the ci scripts modify the system
TEST_RUNNER_PORT_MIN: "14000" # Must be larger than 12321, which is used for the http cache. See https://cirrus-ci.org/guide/writing-tasks/#http-cache
CCACHE_SIZE: "200M"
CCACHE_DIR: "/tmp/ccache_dir"
+ CCACHE_NOHASHDIR: "1" # Debug info might contain a stale path if the build dir changes, but this is fine
+
+cirrus_ephemeral_worker_template_env: &CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
+ DANGER_RUN_CI_ON_HOST: "1" # Containers will be discarded after the run, so there is no risk that the ci scripts modify the system
+
+persistent_worker_template_env: &PERSISTENT_WORKER_TEMPLATE_ENV
+ RESTART_CI_DOCKER_BEFORE_RUN: "1"
+
+persistent_worker_template: &PERSISTENT_WORKER_TEMPLATE
+ persistent_worker: {} # https://cirrus-ci.org/guide/persistent-workers/
# https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks
base_template: &BASE_TEMPLATE
@@ -28,16 +37,17 @@ global_task_template: &GLOBAL_TASK_TEMPLATE
# Each project has 16 CPU in total, assign 2 to each container, so that 8 tasks run in parallel
cpu: 2
memory: 8G # Set to 8GB to avoid OOM. https://cirrus-ci.org/guide/linux/#linux-containers
- kvm: true # Use kvm to avoid spurious CI failures in the default virtualization cluster, see https://github.com/bitcoin/bitcoin/issues/20093
ccache_cache:
folder: "/tmp/ccache_dir"
depends_built_cache:
- folder: "/tmp/cirrus-ci-build/depends/built"
- depends_sdk_cache:
- folder: "/tmp/cirrus-ci-build/depends/sdk-sources"
+ folder: "depends/built"
ci_script:
- ./ci/test_run_all.sh
+depends_sdk_cache_template: &DEPENDS_SDK_CACHE_TEMPLATE
+ depends_sdk_cache:
+ folder: "depends/sdk-sources"
+
compute_credits_template: &CREDITS_TEMPLATE
# https://cirrus-ci.org/pricing/#compute-credits
# Only use credits for pull requests to the main repo
@@ -70,6 +80,8 @@ task:
<< : *CREDITS_TEMPLATE
lint_script:
- ./ci/lint_run_all.sh
+ env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
task:
name: 'ARM [unit tests, no functional tests] [buster]'
@@ -77,6 +89,7 @@ task:
container:
image: debian:buster
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_arm.sh"
task:
@@ -85,6 +98,7 @@ task:
container:
image: ubuntu:focal
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_win64.sh"
task:
@@ -93,29 +107,29 @@ task:
container:
image: centos:8
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
PACKAGE_MANAGER_INSTALL: "yum install -y"
FILE_ENV: "./ci/test/00_setup_env_i686_centos.sh"
task:
- name: '[previous releases, uses qt5 dev package and some depends packages] [unsigned char] [bionic]'
- # For faster CI feedback, immediately schedule a task that compiles most modules
- << : *CREDITS_TEMPLATE
+ name: '[previous releases, uses qt5 dev package and some depends packages, DEBUG] [unsigned char] [bionic]'
+ previous_releases_cache:
+ folder: "releases"
<< : *GLOBAL_TASK_TEMPLATE
- depends_releases_cache:
- folder: "/tmp/cirrus-ci-build/releases"
- container:
- image: ubuntu:bionic
+ << : *PERSISTENT_WORKER_TEMPLATE
env:
+ << : *PERSISTENT_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_native_qt5.sh"
task:
- name: '[depends, sanitizers: thread (TSan), no gui] [focal]'
+ name: '[depends, sanitizers: thread (TSan), no gui] [hirsute]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:focal
- cpu: 4 # Double CPU and increase Memory to avoid timeout
+ image: ubuntu:hirsute
+ cpu: 6 # Increase CPU and Memory to avoid timeout
memory: 24G
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
MAKEJOBS: "-j8"
FILE_ENV: "./ci/test/00_setup_env_native_tsan.sh"
@@ -125,14 +139,16 @@ task:
container:
image: ubuntu:focal
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_native_msan.sh"
task:
- name: '[no depends, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer] [focal]'
+ name: '[no depends, sanitizers: address/leak (ASan + LSan) + undefined (UBSan) + integer] [hirsute]'
<< : *GLOBAL_TASK_TEMPLATE
container:
- image: ubuntu:focal
+ image: ubuntu:hirsute
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_native_asan.sh"
task:
@@ -140,15 +156,23 @@ task:
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:focal
+ cpu: 4 # Increase CPU and memory to avoid timeout
+ memory: 16G
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
+ MAKEJOBS: "-j8"
FILE_ENV: "./ci/test/00_setup_env_native_fuzz.sh"
task:
- name: '[multiprocess] [focal]'
+ name: '[multiprocess, DEBUG] [focal]'
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:focal
+ cpu: 4
+ memory: 16G # The default memory is sometimes just a bit too small, so double everything
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
+ MAKEJOBS: "-j8"
FILE_ENV: "./ci/test/00_setup_env_native_multiprocess.sh"
task:
@@ -157,36 +181,41 @@ task:
container:
image: ubuntu:bionic
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_native_nowallet.sh"
task:
name: 'macOS 10.14 [gui, no tests] [focal]'
+ << : *DEPENDS_SDK_CACHE_TEMPLATE
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:focal
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_mac.sh"
task:
name: 'macOS 11 native [gui] [no depends]'
- macos_brew_addon_script:
- - brew install boost libevent berkeley-db4 qt miniupnpc libnatpmp ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt
+ brew_install_script:
+ - brew install boost libevent berkeley-db4 qt@5 miniupnpc libnatpmp ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt
<< : *GLOBAL_TASK_TEMPLATE
osx_instance:
# Use latest image, but hardcode version to avoid silent upgrades (and breaks)
- image: big-sur-xcode-12.4 # https://cirrus-ci.org/guide/macOS
+ image: big-sur-xcode-12.5 # https://cirrus-ci.org/guide/macOS
env:
- DANGER_RUN_CI_ON_HOST: "true"
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
CI_USE_APT_INSTALL: "no"
PACKAGE_MANAGER_INSTALL: "echo" # Nothing to do
FILE_ENV: "./ci/test/00_setup_env_mac_host.sh"
task:
name: 'ARM64 Android APK [focal]'
+ << : *DEPENDS_SDK_CACHE_TEMPLATE
depends_sources_cache:
- folder: "/tmp/cirrus-ci-build/depends/sources"
+ folder: "depends/sources"
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:focal
env:
+ << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
FILE_ENV: "./ci/test/00_setup_env_android.sh"
diff --git a/.fuzzbuzz.yml b/.fuzzbuzz.yml
deleted file mode 100644
index e40b4df165..0000000000
--- a/.fuzzbuzz.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-base: ubuntu:16.04
-language: c++
-engine: libFuzzer
-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-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
- - git clone https://github.com/bitcoin-core/qa-assets
-auto_targets:
- find_targets_command: find src/test/fuzz/ -executable -type f ! -name "*.cpp" ! -name "*.h"
- base_corpus_dir: qa-assets/fuzz_seed_corpus/
- memory_limit: none
diff --git a/.gitignore b/.gitignore
index c0b5661464..b76864cc38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,7 +61,7 @@ src/qt/bitcoin-qt.includes
.dirstamp
.libs
.*.swp
-*.*~*
+*~
*.bak
*.rej
*.orig
@@ -149,3 +149,5 @@ db4/
osx_volname
dist/
*.background.tiff
+
+/guix-build-*
diff --git a/.tx/config b/.tx/config
index 86b517a612..232d481e18 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[bitcoin.qt-translation-021x]
-file_filter = src/qt/locale/bitcoin_<lang>.ts
-source_file = src/qt/locale/bitcoin_en.ts
+[bitcoin.qt-translation-022x]
+file_filter = src/qt/locale/bitcoin_<lang>.xlf
+source_file = src/qt/locale/bitcoin_en.xlf
source_lang = en
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ae2379fbd5..5cd4715ef0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -57,8 +57,8 @@ Communication Channels
----------------------
Most communication about Bitcoin Core development happens on IRC, in the
-`#bitcoin-core-dev` channel on Freenode. The easiest way to participate on IRC is
-with the web client, [webchat.freenode.net](https://webchat.freenode.net/). Chat
+`#bitcoin-core-dev` channel on Libera Chat. The easiest way to participate on IRC is
+with the web client, [web.libera.chat](https://web.libera.chat/#bitcoin-core-dev). Chat
history logs can be found
on [http://www.erisian.com.au/bitcoin-core-dev/](http://www.erisian.com.au/bitcoin-core-dev/)
and [http://gnusha.org/bitcoin-core-dev/](http://gnusha.org/bitcoin-core-dev/).
diff --git a/Makefile.am b/Makefile.am
index be62c3e6a9..d84dab1842 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
@@ -149,7 +149,7 @@ $(APP_DIST_DIR)/Applications:
$(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt
$(OSX_TEMP_ISO): $(APP_DIST_EXTRAS)
- $(XORRISOFS) -D -l -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -o $@ dist -- $(if $(SOURCE_DATE_EPOCH),-volume_date all_file_dates =$(SOURCE_DATE_EPOCH))
+ $(XORRISOFS) -D -l -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -o $@ $(APP_DIST_DIR) -- $(if $(SOURCE_DATE_EPOCH),-volume_date all_file_dates =$(SOURCE_DATE_EPOCH))
$(OSX_DMG): $(OSX_TEMP_ISO)
$(DMG) dmg "$<" "$@"
diff --git a/REVIEWERS b/REVIEWERS
index fa9a8f525f..d4d8939e1c 100644
--- a/REVIEWERS
+++ b/REVIEWERS
@@ -19,6 +19,7 @@
# Maintainers
# @fanquake
+# @hebasto
# @jonasschnelli
# @laanwj
# @marcofalke
@@ -59,11 +60,16 @@
/src/test/fuzz/ @practicalswift
/doc/fuzzing.md @practicalswift
-# Test framework
+# Tests
+/src/test/net_peer_eviction_tests.cpp @jonatack
/test/functional/mempool_updatefromblock.py @hebasto
/test/functional/feature_asmap.py @jonatack
/test/functional/interface_bitcoin_cli.py @jonatack
-/test/functional/tool_wallet.py @jonatack
+
+# Backwards compatibility tests
+*_compatibility.py @sjors
+/test/functional/wallet_upgradewallet.py @sjors @achow101
+/test/get_previous_releases.py @sjors
# Translations
/src/util/translation.h @hebasto
@@ -98,6 +104,11 @@
# Descriptors
*descriptor* @achow101 @sipa
+# External signer
+*external_signer* @sjors
+/doc/external-signer.md @sjors
+*signer.py @sjors
+
# Interfaces
/src/interfaces/ @ryanofsky
@@ -105,8 +116,7 @@
/src/txdb.* @jamesob
/src/dbwrapper.* @jamesob
-# Scripts/Linter
-*.sh @practicalswift
+# Linter
/test/lint/ @practicalswift
/test/lint/lint-shell.sh @hebasto
diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4
index 2ae33f7140..7aac53c815 100644
--- a/build-aux/m4/ax_boost_base.m4
+++ b/build-aux/m4/ax_boost_base.m4
@@ -11,7 +11,7 @@
# Test for the Boost C++ libraries of a particular version (or newer)
#
# If no path to the installed boost library is given the macro searchs
-# under /usr, /usr/local, /opt and /opt/local and evaluates the
+# under /usr, /usr/local, /opt, /opt/local and /opt/homebrew and evaluates the
# $BOOST_ROOT environment variable. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
@@ -151,7 +151,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
else
search_libsubdirs="$multiarch_libsubdir $libsubdirs"
fi
- for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
+ for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local /opt/homebrew/; do
if test -d "$_AX_BOOST_BASE_boost_path_tmp/include/boost" && test -r "$_AX_BOOST_BASE_boost_path_tmp/include/boost" ; then
for libsubdir in $search_libsubdirs ; do
if ls "$_AX_BOOST_BASE_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
@@ -227,7 +227,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
fi
else
if test "x$cross_compiling" != "xyes" ; then
- for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local ; do
+ for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local /opt/homebrew ; do
if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path" ; then
for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do
_version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index cce06e2fff..232a79bb36 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -108,11 +108,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS])
dnl This is ugly and complicated. Yuck. Works as follows:
- 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 _BITCOIN_QT_CHECK_STATIC_PLUGIN does a quick link-check and appends the
- dnl results to QT_LIBS.
+ dnl We check a header to find out whether Qt is built statically.
+ dnl When Qt is built statically, some plugins must be linked into
+ dnl the final binary as well. _BITCOIN_QT_CHECK_STATIC_PLUGIN does
+ dnl a quick link-check and appends the results to QT_LIBS.
BITCOIN_QT_CHECK([
TEMP_CPPFLAGS=$CPPFLAGS
TEMP_CXXFLAGS=$CXXFLAGS
@@ -147,6 +146,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
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])
+ _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsVistaStylePlugin], [-lqwindowsvistastyle])
AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
elif test "x$TARGET_OS" = xlinux; then
dnl workaround for https://bugreports.qt.io/browse/QTBUG-74874
@@ -162,7 +162,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
_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"
+ QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype $QT_LIBS"
AC_DEFINE(QT_QPA_PLATFORM_ANDROID, 1, [Define this symbol if the qt platform is android])
fi
fi
@@ -171,7 +171,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
])
if test "x$qt_bin_path" = x; then
- qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`"
+ qt_bin_path="`$PKG_CONFIG --variable=host_bins ${qt_lib_prefix}Core 2>/dev/null`"
fi
if test "x$use_hardening" != xno; then
@@ -226,6 +226,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt5 rcc5 rcc], $qt_bin_path)
BITCOIN_QT_PATH_PROGS([LRELEASE], [lrelease-qt5 lrelease5 lrelease], $qt_bin_path)
BITCOIN_QT_PATH_PROGS([LUPDATE], [lupdate-qt5 lupdate5 lupdate],$qt_bin_path, yes)
+ BITCOIN_QT_PATH_PROGS([LCONVERT], [lconvert-qt5 lconvert5 lconvert], $qt_bin_path, yes)
MOC_DEFS='-DHAVE_CONFIG_H -I$(srcdir)'
case $host in
@@ -259,7 +260,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
AC_MSG_ERROR([libQtDBus not found. Install libQtDBus or remove --with-qtdbus.])
fi
if test "x$LUPDATE" = x; then
- AC_MSG_WARN([lupdate is required to update qt translations])
+ AC_MSG_WARN([lupdate tool is required to update Qt translations.])
+ fi
+ if test "x$LCONVERT" = x; then
+ AC_MSG_WARN([lconvert tool is required to update Qt translations.])
fi
],[
bitcoin_enable_qt=no
@@ -282,12 +286,13 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
AC_SUBST(MOC_DEFS)
])
-dnl All macros below are internal and should _not_ be used from the main
-dnl configure.ac.
-dnl ----
+dnl All macros below are internal and should _not_ be used from configure.ac.
-dnl Internal. Check if the linked version of Qt was built as static libs.
-dnl Requires: Qt5.
+dnl Internal. Check if the linked version of Qt was built statically.
+dnl
+dnl _BITCOIN_QT_IS_STATIC
+dnl ---------------------
+dnl
dnl Requires: INCLUDES and LIBS must be populated as necessary.
dnl Output: bitcoin_cv_static_qt=yes|no
AC_DEFUN([_BITCOIN_QT_IS_STATIC],[
@@ -335,47 +340,54 @@ 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"])
+ PKG_CHECK_MODULES([QT_ACCESSIBILITY], [${qt_lib_prefix}AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="$QT_ACCESSIBILITY_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_DEVICEDISCOVERY], [${qt_lib_prefix}DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="$QT_DEVICEDISCOVERY_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_EDID], [${qt_lib_prefix}EdidSupport${qt_lib_suffix}], [QT_LIBS="$QT_EDID_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_EVENTDISPATCHER], [${qt_lib_prefix}EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="$QT_EVENTDISPATCHER_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_FB], [${qt_lib_prefix}FbSupport${qt_lib_suffix}], [QT_LIBS="$QT_FB_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_FONTDATABASE], [${qt_lib_prefix}FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="$QT_FONTDATABASE_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_THEME], [${qt_lib_prefix}ThemeSupport${qt_lib_suffix}], [QT_LIBS="$QT_THEME_LIBS $QT_LIBS"])
if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_INPUT], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_INPUT_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_XCBQPA], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_XCBQPA_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"])
+ PKG_CHECK_MODULES([QT_CLIPBOARD], [${qt_lib_prefix}ClipboardSupport${qt_lib_suffix}], [QT_LIBS="$QT_CLIPBOARD_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_GRAPHICS], [${qt_lib_prefix}GraphicsSupport${qt_lib_suffix}], [QT_LIBS="$QT_GRAPHICS_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport${qt_lib_suffix}], [QT_LIBS="$QT_SERVICE_LIBS $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"])
+ PKG_CHECK_MODULES([QT_WINDOWSUIAUTOMATION], [${qt_lib_prefix}WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="$QT_WINDOWSUIAUTOMATION_LIBS $QT_LIBS"])
+ elif test "x$TARGET_OS" = xandroid; then
+ PKG_CHECK_MODULES([QT_EGL], [${qt_lib_prefix}EglSupport], [QT_LIBS="$QT_EGL_LIBS $QT_LIBS"])
fi
])
dnl Internal. Find Qt libraries using pkg-config.
+dnl
+dnl _BITCOIN_QT_FIND_LIBS
+dnl ---------------------
+dnl
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_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CORE_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CORE_LIBS $QT_LIBS"],
[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_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_GUI_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_GUI_LIBS $QT_LIBS"],
[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_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_WIDGETS_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_WIDGETS_LIBS $QT_LIBS"],
[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_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_NETWORK_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_NETWORK_LIBS $QT_LIBS"],
[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_lib_suffix} $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4
index 5201a8cc7c..40639dfe61 100644
--- a/build-aux/m4/l_atomic.m4
+++ b/build-aux/m4/l_atomic.m4
@@ -12,11 +12,17 @@ dnl warranty.
m4_define([_CHECK_ATOMIC_testbody], [[
#include <atomic>
#include <cstdint>
+ #include <chrono>
+
+ using namespace std::chrono_literals;
int main() {
std::atomic<bool> lock{true};
std::atomic_exchange(&lock, false);
+ std::atomic<std::chrono::seconds> t{0s};
+ t.store(2s);
+
std::atomic<int64_t> a{};
int64_t v = 5;
diff --git a/build_msvc/README.md b/build_msvc/README.md
index 87ea556a23..88a05644a7 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -3,7 +3,7 @@ Building Bitcoin Core with Visual Studio
Introduction
---------------------
-Solution and project files to build the Bitcoin Core applications `msbuild` or Visual Studio can be found in the `build_msvc` directory. The build has been tested with Visual Studio 2017 and 2019.
+Solution and project files to build the Bitcoin Core applications `msbuild` or Visual Studio can be found in the `build_msvc` directory. The build has been tested with Visual Studio 2019 (building with earlier versions of Visual Studio should not be expected to work).
Building with Visual Studio is an alternative to the Linux based [cross-compiler build](https://github.com/bitcoin/bitcoin/blob/master/doc/build-windows.md).
@@ -27,11 +27,15 @@ Options for installing the dependencies in a Visual Studio compatible manner are
- Download the source code, build each dependency, add the required include paths, link libraries and binary tools to the Visual Studio project files.
- Use [nuget](https://www.nuget.org/) packages with the understanding that any binary files have been compiled by an untrusted third party.
-The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) required for building are listed in the `build_msvc/vcpkg.json` file. The `msbuild` project files are configured to automatically install the `vcpkg` dependencies.
+The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) required for building are listed in the `build_msvc/vcpkg.json` file. To ensure `msbuild` project files automatically install the `vcpkg` dependencies use:
+
+```
+vcpkg integrate install
+```
Qt
---------------------
-In order to build the Bitcoin Core a static build of Qt is required. The runtime library version (e.g. v141, v142) and platform type (x86 or x64) must also match.
+In order to build Bitcoin Core a static build of Qt is required. The runtime library version (e.g. v142) and platform type (x86 or x64) must also match.
Some prebuilt x64 versions of Qt can be downloaded from [here](https://github.com/sipsorcery/qt_win_binary/releases). Please be aware these downloads are NOT officially sanctioned by Bitcoin Core and are provided for developer convenience only. They should NOT be used for builds that will be used in a production environment or with real funds.
@@ -53,19 +57,13 @@ PS >py -3 msvc-autogen.py
- An optional step is to adjust the settings in the `build_msvc` directory and the `common.init.vcxproj` file. This project file contains settings that are common to all projects such as the runtime library version and target Windows SDK version. The Qt directories can also be set.
-- To build from the command line with the Visual Studio 2017 toolchain use:
-
-```
-msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /p:PlatformToolset=v141 /t:build
-```
-
- To build from the command line with the Visual Studio 2019 toolchain use:
```
msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
```
-- Alternatively open the `build_msvc/bitcoin.sln` file in Visual Studio.
+- Alternatively, open the `build_msvc/bitcoin.sln` file in Visual Studio 2019.
AppVeyor
---------------------
@@ -77,3 +75,25 @@ For safety reasons the Bitcoin Core .appveyor.yml file has the artifact options
#- 7z a bitcoin-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\build_msvc\%platform%\%configuration%\*.exe
#- path: bitcoin-%APPVEYOR_BUILD_VERSION%.zip
```
+
+Security
+---------------------
+[Base address randomization](https://docs.microsoft.com/en-us/cpp/build/reference/dynamicbase-use-address-space-layout-randomization?view=msvc-160) is used to make Bitcoin Core more secure. When building Bitcoin using the `build_msvc` process base address randomization can be disabled by editing `common.init.vcproj` to change `RandomizedBaseAddress` from `true` to `false` and then rebuilding the project.
+
+To check if `bitcoind` has `RandomizedBaseAddress` enabled or disabled run
+
+```
+.\dumpbin.exe /headers src/bitcoind.exe
+```
+
+If is it enabled then in the output `Dynamic base` will be listed in the `DLL characteristics` under `OPTIONAL HEADER VALUES` as shown below
+
+```
+ 8160 DLL characteristics
+ High Entropy Virtual Addresses
+ Dynamic base
+ NX compatible
+ Terminal Server Aware
+```
+
+This may not disable all stack randomization as versions of windows employ additional stack randomization protections. These protections must be turned off in the OS configuration. \ No newline at end of file
diff --git a/build_msvc/bitcoind/bitcoind.vcxproj b/build_msvc/bitcoind/bitcoind.vcxproj
index 48dfafaee0..c2c32af838 100644
--- a/build_msvc/bitcoind/bitcoind.vcxproj
+++ b/build_msvc/bitcoind/bitcoind.vcxproj
@@ -10,6 +10,9 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\bitcoind.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoind.cpp">
+ <ObjectFileName>$(IntDir)init_bitcoind.obj</ObjectFileName>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index f195d3d451..6ea018d846 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -105,6 +105,7 @@
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>
</Link>
<Lib>
<AdditionalOptions>/ignore:4221</AdditionalOptions>
diff --git a/build_msvc/common.qt.init.vcxproj b/build_msvc/common.qt.init.vcxproj
index 42150a2310..68ad06c4ac 100644
--- a/build_msvc/common.qt.init.vcxproj
+++ b/build_msvc/common.qt.init.vcxproj
@@ -2,15 +2,15 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="QtGlobals">
- <QtBaseDir>C:\Qt5.9.8_x64_static_vs2019</QtBaseDir>
+ <QtBaseDir>C:\Qt5.12.10_x64_static_vs2019_1694</QtBaseDir>
<QtPluginsLibraryDir>$(QtBaseDir)\plugins</QtPluginsLibraryDir>
<QtLibraryDir>$(QtBaseDir)\lib</QtLibraryDir>
<QtIncludeDir>$(QtBaseDir)\include</QtIncludeDir>
<QtIncludes>$(QtIncludeDir);$(QtIncludeDir)\QtNetwork;$(QtIncludeDir)\QtCore;$(QtIncludeDir)\QtWidgets;$(QtIncludeDir)\QtGui;</QtIncludes>
<GeneratedFilesOutDir>.\QtGeneratedFiles\qt</GeneratedFilesOutDir>
<QtToolsDir>$(QtBaseDir)\bin</QtToolsDir>
- <QtReleaseLibraries>$(QtPluginsLibraryDir)\platforms\qminimal.lib;$(QtPluginsLibraryDir)\platforms\qwindows.lib;$(QtLibraryDir)\qtfreetype.lib;$(QtLibraryDir)\qtharfbuzz.lib;$(QtLibraryDir)\qtlibpng.lib;$(QtLibraryDir)\qtpcre2.lib;$(QtLibraryDir)\Qt5AccessibilitySupport.lib;$(QtLibraryDir)\Qt5Core.lib;$(QtLibraryDir)\Qt5Concurrent.lib;$(QtLibraryDir)\Qt5EventDispatcherSupport.lib;$(QtLibraryDir)\Qt5FontDatabaseSupport.lib;$(QtLibraryDir)\Qt5Gui.lib;$(QtLibraryDir)\Qt5Network.lib;$(QtLibraryDir)\Qt5PlatformCompositorSupport.lib;$(QtLibraryDir)\Qt5ThemeSupport.lib;$(QtLibraryDir)\Qt5Widgets.lib;$(QtLibraryDir)\Qt5WinExtras.lib;$(QtLibraryDir)\qtmain.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtReleaseLibraries>
- <QtDebugLibraries>$(QtPluginsLibraryDir)\platforms\qwindowsd.lib;$(QtPluginsLibraryDir)\platforms\qminimald.lib;$(QtLibraryDir)\*d.lib;crypt32.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtDebugLibraries>
+ <QtReleaseLibraries>$(QtPluginsLibraryDir)\platforms\qminimal.lib;$(QtPluginsLibraryDir)\platforms\qwindows.lib;$(QtPluginsLibraryDir)\styles\qwindowsvistastyle.lib;$(QtLibraryDir)\Qt5WindowsUIAutomationSupport.lib;$(QtLibraryDir)\qtfreetype.lib;$(QtLibraryDir)\qtharfbuzz.lib;$(QtLibraryDir)\qtlibpng.lib;$(QtLibraryDir)\qtpcre2.lib;$(QtLibraryDir)\Qt5AccessibilitySupport.lib;$(QtLibraryDir)\Qt5Core.lib;$(QtLibraryDir)\Qt5Concurrent.lib;$(QtLibraryDir)\Qt5EventDispatcherSupport.lib;$(QtLibraryDir)\Qt5FontDatabaseSupport.lib;$(QtLibraryDir)\Qt5Gui.lib;$(QtLibraryDir)\Qt5Network.lib;$(QtLibraryDir)\Qt5PlatformCompositorSupport.lib;$(QtLibraryDir)\Qt5ThemeSupport.lib;$(QtLibraryDir)\Qt5Widgets.lib;$(QtLibraryDir)\Qt5WinExtras.lib;$(QtLibraryDir)\qtmain.lib;Wtsapi32.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtReleaseLibraries>
+ <QtDebugLibraries>$(QtPluginsLibraryDir)\platforms\qwindowsd.lib;$(QtPluginsLibraryDir)\platforms\qminimald.lib;$(QtPluginsLibraryDir)\styles\qwindowsvistastyled.lib;$(QtLibraryDir)\*d.lib;Wtsapi32.lib;crypt32.lib;userenv.lib;netapi32.lib;imm32.lib;Dwmapi.lib;version.lib;winmm.lib;UxTheme.lib</QtDebugLibraries>
</PropertyGroup>
</Project>
diff --git a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
index 490ce8b1ce..96bb584375 100644
--- a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
+++ b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj
@@ -34,6 +34,7 @@
<ClCompile Include="..\..\src\qt\overviewpage.cpp" />
<ClCompile Include="..\..\src\qt\paymentserver.cpp" />
<ClCompile Include="..\..\src\qt\peertablemodel.cpp" />
+ <ClCompile Include="..\..\src\qt\peertablesortproxy.cpp" />
<ClCompile Include="..\..\src\qt\platformstyle.cpp" />
<ClCompile Include="..\..\src\qt\psbtoperationsdialog.cpp" />
<ClCompile Include="..\..\src\qt\qrimagewidget.cpp" />
@@ -87,6 +88,7 @@
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_overviewpage.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_paymentserver.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablemodel.cpp" />
+ <ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablesortproxy.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_platformstyle.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_psbtoperationsdialog.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qrimagewidget.cpp" />
diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py
index d99b17d381..d99b17d381 100644..100755
--- a/build_msvc/msvc-autogen.py
+++ b/build_msvc/msvc-autogen.py
diff --git a/build_msvc/testconsensus/testconsensus.cpp b/build_msvc/testconsensus/testconsensus.cpp
index 115c92792d..f3c8517130 100644
--- a/build_msvc/testconsensus/testconsensus.cpp
+++ b/build_msvc/testconsensus/testconsensus.cpp
@@ -45,7 +45,7 @@ int main()
stream << vanillaSpendTx;
bitcoinconsensus_error err;
- auto op0Result = bitcoinconsensus_verify_script_with_amount(pubKeyScript.data(), pubKeyScript.size(), amount, (const unsigned char*)&stream[0], stream.size(), 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL, &err);
+ auto op0Result = bitcoinconsensus_verify_script_with_amount(pubKeyScript.data(), pubKeyScript.size(), amount, stream.data(), stream.size(), 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL, &err);
std::cout << "Op0 result: " << op0Result << ", error code " << err << std::endl;
getchar();
diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh
index 343b82a1ad..2c63a9efac 100755
--- a/ci/lint/04_install.sh
+++ b/ci/lint/04_install.sh
@@ -17,6 +17,6 @@ ${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
+SHELLCHECK_VERSION=v0.7.2
curl -sL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
export PATH="/tmp/shellcheck-${SHELLCHECK_VERSION}:${PATH}"
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index e6aec723bc..fa4d0410fa 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -14,6 +14,9 @@ export BASE_ROOT_DIR
# The depends dir.
# This folder exists on the ci host and ci guest. Changes are propagated back and forth.
export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends}
+# A folder for the ci system to put temporary files (ccache, datadirs for tests, ...)
+# This folder only exists on the ci host.
+export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch}
echo "Setting specific values in env"
if [ -n "${FILE_ENV}" ]; then
@@ -25,9 +28,6 @@ fi
echo "Fallback to default values in env (if not yet set)"
# The number of parallel jobs to pass down to make and test_runner.py
export MAKEJOBS=${MAKEJOBS:--j4}
-# A folder for the ci system to put temporary files (ccache, datadirs for tests, ...)
-# This folder only exists on the ci host.
-export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch}
# What host to compile for. See also ./depends/README.md
# Tests that need cross-compilation export the appropriate HOST.
# Tests that run natively guess the host
diff --git a/ci/test/00_setup_env_android.sh b/ci/test/00_setup_env_android.sh
index f78a84eeac..f78a84eeac 100644..100755
--- a/ci/test/00_setup_env_android.sh
+++ b/ci/test/00_setup_env_android.sh
diff --git a/ci/test/00_setup_env_arm.sh b/ci/test/00_setup_env_arm.sh
index 42783197a9..07f099b85c 100644..100755
--- a/ci/test/00_setup_env_arm.sh
+++ b/ci/test/00_setup_env_arm.sh
@@ -25,4 +25,4 @@ export RUN_FUNCTIONAL_TESTS=false
export GOAL="install"
# -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1"
# This could be removed once the ABI change warning does not show up by default
-export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi --with-boost-process"
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi --enable-external-signer"
diff --git a/ci/test/00_setup_env_i686_centos.sh b/ci/test/00_setup_env_i686_centos.sh
index 07e7c2dc27..05c724fc0b 100644..100755
--- a/ci/test/00_setup_env_i686_centos.sh
+++ b/ci/test/00_setup_env_i686_centos.sh
@@ -11,6 +11,6 @@ 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 bison"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports --with-boost-process"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports --enable-external-signer"
export CONFIG_SHELL="/bin/dash"
export TEST_RUNNER_ENV="LC_ALL=en_US.UTF-8"
diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh
index 6ce3812447..196394e908 100644..100755
--- a/ci/test/00_setup_env_mac.sh
+++ b/ci/test/00_setup_env_mac.sh
@@ -10,9 +10,9 @@ export CONTAINER_NAME=ci_macos_cross
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 librsvg2-bin libz-dev libtiff-tools libtinfo5 python3-setuptools xorriso"
-export XCODE_VERSION=11.3.1
-export XCODE_BUILD_ID=11C505
+export XCODE_VERSION=12.1
+export XCODE_BUILD_ID=12A7403
export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export GOAL="deploy"
-export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --with-boost-process"
+export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --enable-external-signer"
diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh
index 274a0d1b7c..898c1530a1 100644..100755
--- a/ci/test/00_setup_env_mac_host.sh
+++ b/ci/test/00_setup_env_mac_host.sh
@@ -7,12 +7,11 @@
export LC_ALL=C.UTF-8
export HOST=x86_64-apple-darwin18
-export PIP_PACKAGES="zmq"
+export PIP_PACKAGES="zmq lief"
export GOAL="install"
-export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --with-boost-process"
+export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --enable-external-signer"
export CI_OS_NAME="macos"
export NO_DEPENDS=1
export OSX_SDK=""
export CCACHE_SIZE=300M
-
export RUN_SECURITY_TESTS="true"
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index e47119e6fa..92af98aa9b 100644..100755
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -8,7 +8,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-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 DOCKER_NAME_TAG=ubuntu:hirsute
export NO_DEPENDS=1
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang CXX=clang++ --with-boost-process"
+export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang CXX=clang++ --enable-external-signer"
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index ebb5a1cabe..bedd0cf9aa 100644..100755
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -14,5 +14,5 @@ export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export GOAL="install"
-export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC=clang CXX=clang++ --with-boost-process"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC=clang CXX=clang++ --enable-external-signer"
export CCACHE_SIZE=200M
diff --git a/ci/test/00_setup_env_native_fuzz_with_msan.sh b/ci/test/00_setup_env_native_fuzz_with_msan.sh
new file mode 100755
index 0000000000..d5b28ca5cf
--- /dev/null
+++ b/ci/test/00_setup_env_native_fuzz_with_msan.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+#
+# 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.
+
+export LC_ALL=C.UTF-8
+
+export DOCKER_NAME_TAG="ubuntu:20.04"
+LIBCXX_DIR="${BASE_SCRATCH_DIR}/msan/build/"
+export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
+LIBCXX_FLAGS="-nostdinc++ -stdlib=libc++ -L${LIBCXX_DIR}lib -lc++abi -I${LIBCXX_DIR}include -I${LIBCXX_DIR}include/c++/v1 -lpthread -Wl,-rpath,${LIBCXX_DIR}lib -Wno-unused-command-line-argument"
+export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
+
+export CONTAINER_NAME="ci_native_msan"
+export PACKAGES="clang-9 llvm-9 cmake"
+export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++17 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,memory --with-asm=no --prefix=${DEPENDS_DIR}/x86_64-pc-linux-gnu/ CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}'"
+export USE_MEMORY_SANITIZER="true"
+export RUN_UNIT_TESTS="false"
+export RUN_FUNCTIONAL_TESTS="false"
+export RUN_FUZZ_TESTS=true
+export CCACHE_SIZE=250M
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 2cf672b91e..2cf672b91e 100644..100755
--- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
diff --git a/ci/test/00_setup_env_native_msan.sh b/ci/test/00_setup_env_native_msan.sh
index 3ce50f816f..7bcf9f23a2 100644..100755
--- a/ci/test/00_setup_env_native_msan.sh
+++ b/ci/test/00_setup_env_native_msan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export DOCKER_NAME_TAG="ubuntu:20.04"
-LIBCXX_DIR="${BASE_ROOT_DIR}/ci/scratch/msan/build/"
+LIBCXX_DIR="${BASE_SCRATCH_DIR}/msan/build/"
export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
LIBCXX_FLAGS="-nostdinc++ -stdlib=libc++ -L${LIBCXX_DIR}lib -lc++abi -I${LIBCXX_DIR}include -I${LIBCXX_DIR}include/c++/v1 -lpthread -Wl,-rpath,${LIBCXX_DIR}lib -Wno-unused-command-line-argument"
export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
@@ -15,9 +15,9 @@ export BDB_PREFIX="${BASE_ROOT_DIR}/db4"
export CONTAINER_NAME="ci_native_msan"
export PACKAGES="clang-9 llvm-9 cmake"
-export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++17 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
+export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++17 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' libevent_cflags='${MSAN_FLAGS}' sqlite_cflags='${MSAN_FLAGS}' zeromq_cxxflags='-std=c++17 ${MSAN_AND_LIBCXX_FLAGS}'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-wallet --with-sanitizers=memory --with-asm=no --prefix=${BASE_ROOT_DIR}/depends/x86_64-pc-linux-gnu/ CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' BDB_LIBS='-L${BDB_PREFIX}/lib -ldb_cxx-4.8' BDB_CFLAGS='-I${BDB_PREFIX}/include'"
+export BITCOIN_CONFIG="--enable-wallet --with-sanitizers=memory --with-asm=no --prefix=${DEPENDS_DIR}/x86_64-pc-linux-gnu/ CC=clang CXX=clang++ CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' BDB_LIBS='-L${BDB_PREFIX}/lib -ldb_cxx-4.8' BDB_CFLAGS='-I${BDB_PREFIX}/include'"
export USE_MEMORY_SANITIZER="true"
export RUN_FUNCTIONAL_TESTS="false"
export CCACHE_SIZE=250M
diff --git a/ci/test/00_setup_env_native_multiprocess.sh b/ci/test/00_setup_env_native_multiprocess.sh
index c5692d786a..37d714400b 100644..100755
--- a/ci/test/00_setup_env_native_multiprocess.sh
+++ b/ci/test/00_setup_env_native_multiprocess.sh
@@ -8,9 +8,10 @@ export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_multiprocess
export DOCKER_NAME_TAG=ubuntu:20.04
-export PACKAGES="cmake python3"
-export DEP_OPTS="MULTIPROCESS=1"
+export PACKAGES="cmake python3 python3-pip llvm clang"
+export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
export GOAL="install"
-export BITCOIN_CONFIG="--with-boost-process"
+export BITCOIN_CONFIG="--enable-external-signer --enable-debug CC=clang CXX=clang++" # Use clang to avoid OOM
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
export RUN_SECURITY_TESTS="true"
+export PIP_PACKAGES="lief"
diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet.sh
index 7ff044c020..a496b5af6e 100644..100755
--- a/ci/test/00_setup_env_native_nowallet.sh
+++ b/ci/test/00_setup_env_native_nowallet.sh
@@ -11,4 +11,4 @@ export DOCKER_NAME_TAG=ubuntu:18.04 # Use bionic to have one config run the tes
export PACKAGES="python3-zmq clang-5.0 llvm-5.0" # Use clang-5 to test C++17 compatibility, see doc/dependencies.md
export DEP_OPTS="NO_WALLET=1"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CC=clang-5.0 CXX=clang++-5.0 --with-boost-process"
+export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CC=clang-5.0 CXX=clang++-5.0 --enable-external-signer"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 4c42605e9a..61948ab221 100644..100755
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -16,4 +16,4 @@ 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 --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --with-boost-process"
+--enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --enable-external-signer"
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 182e42ee7d..33f63fa9ba 100644..100755
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -7,8 +7,8 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_tsan
-export DOCKER_NAME_TAG=ubuntu:20.04
+export DOCKER_NAME_TAG=ubuntu:hirsute
export PACKAGES="clang llvm libc++abi-dev libc++-dev python3-zmq"
export DEP_OPTS="CC=clang CXX='clang++ -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-zmq --with-gui=no CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' CXXFLAGS='-g' --with-sanitizers=thread CC=clang CXX='clang++ -stdlib=libc++' --with-boost-process"
+export BITCOIN_CONFIG="--enable-zmq --with-gui=no CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' CXXFLAGS='-g' --with-sanitizers=thread CC=clang CXX='clang++ -stdlib=libc++' --enable-external-signer"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index e079a7057c..e079a7057c 100644..100755
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh
index accbd07e22..88b431f3c7 100644..100755
--- a/ci/test/00_setup_env_s390x.sh
+++ b/ci/test/00_setup_env_s390x.sh
@@ -23,4 +23,4 @@ export RUN_UNIT_TESTS=true
export TEST_RUNNER_ENV="LC_ALL=C"
export RUN_FUNCTIONAL_TESTS=true
export GOAL="install"
-export BITCOIN_CONFIG="--enable-reduce-exports --with-incompatible-bdb --with-boost-process"
+export BITCOIN_CONFIG="--enable-reduce-exports --with-incompatible-bdb --enable-external-signer"
diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh
index 1e68d2a61a..4d5bde13fd 100644..100755
--- a/ci/test/00_setup_env_win64.sh
+++ b/ci/test/00_setup_env_win64.sh
@@ -13,7 +13,7 @@ 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 GOAL="deploy"
-export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests --without-boost-process"
+export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests --disable-external-signer"
# Compiler for MinGW-w64 causes false -Wreturn-type warning.
# See https://sourceforge.net/p/mingw-w64/bugs/306/
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index f0ed314d19..01dbfe221b 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -33,7 +33,12 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
echo "Creating $DOCKER_NAME_TAG container to run in"
${CI_RETRY_EXE} docker pull "$DOCKER_NAME_TAG"
- DOCKER_ID=$(docker run $DOCKER_ADMIN -idt \
+ if [ -n "${RESTART_CI_DOCKER_BEFORE_RUN}" ] ; then
+ echo "Restart docker before run to stop and clear all containers started with --rm"
+ systemctl restart docker
+ fi
+
+ DOCKER_ID=$(docker run $DOCKER_ADMIN --rm --interactive --detach --tty \
--mount type=bind,src=$BASE_ROOT_DIR,dst=/ro_base,readonly \
--mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR \
--mount type=bind,src=$DEPENDS_DIR,dst=$DEPENDS_DIR \
@@ -62,6 +67,9 @@ if [[ $DOCKER_NAME_TAG == centos* ]]; then
elif [ "$CI_USE_APT_INSTALL" != "no" ]; then
${CI_RETRY_EXE} DOCKER_EXEC apt-get update
${CI_RETRY_EXE} DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -y $PACKAGES $DOCKER_PACKAGES
+ if [ -n "$PIP_PACKAGES" ]; then
+ ${CI_RETRY_EXE} pip3 install --user $PIP_PACKAGES
+ fi
fi
if [ "$CI_OS_NAME" == "macos" ]; then
@@ -87,7 +95,7 @@ if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then
DOCKER_EXEC "update-alternatives --install /usr/bin/clang++ clang++ \$(which clang++-9) 100"
DOCKER_EXEC "update-alternatives --install /usr/bin/clang clang \$(which clang-9) 100"
DOCKER_EXEC "mkdir -p ${BASE_SCRATCH_DIR}/msan/build/"
- DOCKER_EXEC "git clone --depth=1 https://github.com/llvm/llvm-project -b llvmorg-10.0.0 ${BASE_SCRATCH_DIR}/msan/llvm-project"
+ DOCKER_EXEC "git clone --depth=1 https://github.com/llvm/llvm-project -b llvmorg-12.0.0 ${BASE_SCRATCH_DIR}/msan/llvm-project"
DOCKER_EXEC "cd ${BASE_SCRATCH_DIR}/msan/build/ && cmake -DLLVM_ENABLE_PROJECTS='libcxx;libcxxabi' -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_SANITIZER=Memory -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_TARGETS_TO_BUILD=X86 ../llvm-project/llvm/"
DOCKER_EXEC "cd ${BASE_SCRATCH_DIR}/msan/build/ && make $MAKEJOBS cxx"
fi
diff --git a/configure.ac b/configure.ac
index 2605b77e34..aaa932b093 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,12 +194,6 @@ AC_ARG_ENABLE([fuzz-binary],
[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).]),
- [enable_danger_fuzz_link_all=$enableval],
- [enable_danger_fuzz_link_all=no])
-
AC_ARG_WITH([qrencode],
[AS_HELP_STRING([--with-qrencode],
[enable QR code support (default is yes if qt is enabled and libqrencode is found)])],
@@ -439,6 +433,10 @@ if test "x$enable_werror" = "xyes"; then
[AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
AX_CHECK_COMPILE_FLAG([-Werror=unreachable-code-loop-increment],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=mismatched-tags], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=mismatched-tags"], [], [$CXXFLAG_WERROR])
+
+ if test x$suppress_external_warnings != xno ; then
+ AX_CHECK_COMPILE_FLAG([-Werror=documentation],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=documentation"],,[[$CXXFLAG_WERROR]])
+ fi
fi
if test "x$CXXFLAGS_overridden" = "xno"; then
@@ -466,13 +464,16 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
[AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])])
AX_CHECK_COMPILE_FLAG([-Wunreachable-code-loop-increment],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]])
+ if test x$suppress_external_warnings != xno ; then
+ AX_CHECK_COMPILE_FLAG([-Wdocumentation],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wdocumentation"],,[[$CXXFLAG_WERROR]])
+ fi
+
dnl Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
dnl unknown options if any other warning is produced. Test the -Wfoo case, and
dnl set the -Wno-foo case if it works.
AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wself-assign],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wdeprecated-register],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-register"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-implicit-fallthrough"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wdeprecated-copy],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-deprecated-copy"],,[[$CXXFLAG_WERROR]])
fi
@@ -887,7 +888,7 @@ if test x$use_hardening != xno; then
dnl See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details.
;;
*)
- AX_CHECK_COMPILE_FLAG([-fstack-clash-protection],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"])
+ AX_CHECK_COMPILE_FLAG([-fstack-clash-protection], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"], [], [$CXXFLAG_WERROR])
;;
esac
@@ -1319,9 +1320,11 @@ fi
if test x$enable_wallet != xno; then
dnl Check for libdb_cxx only if wallet enabled
- BITCOIN_FIND_BDB48
- if test x$suppress_external_warnings != xno ; then
+ if test "x$use_bdb" != "xno"; then
+ BITCOIN_FIND_BDB48
+ if test x$suppress_external_warnings != xno ; then
BDB_CPPFLAGS=SUPPRESS_WARNINGS($BDB_CPPFLAGS)
+ fi
fi
dnl Check for sqlite3
@@ -1486,6 +1489,10 @@ if test x$build_bitcoin_cli$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench
if test x$TARGET_OS != xwindows; then
PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads >= 2.0.21],, [AC_MSG_ERROR([libevent_pthreads version 2.0.21 or greater not found.])])
fi
+
+ if test x$suppress_external_warnings != xno; then
+ EVENT_CFLAGS=SUPPRESS_WARNINGS($EVENT_CFLAGS)
+ fi
fi
dnl QR Code encoding library check
@@ -1629,6 +1636,10 @@ if test "x$use_ccache" != "xno"; then
CXX="$ac_cv_path_CCACHE $CXX"
fi
AC_MSG_RESULT($use_ccache)
+ if test "x$use_ccache" = "xyes"; then
+ AX_CHECK_COMPILE_FLAG([-fdebug-prefix-map=A=B],[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -fdebug-prefix-map=\$(abs_srcdir)=."],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_PREPROC_FLAG([-fmacro-prefix-map=A=B],[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -fmacro-prefix-map=\$(abs_srcdir)=."],,[[$CXXFLAG_WERROR]])
+ fi
fi
dnl enable wallet
@@ -1773,7 +1784,6 @@ 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])
AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes])
diff --git a/contrib/bitcoin-qt.pro b/contrib/bitcoin-qt.pro
deleted file mode 100644
index 0e4eeee0a7..0000000000
--- a/contrib/bitcoin-qt.pro
+++ /dev/null
@@ -1,22 +0,0 @@
-FORMS += \
- ../src/qt/forms/aboutdialog.ui \
- ../src/qt/forms/addressbookpage.ui \
- ../src/qt/forms/askpassphrasedialog.ui \
- ../src/qt/forms/coincontroldialog.ui \
- ../src/qt/forms/editaddressdialog.ui \
- ../src/qt/forms/helpmessagedialog.ui \
- ../src/qt/forms/intro.ui \
- ../src/qt/forms/openuridialog.ui \
- ../src/qt/forms/optionsdialog.ui \
- ../src/qt/forms/overviewpage.ui \
- ../src/qt/forms/receivecoinsdialog.ui \
- ../src/qt/forms/receiverequestdialog.ui \
- ../src/qt/forms/debugwindow.ui \
- ../src/qt/forms/sendcoinsdialog.ui \
- ../src/qt/forms/sendcoinsentry.ui \
- ../src/qt/forms/signverifymessagedialog.ui \
- ../src/qt/forms/transactiondescdialog.ui \
- ../src/qt/forms/createwalletdialog.ui
-
-RESOURCES += \
- ../src/qt/bitcoin.qrc
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index bc5535b4c7..6d23f600c3 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -1,7 +1,7 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Bitcoin
Upstream-Contact: Satoshi Nakamoto <satoshin@gmx.com>
- irc://#bitcoin@freenode.net
+ irc://#bitcoin-core-dev@libera.chat
Source: https://github.com/bitcoin/bitcoin
Files: *
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index 7b09c42fde..0b59d8eada 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -6,22 +6,13 @@
Perform basic security checks on a series of executables.
Exit status will be 0 if successful, and the program will be silent.
Otherwise the exit status will be 1 and it will log which executables failed which checks.
-Needs `objdump` (for PE) and `otool` (for MACHO).
'''
-import subprocess
import sys
-import os
from typing import List, Optional
+import lief
import pixie
-OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
-OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool')
-
-def run_command(command) -> str:
- p = subprocess.run(command, stdout=subprocess.PIPE, check=True, universal_newlines=True)
- return p.stdout
-
def check_ELF_PIE(executable) -> bool:
'''
Check for position independent executable (PIE), allowing for address space randomization.
@@ -143,112 +134,72 @@ def check_ELF_separate_code(executable):
return False
return True
-def get_PE_dll_characteristics(executable) -> int:
- '''Get PE DllCharacteristics bits'''
- stdout = run_command([OBJDUMP_CMD, '-x', executable])
-
- bits = 0
- for line in stdout.splitlines():
- tokens = line.split()
- if len(tokens)>=2 and tokens[0] == 'DllCharacteristics':
- bits = int(tokens[1],16)
- return bits
-
-IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
-IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
-IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100
-
def check_PE_DYNAMIC_BASE(executable) -> bool:
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
- bits = get_PE_dll_characteristics(executable)
- return (bits & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE) == IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
+ binary = lief.parse(executable)
+ return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists
# Must support high-entropy 64-bit address space layout randomization
# in addition to DYNAMIC_BASE to have secure ASLR.
def check_PE_HIGH_ENTROPY_VA(executable) -> bool:
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
- bits = get_PE_dll_characteristics(executable)
- return (bits & IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA) == IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA
+ binary = lief.parse(executable)
+ return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists
def check_PE_RELOC_SECTION(executable) -> bool:
'''Check for a reloc section. This is required for functional ASLR.'''
- stdout = run_command([OBJDUMP_CMD, '-h', executable])
+ binary = lief.parse(executable)
+ return binary.has_relocations
- for line in stdout.splitlines():
- if '.reloc' in line:
- return True
- return False
-
-def check_PE_NX(executable) -> bool:
- '''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)'''
- bits = get_PE_dll_characteristics(executable)
- return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
-
-def get_MACHO_executable_flags(executable) -> List[str]:
- stdout = run_command([OTOOL_CMD, '-vh', executable])
+def check_MACHO_NOUNDEFS(executable) -> bool:
+ '''
+ Check for no undefined references.
+ '''
+ binary = lief.parse(executable)
+ return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS)
- flags: List[str] = []
- for line in stdout.splitlines():
- tokens = line.split()
- # filter first two header lines
- if 'magic' in tokens or 'Mach' in tokens:
- continue
- # filter ncmds and sizeofcmds values
- flags += [t for t in tokens if not t.isdigit()]
- return flags
+def check_MACHO_LAZY_BINDINGS(executable) -> bool:
+ '''
+ Check for no lazy bindings.
+ We don't use or check for MH_BINDATLOAD. See #18295.
+ '''
+ binary = lief.parse(executable)
+ return binary.dyld_info.lazy_bind == (0,0)
-def check_MACHO_PIE(executable) -> bool:
+def check_MACHO_Canary(executable) -> bool:
'''
- Check for position independent executable (PIE), allowing for address space randomization.
+ Check for use of stack canary
'''
- flags = get_MACHO_executable_flags(executable)
- if 'PIE' in flags:
- return True
- return False
+ binary = lief.parse(executable)
+ return binary.has_symbol('___stack_chk_fail')
-def check_MACHO_NOUNDEFS(executable) -> bool:
+def check_PIE(executable) -> bool:
'''
- Check for no undefined references.
+ Check for position independent executable (PIE),
+ allowing for address space randomization.
'''
- flags = get_MACHO_executable_flags(executable)
- if 'NOUNDEFS' in flags:
- return True
- return False
+ binary = lief.parse(executable)
+ return binary.is_pie
-def check_MACHO_NX(executable) -> bool:
+def check_NX(executable) -> bool:
'''
Check for no stack execution
'''
- flags = get_MACHO_executable_flags(executable)
- if 'ALLOW_STACK_EXECUTION' in flags:
- return False
- return True
+ binary = lief.parse(executable)
+ return binary.has_nx
-def check_MACHO_LAZY_BINDINGS(executable) -> bool:
+def check_control_flow(executable) -> bool:
'''
- Check for no lazy bindings.
- We don't use or check for MH_BINDATLOAD. See #18295.
+ Check for control flow instrumentation
'''
- stdout = run_command([OTOOL_CMD, '-l', executable])
+ binary = lief.parse(executable)
- for line in stdout.splitlines():
- tokens = line.split()
- if 'lazy_bind_off' in tokens or 'lazy_bind_size' in tokens:
- if tokens[1] != '0':
- return False
- return True
+ content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO)
-def check_MACHO_Canary(executable) -> bool:
- '''
- Check for use of stack canary
- '''
- stdout = run_command([OTOOL_CMD, '-Iv', executable])
+ if content == [243, 15, 30, 250]: # endbr64
+ return True
+ return False
- ok = False
- for line in stdout.splitlines():
- if '___stack_chk_fail' in line:
- ok = True
- return ok
CHECKS = {
'ELF': [
@@ -259,17 +210,19 @@ CHECKS = {
('separate_code', check_ELF_separate_code),
],
'PE': [
+ ('PIE', check_PIE),
('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),
('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),
- ('NX', check_PE_NX),
+ ('NX', check_NX),
('RELOC_SECTION', check_PE_RELOC_SECTION)
],
'MACHO': [
- ('PIE', check_MACHO_PIE),
+ ('PIE', check_PIE),
('NOUNDEFS', check_MACHO_NOUNDEFS),
- ('NX', check_MACHO_NX),
+ ('NX', check_NX),
('LAZY_BINDINGS', check_MACHO_LAZY_BINDINGS),
- ('Canary', check_MACHO_Canary)
+ ('Canary', check_MACHO_Canary),
+ ('CONTROL_FLOW', check_control_flow),
]
}
@@ -285,24 +238,24 @@ def identify_executable(executable) -> Optional[str]:
return None
if __name__ == '__main__':
- retval = 0
+ retval: int = 0
for filename in sys.argv[1:]:
try:
etype = identify_executable(filename)
if etype is None:
- print('%s: unknown format' % filename)
+ print(f'{filename}: unknown format')
retval = 1
continue
- failed = []
+ failed: List[str] = []
for (name, func) in CHECKS[etype]:
if not func(filename):
failed.append(name)
if failed:
- print('%s: failed %s' % (filename, ' '.join(failed)))
+ print(f'{filename}: failed {" ".join(failed)}')
retval = 1
except IOError:
- print('%s: cannot open' % filename)
+ print(f'{filename}: cannot open')
retval = 1
sys.exit(retval)
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 436f179d61..d740a94560 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -15,6 +15,7 @@ import sys
import os
from typing import List, Optional
+import lief
import pixie
# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases
@@ -52,8 +53,6 @@ IGNORE_EXPORTS = {
'environ', '_environ', '__environ',
}
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
-OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump')
-OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool')
# Allowed NEEDED libraries
ELF_ALLOWED_LIBRARIES = {
@@ -203,44 +202,22 @@ def check_ELF_libraries(filename) -> bool:
ok = False
return ok
-def macho_read_libraries(filename) -> List[str]:
- p = subprocess.Popen([OTOOL_CMD, '-L', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
- (stdout, stderr) = p.communicate()
- if p.returncode:
- raise IOError('Error opening file')
- libraries = []
- for line in stdout.splitlines():
- tokens = line.split()
- if len(tokens) == 1: # skip executable name
- continue
- libraries.append(tokens[0].split('/')[-1])
- return libraries
-
def check_MACHO_libraries(filename) -> bool:
ok: bool = True
- for dylib in macho_read_libraries(filename):
- if dylib not in MACHO_ALLOWED_LIBRARIES:
- print('{} is not in ALLOWED_LIBRARIES!'.format(dylib))
+ binary = lief.parse(filename)
+ for dylib in binary.libraries:
+ split = dylib.name.split('/')
+ if split[-1] not in MACHO_ALLOWED_LIBRARIES:
+ print(f'{split[-1]} is not in ALLOWED_LIBRARIES!')
ok = False
return ok
-def pe_read_libraries(filename) -> List[str]:
- p = subprocess.Popen([OBJDUMP_CMD, '-x', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
- (stdout, stderr) = p.communicate()
- if p.returncode:
- raise IOError('Error opening file')
- libraries = []
- for line in stdout.splitlines():
- if 'DLL Name:' in line:
- tokens = line.split(': ')
- libraries.append(tokens[1])
- return libraries
-
def check_PE_libraries(filename) -> bool:
ok: bool = True
- for dylib in pe_read_libraries(filename):
+ binary = lief.parse(filename)
+ for dylib in binary.libraries:
if dylib not in PE_ALLOWED_LIBRARIES:
- print('{} is not in ALLOWED_LIBRARIES!'.format(dylib))
+ print(f'{dylib} is not in ALLOWED_LIBRARIES!')
ok = False
return ok
@@ -275,7 +252,7 @@ if __name__ == '__main__':
try:
etype = identify_executable(filename)
if etype is None:
- print('{}: unknown format'.format(filename))
+ print(f'{filename}: unknown format')
retval = 1
continue
@@ -284,9 +261,9 @@ if __name__ == '__main__':
if not func(filename):
failed.append(name)
if failed:
- print('{}: failed {}'.format(filename, ' '.join(failed)))
+ print(f'{filename}: failed {" ".join(failed)}')
retval = 1
except IOError:
- print('{}: cannot open'.format(filename))
+ print(f'{filename}: cannot open')
retval = 1
sys.exit(retval)
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 28b5f57489..c079fe5b4d 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -77,16 +77,18 @@ class TestSecurityChecks(unittest.TestCase):
write_testcode(source)
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector']),
- (1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS Canary'))
+ (1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS Canary CONTROL_FLOW'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fstack-protector-all']),
- (1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS'))
+ (1, executable+': failed PIE NOUNDEFS NX LAZY_BINDINGS CONTROL_FLOW'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-fstack-protector-all']),
- (1, executable+': failed PIE NOUNDEFS LAZY_BINDINGS'))
+ (1, executable+': failed PIE NOUNDEFS LAZY_BINDINGS CONTROL_FLOW'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-fstack-protector-all']),
- (1, executable+': failed PIE LAZY_BINDINGS'))
+ (1, executable+': failed PIE LAZY_BINDINGS CONTROL_FLOW'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all']),
+ (1, executable+': failed PIE CONTROL_FLOW'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full']),
(1, executable+': failed PIE'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie','-Wl,-bind_at_load','-fstack-protector-all', '-fcf-protection=full']),
(0, ''))
clean_files(source, executable)
diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py
index 60acb0d593..5df87d9e70 100755
--- a/contrib/gitian-build.py
+++ b/contrib/gitian-build.py
@@ -210,7 +210,7 @@ def main():
args.macos = 'm' in args.os
# Disable for MacOS if no SDK found
- if args.macos and not os.path.isfile('gitian-builder/inputs/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz'):
+ if args.macos and not os.path.isfile('gitian-builder/inputs/Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz'):
print('Cannot build for MacOS, SDK does not exist. Will build for other OSes')
args.macos = False
diff --git a/contrib/gitian-descriptors/assign_DISTNAME b/contrib/gitian-descriptors/assign_DISTNAME
index a2ca768aaa..330fbc041b 100755..100644
--- a/contrib/gitian-descriptors/assign_DISTNAME
+++ b/contrib/gitian-descriptors/assign_DISTNAME
@@ -4,7 +4,7 @@
#
# A helper script to be sourced into the gitian descriptors
-if RECENT_TAG="$(git describe --exact-match HEAD)"; then
+if RECENT_TAG="$(git describe --exact-match HEAD 2> /dev/null)"; then
VERSION="${RECENT_TAG#v}"
else
VERSION="$(git rev-parse --short=12 HEAD)"
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index 52e2a0514a..bed3531720 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -23,6 +23,7 @@ packages:
- "patch"
- "pkg-config"
- "python3"
+- "python3-pip"
# Cross compilation HOSTS:
# - arm-linux-gnueabihf
- "binutils-arm-linux-gnueabihf"
@@ -55,8 +56,6 @@ script: |
HOST_CXXFLAGS="-O2 -g"
HOST_LDFLAGS_BASE="-static-libstdc++ -Wl,-O2"
- export QT_RCC_TEST=1
- export QT_RCC_SOURCE_DATE_OVERRIDE=1
export TZ="UTC"
export BUILD_DIR="$PWD"
mkdir -p ${WRAP_DIR}
@@ -100,6 +99,8 @@ script: |
done
}
+ pip3 install lief==0.11.4
+
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
create_global_faketime_wrappers "2000-01-01 12:00:00"
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index c37ad5b842..1d4506e3c2 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -23,6 +23,7 @@ packages:
- "imagemagick"
- "libz-dev"
- "python3"
+- "python3-pip"
- "python3-setuptools"
- "fonts-tuffy"
- "xorriso"
@@ -31,7 +32,7 @@ remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
files:
-- "Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz"
+- "Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz"
script: |
set -e -o pipefail
@@ -41,8 +42,6 @@ script: |
FAKETIME_HOST_PROGS=""
FAKETIME_PROGS="ar ranlib date dmg xorrisofs"
- export QT_RCC_TEST=1
- export QT_RCC_SOURCE_DATE_OVERRIDE=1
export TZ="UTC"
export BUILD_DIR="$PWD"
mkdir -p ${WRAP_DIR}
@@ -79,6 +78,8 @@ script: |
done
}
+ pip3 install lief==0.11.4
+
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
create_global_faketime_wrappers "2000-01-01 12:00:00"
@@ -89,7 +90,7 @@ script: |
BASEPREFIX="${PWD}/depends"
mkdir -p ${BASEPREFIX}/SDKs
- tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz
+ tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz
# Build dependencies for each host
for i in $HOSTS; do
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 95cf0185e2..03eba71366 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -22,6 +22,7 @@ packages:
- "zip"
- "ca-certificates"
- "python3"
+- "python3-pip"
remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
@@ -37,8 +38,6 @@ script: |
HOST_CFLAGS="-O2 -g -fno-ident"
HOST_CXXFLAGS="-O2 -g -fno-ident"
- export QT_RCC_TEST=1
- export QT_RCC_SOURCE_DATE_OVERRIDE=1
export TZ="UTC"
export BUILD_DIR="$PWD"
mkdir -p ${WRAP_DIR}
@@ -87,6 +86,8 @@ script: |
done
}
+ pip3 install lief==0.11.4
+
# Faketime for depends so intermediate results are comparable
export PATH_orig=${PATH}
create_global_faketime_wrappers "2000-01-01 12:00:00"
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
index 1122ec9ba5..e604b370e3 100644
--- a/contrib/guix/README.md
+++ b/contrib/guix/README.md
@@ -80,6 +80,50 @@ at the end of the `guix pull`)
export PATH="${HOME}/.config/guix/current/bin${PATH:+:}$PATH"
```
+### Controlling the number of threads used by `guix` build commands
+
+By default, the scripts under `./contrib/guix` will invoke all `guix` build
+commands with `--cores="$JOBS"`. Note that `$JOBS` defaults to `$(nproc)` if not
+specified. However, astute manual readers will also notice that there is a
+`--max-jobs=` flag (which defaults to 1 if unspecified).
+
+Here is the difference between `--cores=` and `--max-jobs=`:
+
+> Note: When I say "derivation," think "package"
+
+`--cores=`
+
+ - controls the number of CPU cores to build each derivation. This is the value
+ passed to `make`'s `--jobs=` flag.
+
+`--max-jobs=`
+
+ - controls how many derivations can be built in parallel
+ - defaults to 1
+
+Therefore, the default is for `guix` build commands to build one derivation at a
+time, utilizing `$JOBS` threads.
+
+Specifying the `$JOBS` environment variable will only modify `--cores=`, but you
+can also modify the value for `--max-jobs=` by specifying
+`$ADDITIONAL_GUIX_COMMON_FLAGS`. For example, if you have a LOT of memory, you
+may want to set:
+
+```sh
+export ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8'
+```
+
+Which allows for a maximum of 8 derivations to be built at the same time, each
+utilizing `$JOBS` threads.
+
+Or, if you'd like to avoid spurious build failures caused by issues with
+parallelism within a single package, but would still like to build multiple
+packages when the dependency graph allows for it, you may want to try:
+
+```sh
+export JOBS=1 ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8'
+```
+
## Usage
### As a Tool for Deterministic Builds
@@ -87,7 +131,7 @@ export PATH="${HOME}/.config/guix/current/bin${PATH:+:}$PATH"
From the top of a clean Bitcoin Core repository:
```sh
-./contrib/guix/guix-build.sh
+./contrib/guix/guix-build
```
After the build finishes successfully (check the status code please), compare
@@ -123,14 +167,18 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
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).
+ $HOME/Downloads/macOS-SDKs/Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers).
+
+* _**JOBS**_
+
+ Override the number of jobs to run simultaneously, you might want to do so on
+ a memory-limited machine. This may be passed to:
-* _**MAX_JOBS**_
+ - `guix` build commands as in `guix environment --cores="$JOBS"`
+ - `make` as in `make --jobs="$JOBS"`
+ - `xargs` as in `xargs -P"$JOBS"`
- Override the maximum number of jobs to run simultaneously, you might want to
- do so on a memory-limited machine. This may be passed to `make` as in `make
- --jobs="$MAX_JOBS"` or `xargs` as in `xargs -P"$MAX_JOBS"`. _(defaults to the
- value of `nproc` outside the container)_
+ _(defaults to the value of `nproc` outside the container)_
* _**SOURCE_DATE_EPOCH**_
@@ -157,10 +205,7 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
* _**ADDITIONAL_GUIX_COMMON_FLAGS**_
- Additional flags to be passed to all `guix` commands. For a fully-bootstrapped
- build, set this to `--bootstrap --no-substitutes` (refer to the [security
- model section](#choosing-your-security-model) for more details). Note that a
- fully-bootstrapped build will take quite a long time on the first run.
+ Additional flags to be passed to all `guix` commands.
* _**ADDITIONAL_GUIX_TIMEMACHINE_FLAGS**_
@@ -217,6 +262,57 @@ To use dongcarl's substitute server for Bitcoin Core builds after having
export SUBSTITUTE_URLS='https://guix.carldong.io https://ci.guix.gnu.org'
```
+## Troubleshooting
+
+### Derivation failed to build
+
+When you see a build failure like below:
+
+```
+building /gnu/store/...-foo-3.6.12.drv...
+/ 'check' phasenote: keeping build directory `/tmp/guix-build-foo-3.6.12.drv-0'
+builder for `/gnu/store/...-foo-3.6.12.drv' failed with exit code 1
+build of /gnu/store/...-foo-3.6.12.drv failed
+View build log at '/var/log/guix/drvs/../...-foo-3.6.12.drv.bz2'.
+cannot build derivation `/gnu/store/...-qux-7.69.1.drv': 1 dependencies couldn't be built
+cannot build derivation `/gnu/store/...-bar-3.16.5.drv': 1 dependencies couldn't be built
+cannot build derivation `/gnu/store/...-baz-2.0.5.drv': 1 dependencies couldn't be built
+guix time-machine: error: build of `/gnu/store/...-baz-2.0.5.drv' failed
+```
+
+It means that `guix` failed to build a package named `foo`, which was a
+dependency of `qux`, `bar`, and `baz`. Importantly, note that the last "failed"
+line is not necessarily the root cause, the first "failed" line is.
+
+Most of the time, the build failure is due to a spurious test failure or the
+package's build system/test suite breaking when running multi-threaded. To
+rebuild _just_ this derivation in a single-threaded fashion:
+
+```sh
+$ guix build --cores=1 /gnu/store/...-foo-3.6.12.drv
+```
+
+If the single-threaded rebuild did not succeed, you may need to dig deeper.
+You may view `foo`'s build logs in `less` like so (please replace paths with the
+path you see in the build failure output):
+
+```sh
+$ bzcat /var/log/guix/drvs/../...-foo-3.6.12.drv.bz2 | less
+```
+
+`foo`'s build directory is also preserved and available at
+`/tmp/guix-build-foo-3.6.12.drv-0`. However, if you fail to build `foo` multiple
+times, it may be `/tmp/...drv-1` or `/tmp/...drv-2`. Always consult the build
+failure output for the most accurate, up-to-date information.
+
+#### python(-minimal): [Errno 84] Invalid or incomplete multibyte or wide character
+
+This error occurs when your `$TMPDIR` (default: /tmp) exists on a filesystem
+which rejects characters not present in the UTF-8 character code set. An example
+is ZFS with the utf8only=on option set.
+
+More information: https://bugs.python.org/issue37584
+
## FAQ
### How can I trust the binary installation?
diff --git a/contrib/guix/guix-attest b/contrib/guix/guix-attest
new file mode 100755
index 0000000000..081d1c0465
--- /dev/null
+++ b/contrib/guix/guix-attest
@@ -0,0 +1,207 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
+###################
+## Sanity Checks ##
+###################
+
+################
+# Required non-builtin commands should be invokable
+################
+
+check_tools cat env basename mkdir xargs find
+if [ -z "$NO_SIGN" ]; then
+ check_tools gpg
+fi
+
+################
+# Required env vars should be non-empty
+################
+
+cmd_usage() {
+cat <<EOF
+Synopsis:
+
+ env GUIX_SIGS_REPO=<path/to/guix.sigs> \\
+ SIGNER=GPG_KEY_NAME[=SIGNER_NAME] \\
+ [ NO_SIGN=1 ]
+ ./contrib/guix/guix-attest
+
+Example w/o overriding signing name:
+
+ env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\
+ SIGNER=achow101 \\
+ ./contrib/guix/guix-attest
+
+Example overriding signing name:
+
+ env GUIX_SIGS_REPO=/home/dongcarl/guix.sigs \\
+ SIGNER=0x96AB007F1A7ED999=dongcarl \\
+ ./contrib/guix/guix-attest
+
+Example w/o signing, just creating SHA256SUMS:
+
+ env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\
+ SIGNER=achow101 \\
+ NO_SIGN=1 \\
+ ./contrib/guix/guix-attest
+
+EOF
+}
+
+if [ -z "$GUIX_SIGS_REPO" ] || [ -z "$SIGNER" ]; then
+ cmd_usage
+ exit 1
+fi
+
+################
+# GUIX_SIGS_REPO should exist as a directory
+################
+
+if [ ! -d "$GUIX_SIGS_REPO" ]; then
+cat << EOF
+ERR: The specified GUIX_SIGS_REPO is not an existent directory:
+
+ '$GUIX_SIGS_REPO'
+
+Hint: Please clone the guix.sigs repository and point to it with the
+ GUIX_SIGS_REPO environment variable.
+
+EOF
+cmd_usage
+exit 1
+fi
+
+################
+# The key specified in SIGNER should be usable
+################
+
+IFS='=' read -r gpg_key_name signer_name <<< "$SIGNER"
+if [ -z "${signer_name}" ]; then
+ signer_name="$gpg_key_name"
+fi
+
+if [ -z "$NO_SIGN" ] && ! gpg --dry-run --list-secret-keys "${gpg_key_name}" >/dev/null 2>&1; then
+ echo "ERR: GPG can't seem to find any key named '${gpg_key_name}'"
+ exit 1
+fi
+
+################
+# We should be able to find at least one output
+################
+
+echo "Looking for build output directories in ${OUTDIR_BASE}"
+
+shopt -s nullglob
+OUTDIRS=( "${OUTDIR_BASE}"/* ) # This expands to an array of directories...
+shopt -u nullglob
+
+if (( ${#OUTDIRS[@]} )); then
+ echo "Found build output directories:"
+ for outdir in "${OUTDIRS[@]}"; do
+ echo " '$outdir'"
+ done
+ echo
+else
+ echo "ERR: Could not find any build output directories in ${OUTDIR_BASE}"
+ exit 1
+fi
+
+
+##############
+## Attest ##
+##############
+
+# Usage: out_name $outdir
+#
+# HOST: The output directory being attested
+#
+out_name() {
+ basename "$1"
+}
+
+# Usage: out_sig_dir $outdir
+#
+# outdir: The output directory being attested
+#
+out_sig_dir() {
+ echo "$GUIX_SIGS_REPO/$VERSION/$(out_name "$1")/$signer_name"
+}
+
+# Accumulate a list of signature directories that already exist...
+outdirs_already_attested_to=()
+
+echo "Attesting to build outputs for version: '${VERSION}'"
+echo ""
+
+# MAIN LOGIC: Loop through each output for VERSION and attest to output in
+# GUIX_SIGS_REPO as SIGNER, if attestation does not exist
+for outdir in "${OUTDIRS[@]}"; do
+ if [ -e "${outdir}/SKIPATTEST.TAG" ]; then
+ echo "${outname}: SKIPPING: Output directory marked with SKIPATTEST.TAG file"
+ continue
+ fi
+ outname="$(out_name "$outdir")"
+ outsigdir="$(out_sig_dir "$outdir")"
+ if [ -e "$outsigdir" ]; then
+ echo "${outname}: SKIPPING: Signature directory already exists in the specified guix.sigs repository"
+ outdirs_already_attested_to+=("$outdir")
+ else
+ # Clean up incomplete sigdir if something fails (likely gpg)
+ trap 'rm -rf "$outsigdir"' ERR
+
+ mkdir -p "$outsigdir"
+
+ (
+ cd "$outdir"
+
+ if [ -e inputs.SHA256SUMS ]; then
+ echo "${outname}: Including existent input SHA256SUMS"
+ cat inputs.SHA256SUMS >> "$outsigdir"/SHA256SUMS
+ fi
+
+ echo "${outname}: Hashing build outputs to produce SHA256SUMS"
+ files="$(find -L . -type f ! -iname '*.SHA256SUMS')"
+ if [ -n "$files" ]; then
+ cut -c3- <<< "$files" | env LC_ALL=C sort | xargs sha256sum >> "$outsigdir"/SHA256SUMS
+ else
+ echo "ERR: ${outname}: No outputs found in '${outdir}'"
+ exit 1
+ fi
+ )
+ if [ -z "$NO_SIGN" ]; then
+ echo "${outname}: Signing SHA256SUMS to produce SHA256SUMS.asc"
+ gpg --detach-sign --local-user "$gpg_key_name" --armor --output "$outsigdir"/SHA256SUMS.asc "$outsigdir"/SHA256SUMS
+ else
+ echo "${outname}: Not signing SHA256SUMS as \$NO_SIGN is not empty"
+ fi
+ echo ""
+
+ trap - ERR # Reset ERR trap
+ fi
+done
+
+if (( ${#outdirs_already_attested_to[@]} )); then
+# ...so that we can print them out nicely in a warning message
+cat << EOF
+
+WARN: Signature directories from '$signer_name' already exist in the specified
+ guix.sigs repository for the following output directories and were
+ skipped:
+
+EOF
+for outdir in "${outdirs_already_attested_to[@]}"; do
+ echo " '${outdir}'"
+ echo " Corresponds to: '$(out_sig_dir "$outdir")'"
+ echo ""
+done
+fi
diff --git a/contrib/guix/guix-build.sh b/contrib/guix/guix-build
index 7c32fb5726..69c244a6fa 100755
--- a/contrib/guix/guix-build.sh
+++ b/contrib/guix/guix-build
@@ -2,22 +2,26 @@
export LC_ALL=C
set -e -o pipefail
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
###################
-## Sanity Checks ##
+## SANITY CHECKS ##
###################
################
-# Check 1: Make sure that we can invoke required tools
+# Required non-builtin commands should be invocable
################
-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
- fi
-done
+
+check_tools cat mkdir make git guix
################
-# Check 2: Make sure GUIX_BUILD_OPTIONS is empty
+# GUIX_BUILD_OPTIONS should be empty
################
#
# GUIX_BUILD_OPTIONS is an environment variable recognized by guix commands that
@@ -45,8 +49,9 @@ exit 1
fi
################
-# Check 3: Make sure that we're not in a dirty worktree
+# The git worktree should not be dirty
################
+
if ! git diff-index --quiet HEAD -- && [ -z "$FORCE_DIRTY_WORKTREE" ]; then
cat << EOF
ERR: The current git worktree is dirty, which may lead to broken builds.
@@ -60,12 +65,12 @@ Hint: To make your git worktree clean, You may want to:
using a dirty worktree
EOF
exit 1
-else
- GIT_COMMIT=$(git rev-parse --short=12 HEAD)
fi
+mkdir -p "$VERSION_BASE"
+
################
-# Check 4: Make sure that build directories do not exist
+# Build directories should not exist
################
# Default to building for all supported HOSTs (overridable by environment)
@@ -73,14 +78,12 @@ export HOSTS="${HOSTS:-x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu ri
x86_64-w64-mingw32
x86_64-apple-darwin18}"
-DISTSRC_BASE="${DISTSRC_BASE:-${PWD}}"
-
# Usage: distsrc_for_host HOST
#
# HOST: The current platform triple we're building for
#
distsrc_for_host() {
- echo "${DISTSRC_BASE}/distsrc-${GIT_COMMIT}-${1}"
+ echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}"
}
# Accumulate a list of build directories that already exist...
@@ -100,24 +103,31 @@ ERR: Build directories for this commit already exist for the following platform
Aborting...
+Hint: To blow everything away, you may want to use:
+
+ $ ./contrib/guix/guix-clean
+
+Specifically, this will remove all files without an entry in the index,
+excluding the SDK directory, the depends download cache, the depends built
+packages cache, the garbage collector roots for Guix environments, and the
+output directory.
EOF
for host in $hosts_distsrc_exists; do
echo " ${host} '$(distsrc_for_host "$host")'"
done
exit 1
else
-
mkdir -p "$DISTSRC_BASE"
fi
################
-# Check 5: When building for darwin, make sure that the macOS SDK exists
+# When building for darwin, the macOS SDK should exists
################
for host in $HOSTS; do
case "$host" in
*darwin*)
- OSX_SDK="$(make -C "${PWD}/depends" --no-print-directory HOST="$host" print-OSX_SDK | sed 's@^[^=]\+=[[:space:]]\+@@g')"
+ OSX_SDK="$(make -C "${PWD}/depends" --no-print-directory HOST="$host" print-OSX_SDK | sed 's@^[^=]\+=@@g')"
if [ -e "$OSX_SDK" ]; then
echo "Found macOS SDK at '${OSX_SDK}', using..."
else
@@ -128,13 +138,40 @@ for host in $HOSTS; do
esac
done
+################
+# Check that we can connect to the guix-daemon
+################
+
+cat << EOF
+Checking that we can connect to the guix-daemon...
+
+Hint: If this hangs, you may want to try turning your guix-daemon off and on
+ again.
+
+EOF
+if ! guix gc --list-failures > /dev/null; then
+cat << EOF
+
+ERR: Failed to connect to the guix-daemon, please ensure that one is running and
+ reachable.
+EOF
+exit 1
+fi
+
+# Developer note: we could use `guix repl` for this check and run:
+#
+# (import (guix store)) (close-connection (open-connection))
+#
+# However, the internal API is likely to change more than the CLI invocation
+
+
#########
-# Setup #
+# SETUP #
#########
# Determine the maximum number of jobs to run simultaneously (overridable by
# environment)
-MAX_JOBS="${MAX_JOBS:-$(nproc)}"
+JOBS="${JOBS:-$(nproc)}"
# Usage: host_to_commonname HOST
#
@@ -149,12 +186,6 @@ host_to_commonname() {
esac
}
-# Download the depends sources now as we won't have internet access in the build
-# container
-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)}"
@@ -164,19 +195,97 @@ time-machine() {
# shellcheck disable=SC2086
guix time-machine --url=https://github.com/dongcarl/guix.git \
--commit=490e39ff303f4f6873a04bfb8253755bdae1b29c \
- --max-jobs="$MAX_JOBS" \
+ --cores="$JOBS" \
--keep-failed \
+ --fallback \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} \
-- "$@"
}
+
+# Precious directories are those which should not be cleaned between successive
+# guix builds
+depends_precious_dir_names='SOURCES_PATH BASE_CACHE SDK_PATH'
+precious_dir_names="${depends_precious_dir_names} OUTDIR_BASE PROFILES_BASE"
+
+# Usage: contains IFS-SEPARATED-LIST ITEM
+contains() {
+ for i in ${1}; do
+ if [ "$i" = "${2}" ]; then
+ return 0 # Found!
+ fi
+ done
+ return 1
+}
+
+# If the user explicitly specified a precious directory, create it so we
+# can map it into the container
+for precious_dir_name in $precious_dir_names; do
+ precious_dir_path="${!precious_dir_name}"
+ if [ -n "$precious_dir_path" ]; then
+ if [ ! -e "$precious_dir_path" ]; then
+ mkdir -p "$precious_dir_path"
+ elif [ -L "$precious_dir_path" ]; then
+ echo "ERR: ${precious_dir_name} cannot be a symbolic link"
+ exit 1
+ elif [ ! -d "$precious_dir_path" ]; then
+ echo "ERR: ${precious_dir_name} must be a directory"
+ exit 1
+ fi
+ fi
+done
+
+mkdir -p "$VAR_BASE"
+
+# Record the _effective_ values of precious directories such that guix-clean can
+# avoid clobbering them if appropriate.
+#
+# shellcheck disable=SC2046,SC2086
+{
+ # Get depends precious dir definitions from depends
+ make -C "${PWD}/depends" \
+ --no-print-directory \
+ -- $(printf "print-%s\n" $depends_precious_dir_names)
+
+ # Get remaining precious dir definitions from the environment
+ for precious_dir_name in $precious_dir_names; do
+ precious_dir_path="${!precious_dir_name}"
+ if ! contains "$depends_precious_dir_names" "$precious_dir_name"; then
+ echo "${precious_dir_name}=${precious_dir_path}"
+ fi
+ done
+} > "${VAR_BASE}/precious_dirs"
+
# Make sure an output directory exists for our builds
-OUTDIR="${OUTDIR:-${PWD}/output}"
-[ -e "$OUTDIR" ] || mkdir -p "$OUTDIR"
+OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}"
+mkdir -p "$OUTDIR_BASE"
+
+# Download the depends sources now as we won't have internet access in the build
+# container
+for host in $HOSTS; do
+ make -C "${PWD}/depends" -j"$JOBS" download-"$(host_to_commonname "$host")" ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
+done
+
+# Usage: outdir_for_host HOST SUFFIX
+#
+# HOST: The current platform triple we're building for
+#
+outdir_for_host() {
+ echo "${OUTDIR_BASE}/${1}${2:+-${2}}"
+}
+
+# Usage: profiledir_for_host HOST SUFFIX
+#
+# HOST: The current platform triple we're building for
+#
+profiledir_for_host() {
+ echo "${PROFILES_BASE}/${1}${2:+-${2}}"
+}
+
#########
-# Build #
+# BUILD #
#########
# Function to be called when building for host ${1} and the user interrupts the
@@ -184,24 +293,19 @@ OUTDIR="${OUTDIR:-${PWD}/output}"
int_trap() {
cat << EOF
** INT received while building ${1}, you may want to clean up the relevant
- output, deploy, and distsrc-* directories before rebuilding
+ work directories (e.g. distsrc-*) before rebuilding
Hint: To blow everything away, you may want to use:
- $ git clean -xdff --exclude='/depends/SDKs/*'
+ $ ./contrib/guix/guix-clean
Specifically, this will remove all files without an entry in the index,
-excluding the SDK directory. Practically speaking, this means that all ignored
-and untracked files and directories will be wiped, allowing you to start anew.
+excluding the SDK directory, the depends download cache, the depends built
+packages cache, the garbage collector roots for Guix environments, and the
+output directory.
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
@@ -216,15 +320,15 @@ for host in $HOSTS; do
# shellcheck disable=SC2030
cat << EOF
-INFO: Building commit ${GIT_COMMIT:?not set} for platform triple ${HOST:?not set}:
+INFO: Building ${VERSION:?not set} for platform triple ${HOST:?not set}:
...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set}
- ...running at most ${MAX_JOBS:?not set} jobs
+ ...running at most ${JOBS:?not set} jobs
...from worktree directory: '${PWD}'
...bind-mounted in container to: '/bitcoin'
...in build directory: '$(distsrc_for_host "$HOST")'
...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")'
- ...outputting in: '${OUTDIR:?not set}'
- ...bind-mounted in container to: '/outdir'
+ ...outputting in: '$(outdir_for_host "$HOST")'
+ ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")'
EOF
# Run the build script 'contrib/guix/libexec/build.sh' in the build
@@ -299,24 +403,29 @@ EOF
--no-cwd \
--share="$PWD"=/bitcoin \
--share="$DISTSRC_BASE"=/distsrc-base \
- --share="$OUTDIR"=/outdir \
+ --share="$OUTDIR_BASE"=/outdir-base \
--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" \
+ --cores="$JOBS" \
--keep-failed \
+ --fallback \
+ --link-profile \
+ --root="$(profiledir_for_host "${HOST}")" \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
-- env HOST="$host" \
- MAX_JOBS="$MAX_JOBS" \
+ DISTNAME="$DISTNAME" \
+ JOBS="$JOBS" \
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 \
+ OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")" \
+ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
)
diff --git a/contrib/guix/guix-clean b/contrib/guix/guix-clean
new file mode 100755
index 0000000000..9fa17191e8
--- /dev/null
+++ b/contrib/guix/guix-clean
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
+###################
+## Sanity Checks ##
+###################
+
+################
+# Required non-builtin commands should be invokable
+################
+
+check_tools cat mkdir make git guix
+
+
+#############
+## Clean ##
+#############
+
+# Usage: under_dir MAYBE_PARENT MAYBE_CHILD
+#
+# If MAYBE_CHILD is a subdirectory of MAYBE_PARENT, print the relative path
+# from MAYBE_PARENT to MAYBE_CHILD. Otherwise, return 1 as the error code.
+#
+# NOTE: This does not perform any symlink-resolving or path canonicalization.
+#
+under_dir() {
+ local path_residue
+ path_residue="${2##${1}}"
+ if [ -z "$path_residue" ] || [ "$path_residue" = "$2" ]; then
+ return 1
+ else
+ echo "$path_residue"
+ fi
+}
+
+# Usage: dir_under_git_root MAYBE_CHILD
+#
+# If MAYBE_CHILD is under the current git repository and exists, print the
+# relative path from the git repository's top-level directory to MAYBE_CHILD,
+# otherwise, exit with an error code.
+#
+dir_under_git_root() {
+ local rv
+ rv="$(under_dir "$(git_root)" "$1")"
+ [ -n "$rv" ] && echo "$rv"
+}
+
+shopt -s nullglob
+found_precious_dirs_files=( "${version_base_prefix}"*/"${var_base_basename}/precious_dirs" ) # This expands to an array of directories...
+shopt -u nullglob
+
+exclude_flags=()
+
+for precious_dirs_file in "${found_precious_dirs_files[@]}"; do
+ # Make sure the precious directories (e.g. SOURCES_PATH, BASE_CACHE, SDK_PATH)
+ # are excluded from git-clean
+ echo "Found precious_dirs file: '${precious_dirs_file}'"
+
+ # Exclude the precious_dirs file itself
+ if dirs_file_exclude_fragment=$(dir_under_git_root "$(dirname "$precious_dirs_file")"); then
+ exclude_flags+=( --exclude="${dirs_file_exclude_fragment}/precious_dirs" )
+ fi
+
+ # Read each 'name=dir' pair from the precious_dirs file
+ while IFS='=' read -r name dir; do
+ # Add an exclusion flag if the precious directory is under the git root.
+ if under=$(dir_under_git_root "$dir"); then
+ echo "Avoiding ${name}: ${under}"
+ exclude_flags+=( --exclude="$under" )
+ fi
+ done < "$precious_dirs_file"
+done
+
+git clean -xdff "${exclude_flags[@]}"
diff --git a/contrib/guix/guix-codesign b/contrib/guix/guix-codesign
new file mode 100755
index 0000000000..62853dbf4d
--- /dev/null
+++ b/contrib/guix/guix-codesign
@@ -0,0 +1,392 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
+###################
+## SANITY CHECKS ##
+###################
+
+################
+# Required non-builtin commands should be invocable
+################
+
+check_tools cat mkdir git guix
+
+################
+# Required env vars should be non-empty
+################
+
+cmd_usage() {
+ cat <<EOF
+Synopsis:
+
+ env DETACHED_SIGS_REPO=<path/to/bitcoin-detached-sigs> \\
+ ./contrib/guix/guix-codesign
+
+EOF
+}
+
+if [ -z "$DETACHED_SIGS_REPO" ]; then
+ cmd_usage
+ exit 1
+fi
+
+################
+# GUIX_BUILD_OPTIONS should be empty
+################
+#
+# GUIX_BUILD_OPTIONS is an environment variable recognized by guix commands that
+# can perform builds. This seems like what we want instead of
+# ADDITIONAL_GUIX_COMMON_FLAGS, but the value of GUIX_BUILD_OPTIONS is actually
+# _appended_ to normal command-line options. Meaning that they will take
+# precedence over the command-specific ADDITIONAL_GUIX_<CMD>_FLAGS.
+#
+# This seems like a poor user experience. Thus we check for GUIX_BUILD_OPTIONS's
+# existence here and direct users of this script to use our (more flexible)
+# custom environment variables.
+if [ -n "$GUIX_BUILD_OPTIONS" ]; then
+cat << EOF
+Error: Environment variable GUIX_BUILD_OPTIONS is not empty:
+ '$GUIX_BUILD_OPTIONS'
+
+Unfortunately this script is incompatible with GUIX_BUILD_OPTIONS, please unset
+GUIX_BUILD_OPTIONS and use ADDITIONAL_GUIX_COMMON_FLAGS to set build options
+across guix commands or ADDITIONAL_GUIX_<CMD>_FLAGS to set build options for a
+specific guix command.
+
+See contrib/guix/README.md for more details.
+EOF
+exit 1
+fi
+
+################
+# The codesignature git worktree should not be dirty
+################
+
+if ! git -C "$DETACHED_SIGS_REPO" diff-index --quiet HEAD -- && [ -z "$FORCE_DIRTY_WORKTREE" ]; then
+ cat << EOF
+ERR: The DETACHED CODESIGNATURE git worktree is dirty, which may lead to broken builds.
+
+ Aborting...
+
+Hint: To make your git worktree clean, You may want to:
+ 1. Commit your changes,
+ 2. Stash your changes, or
+ 3. Set the 'FORCE_DIRTY_WORKTREE' environment variable if you insist on
+ using a dirty worktree
+EOF
+ exit 1
+fi
+
+################
+# Build directories should not exist
+################
+
+# Default to building for all supported HOSTs (overridable by environment)
+export HOSTS="${HOSTS:-x86_64-w64-mingw32 x86_64-apple-darwin18}"
+
+# Usage: distsrc_for_host HOST
+#
+# HOST: The current platform triple we're building for
+#
+distsrc_for_host() {
+ echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}-codesigned"
+}
+
+# Accumulate a list of build directories that already exist...
+hosts_distsrc_exists=""
+for host in $HOSTS; do
+ if [ -e "$(distsrc_for_host "$host")" ]; then
+ hosts_distsrc_exists+=" ${host}"
+ fi
+done
+
+if [ -n "$hosts_distsrc_exists" ]; then
+# ...so that we can print them out nicely in an error message
+cat << EOF
+ERR: Build directories for this commit already exist for the following platform
+ triples you're attempting to build, probably because of previous builds.
+ Please remove, or otherwise deal with them prior to starting another build.
+
+ Aborting...
+
+Hint: To blow everything away, you may want to use:
+
+ $ ./contrib/guix/guix-clean
+
+Specifically, this will remove all files without an entry in the index,
+excluding the SDK directory, the depends download cache, the depends built
+packages cache, the garbage collector roots for Guix environments, and the
+output directory.
+EOF
+for host in $hosts_distsrc_exists; do
+ echo " ${host} '$(distsrc_for_host "$host")'"
+done
+exit 1
+else
+ mkdir -p "$DISTSRC_BASE"
+fi
+
+
+################
+# Unsigned tarballs SHOULD exist
+################
+
+# Usage: outdir_for_host HOST SUFFIX
+#
+# HOST: The current platform triple we're building for
+#
+outdir_for_host() {
+ echo "${OUTDIR_BASE}/${1}${2:+-${2}}"
+}
+
+
+unsigned_tarball_for_host() {
+ case "$1" in
+ *mingw*)
+ echo "$(outdir_for_host "$1")/${DISTNAME}-win-unsigned.tar.gz"
+ ;;
+ *darwin*)
+ echo "$(outdir_for_host "$1")/${DISTNAME}-osx-unsigned.tar.gz"
+ ;;
+ *)
+ exit 1
+ ;;
+ esac
+}
+
+# Accumulate a list of build directories that already exist...
+hosts_unsigned_tarball_missing=""
+for host in $HOSTS; do
+ if [ ! -e "$(unsigned_tarball_for_host "$host")" ]; then
+ hosts_unsigned_tarball_missing+=" ${host}"
+ fi
+done
+
+if [ -n "$hosts_unsigned_tarball_missing" ]; then
+ # ...so that we can print them out nicely in an error message
+ cat << EOF
+ERR: Unsigned tarballs do not exist
+...
+
+EOF
+for host in $hosts_unsigned_tarball_missing; do
+ echo " ${host} '$(unsigned_tarball_for_host "$host")'"
+done
+exit 1
+fi
+
+################
+# Check that we can connect to the guix-daemon
+################
+
+cat << EOF
+Checking that we can connect to the guix-daemon...
+
+Hint: If this hangs, you may want to try turning your guix-daemon off and on
+ again.
+
+EOF
+if ! guix gc --list-failures > /dev/null; then
+ cat << EOF
+
+ERR: Failed to connect to the guix-daemon, please ensure that one is running and
+ reachable.
+EOF
+ exit 1
+fi
+
+# Developer note: we could use `guix repl` for this check and run:
+#
+# (import (guix store)) (close-connection (open-connection))
+#
+# However, the internal API is likely to change more than the CLI invocation
+
+
+#########
+# SETUP #
+#########
+
+# Determine the maximum number of jobs to run simultaneously (overridable by
+# environment)
+JOBS="${JOBS:-$(nproc)}"
+
+# Determine the reference time used for determinism (overridable by environment)
+SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git log --format=%at -1)}"
+
+# Execute "$@" in a pinned, possibly older version of Guix, for reproducibility
+# across time.
+time-machine() {
+ # shellcheck disable=SC2086
+ guix time-machine --url=https://github.com/dongcarl/guix.git \
+ --commit=490e39ff303f4f6873a04bfb8253755bdae1b29c \
+ --cores="$JOBS" \
+ --keep-failed \
+ --fallback \
+ ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
+ ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} \
+ -- "$@"
+}
+
+# Make sure an output directory exists for our builds
+OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}"
+mkdir -p "$OUTDIR_BASE"
+
+# Usage: profiledir_for_host HOST SUFFIX
+#
+# HOST: The current platform triple we're building for
+#
+profiledir_for_host() {
+ echo "${PROFILES_BASE}/${1}${2:+-${2}}"
+}
+
+#########
+# BUILD #
+#########
+
+# Function to be called when codesigning for host ${1} and the user interrupts
+# the codesign
+int_trap() {
+cat << EOF
+** INT received while codesigning ${1}, you may want to clean up the relevant
+ work directories (e.g. distsrc-*) before recodesigning
+
+Hint: To blow everything away, you may want to use:
+
+ $ ./contrib/guix/guix-clean
+
+Specifically, this will remove all files without an entry in the index,
+excluding the SDK directory, the depends download cache, the depends built
+packages cache, the garbage collector roots for Guix environments, and the
+output directory.
+EOF
+}
+
+# Deterministically build Bitcoin Core
+# shellcheck disable=SC2153
+for host in $HOSTS; do
+
+ # Display proper warning when the user interrupts the build
+ trap 'int_trap ${host}' INT
+
+ (
+ # Required for 'contrib/guix/manifest.scm' to output the right manifest
+ # for the particular $HOST we're building for
+ export HOST="$host"
+
+ # shellcheck disable=SC2030
+cat << EOF
+INFO: Codesigning ${VERSION:?not set} for platform triple ${HOST:?not set}:
+ ...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set}
+ ...from worktree directory: '${PWD}'
+ ...bind-mounted in container to: '/bitcoin'
+ ...in build directory: '$(distsrc_for_host "$HOST")'
+ ...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")'
+ ...outputting in: '$(outdir_for_host "$HOST" codesigned)'
+ ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)'
+ ...using detached signatures in: '${DETACHED_SIGS_REPO:?not set}'
+ ...bind-mounted in container to: '/detached-sigs'
+EOF
+
+
+ # Run the build script 'contrib/guix/libexec/build.sh' in the build
+ # container specified by 'contrib/guix/manifest.scm'.
+ #
+ # Explanation of `guix environment` flags:
+ #
+ # --container run command within an isolated container
+ #
+ # Running in an isolated container minimizes build-time differences
+ # between machines and improves reproducibility
+ #
+ # --pure unset existing environment variables
+ #
+ # Same rationale as --container
+ #
+ # --no-cwd do not share current working directory with an
+ # isolated container
+ #
+ # When --container is specified, the default behavior is to share
+ # the current working directory with the isolated container at the
+ # same exact path (e.g. mapping '/home/satoshi/bitcoin/' to
+ # '/home/satoshi/bitcoin/'). This means that the $PWD inside the
+ # container becomes a source of irreproducibility. --no-cwd disables
+ # this behaviour.
+ #
+ # --share=SPEC for containers, share writable host file system
+ # according to SPEC
+ #
+ # --share="$PWD"=/bitcoin
+ #
+ # maps our current working directory to /bitcoin
+ # inside the isolated container, which we later cd
+ # into.
+ #
+ # While we don't want to map our current working directory to the
+ # same exact path (as this introduces irreproducibility), we do want
+ # it to be at a _fixed_ path _somewhere_ inside the isolated
+ # container so that we have something to build. '/bitcoin' was
+ # chosen arbitrarily.
+ #
+ # ${SOURCES_PATH:+--share="$SOURCES_PATH"}
+ #
+ # make the downloaded depends sources path available
+ # inside the isolated container
+ #
+ # The isolated container has no network access as it's in a
+ # different network namespace from the main machine, so we have to
+ # make the downloaded depends sources available to it. The sources
+ # should have been downloaded prior to this invocation.
+ #
+ # ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"}
+ #
+ # fetch substitute from SUBSTITUTE_URLS if they are
+ # authorized
+ #
+ # Depending on the user's security model, it may be desirable to use
+ # substitutes (pre-built packages) from servers that the user trusts.
+ # Please read the README.md in the same directory as this file for
+ # more information.
+ #
+ # shellcheck disable=SC2086,SC2031
+ time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \
+ --container \
+ --pure \
+ --no-cwd \
+ --share="$PWD"=/bitcoin \
+ --share="$DISTSRC_BASE"=/distsrc-base \
+ --share="$OUTDIR_BASE"=/outdir-base \
+ --share="$DETACHED_SIGS_REPO"=/detached-sigs \
+ --expose="$(git rev-parse --git-common-dir)" \
+ --expose="$(git -C "$DETACHED_SIGS_REPO" rev-parse --git-common-dir)" \
+ ${SOURCES_PATH:+--share="$SOURCES_PATH"} \
+ --cores="$JOBS" \
+ --keep-failed \
+ --fallback \
+ --link-profile \
+ --root="$(profiledir_for_host "${HOST}" codesigned)" \
+ ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
+ ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
+ -- env HOST="$host" \
+ DISTNAME="$DISTNAME" \
+ JOBS="$JOBS" \
+ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \
+ ${V:+V=1} \
+ ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
+ DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \
+ OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)" \
+ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \
+ DETACHED_SIGS_REPO=/detached-sigs \
+ UNSIGNED_TARBALL="$(OUTDIR_BASE=/outdir-base && unsigned_tarball_for_host "$HOST")" \
+ bash -c "cd /bitcoin && bash contrib/guix/libexec/codesign.sh"
+ )
+
+done
diff --git a/contrib/guix/guix-verify b/contrib/guix/guix-verify
new file mode 100755
index 0000000000..629050956c
--- /dev/null
+++ b/contrib/guix/guix-verify
@@ -0,0 +1,113 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
+###################
+## Sanity Checks ##
+###################
+
+################
+# Required non-builtin commands should be invokable
+################
+
+check_tools cat diff gpg
+
+################
+# Required env vars should be non-empty
+################
+
+cmd_usage() {
+cat <<EOF
+Synopsis:
+
+ env GUIX_SIGS_REPO=<path/to/guix.sigs> ./contrib/guix/guix-verify
+
+EOF
+}
+
+if [ -z "$GUIX_SIGS_REPO" ]; then
+ cmd_usage
+ exit 1
+fi
+
+################
+# GUIX_SIGS_REPO should exist as a directory
+################
+
+if [ ! -d "$GUIX_SIGS_REPO" ]; then
+cat << EOF
+ERR: The specified GUIX_SIGS_REPO is not an existent directory:
+
+ '$GUIX_SIGS_REPO'
+
+Hint: Please clone the guix.sigs repository and point to it with the
+ GUIX_SIGS_REPO environment variable.
+
+EOF
+cmd_usage
+exit 1
+fi
+
+################
+# We should be able to find at least one output
+################
+
+OUTSIGDIR_BASE="${GUIX_SIGS_REPO}/${VERSION}"
+echo "Looking for output signature directories in '${OUTSIGDIR_BASE}'"
+
+shopt -s nullglob
+OUTSIGDIRS=( "$OUTSIGDIR_BASE"/* ) # This expands to an array of directories...
+shopt -u nullglob
+
+if (( ${#OUTSIGDIRS[@]} )); then
+ echo "Found output signature directories:"
+ for outsigdir in "${OUTSIGDIRS[@]}"; do
+ echo " '$outsigdir'"
+ done
+ echo
+else
+ echo "ERR: Could not find any output signature directories in ${OUTSIGDIR_BASE}"
+ exit 1
+fi
+
+
+##############
+## Verify ##
+##############
+
+# MAIN LOGIC: Loop through each output for VERSION and check that the SHA256SUMS
+# and SHA256SUMS.asc file match between signers, using the first
+# available signer as the arbitrary comparison base.
+for outsigdir in "${OUTSIGDIRS[@]}"; do
+ echo "BEGIN: Checking output signatures for $(basename "$outsigdir")"
+ echo ""
+ signer_dirs=( "$outsigdir"/* ) # This expands to an array of directories...
+ compare_signer_dir="${signer_dirs[0]}" # ...we just want the first one
+ for current_signer_dir in "${signer_dirs[@]}"; do
+ if ! gpg --quiet --batch --verify "$current_signer_dir"/SHA256SUMS.asc "$current_signer_dir"/SHA256SUMS; then
+ echo "ERR: Failed to verify GPG signature in '${current_signer_dir}/SHA256SUMS.asc'"
+ echo ""
+ echo "Hint: Either the signature is invalid or the public key is missing"
+ echo ""
+ elif ! diff --report-identical "$compare_signer_dir"/SHA256SUMS "$current_signer_dir"/SHA256SUMS; then
+ echo "ERR: The SHA256SUMS attestation in these two directories differ:"
+ echo " '${compare_signer_dir}'"
+ echo " '${current_signer_dir}'"
+ echo ""
+ else
+ echo "Verified: '${current_signer_dir}'"
+ echo ""
+ fi
+ done
+ echo "DONE: Checking output signatures for $(basename "$outsigdir")"
+ echo ""
+ echo ""
+done
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 051066a6a0..46bfa29b74 100644..100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -24,13 +24,18 @@ fi
# Check that required environment variables are set
cat << EOF
Required environment variables as seen inside the container:
+ DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set}
+ DISTNAME: ${DISTNAME:?not set}
HOST: ${HOST:?not set}
SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:?not set}
- MAX_JOBS: ${MAX_JOBS:?not set}
+ JOBS: ${JOBS:?not set}
DISTSRC: ${DISTSRC:?not set}
OUTDIR: ${OUTDIR:?not set}
EOF
+ACTUAL_OUTDIR="${OUTDIR}"
+OUTDIR="${DISTSRC}/output"
+
#####################
# Environment Setup #
#####################
@@ -52,16 +57,36 @@ store_path() {
# Set environment variables to point the NATIVE toolchain to the right
# includes/libs
NATIVE_GCC="$(store_path gcc-toolchain)"
-export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64"
-export CPATH="${NATIVE_GCC}/include"
+NATIVE_GCC_STATIC="$(store_path gcc-toolchain static)"
+
+unset LIBRARY_PATH
+unset CPATH
unset C_INCLUDE_PATH
unset CPLUS_INCLUDE_PATH
+unset OBJC_INCLUDE_PATH
+unset OBJCPLUS_INCLUDE_PATH
+
+export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64:${NATIVE_GCC_STATIC}/lib:${NATIVE_GCC_STATIC}/lib64"
+export C_INCLUDE_PATH="${NATIVE_GCC}/include"
+export CPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include"
+export OBJC_INCLUDE_PATH="${NATIVE_GCC}/include"
+export OBJCPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include"
+
+prepend_to_search_env_var() {
+ export "${1}=${2}${!1:+:}${!1}"
+}
+
case "$HOST" in
*darwin*)
# 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}"
+ zlib_static_store_path=$(store_path "zlib" static)
+
+ prepend_to_search_env_var LIBRARY_PATH "${zlib_static_store_path}/lib:${zlib_store_path}/lib"
+ prepend_to_search_env_var C_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var CPLUS_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var OBJC_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var OBJCPLUS_INCLUDE_PATH "${zlib_store_path}/include"
esac
# Set environment variables to point the CROSS toolchain to the right
@@ -153,8 +178,6 @@ case "$HOST" in
esac
# Environment variables for determinism
-export QT_RCC_TEST=1
-export QT_RCC_SOURCE_DATE_OVERRIDE=1
export TAR_OPTIONS="--owner=0 --group=0 --numeric-owner --mtime='@${SOURCE_DATE_EPOCH}' --sort=name"
export TZ="UTC"
case "$HOST" in
@@ -173,7 +196,7 @@ esac
####################
# Build the depends tree, overriding variables that assume multilib gcc
-make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
+make -C depends --jobs="$JOBS" HOST="$HOST" \
${V:+V=1} \
${SOURCES_PATH+SOURCES_PATH="$SOURCES_PATH"} \
${BASE_CACHE+BASE_CACHE="$BASE_CACHE"} \
@@ -198,18 +221,30 @@ make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
# Source Tarball Building #
###########################
-# Define DISTNAME variable.
-# shellcheck source=contrib/gitian-descriptors/assign_DISTNAME
-source contrib/gitian-descriptors/assign_DISTNAME
-
-GIT_ARCHIVE="${OUTDIR}/src/${DISTNAME}.tar.gz"
+GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}.tar.gz"
# Create the source tarball if not already there
if [ ! -e "$GIT_ARCHIVE" ]; then
mkdir -p "$(dirname "$GIT_ARCHIVE")"
+ touch "${DIST_ARCHIVE_BASE}"/SKIPATTEST.TAG
git archive --prefix="${DISTNAME}/" --output="$GIT_ARCHIVE" HEAD
fi
+# tmpdir="$(mktemp -d)"
+# (
+# cd "$tmpdir"
+# mkdir -p inputs
+# ln -sf --target-directory=inputs "$GIT_ARCHIVE"
+
+# mkdir -p "$OUTDIR"
+# find -L inputs -type f -print0 | xargs -0 sha256sum > "${OUTDIR}/inputs.SHA256SUMS"
+# )
+
+mkdir -p "$OUTDIR"
+cat << EOF > "$OUTDIR"/inputs.SHA256SUMS
+$(sha256sum "$GIT_ARCHIVE" | cut -d' ' -f1) inputs/$(basename "$GIT_ARCHIVE")
+EOF
+
###########################
# Binary Tarball Building #
###########################
@@ -238,7 +273,7 @@ case "$HOST" in
esac
case "$HOST" in
- powerpc64-linux-*) HOST_LDFLAGS="${HOST_LDFLAGS} -Wl,-z,noexecstack" ;;
+ powerpc64-linux-*|riscv64-linux-*) HOST_LDFLAGS="${HOST_LDFLAGS} -Wl,-z,noexecstack" ;;
esac
# Make $HOST-specific native binaries from depends available in $PATH
@@ -267,7 +302,7 @@ mkdir -p "$DISTSRC"
sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool
# Build Bitcoin Core
- make --jobs="$MAX_JOBS" ${V:+V=1}
+ make --jobs="$JOBS" ${V:+V=1}
# Perform basic ELF security checks on a series of executables.
make -C src --jobs=1 check-security ${V:+V=1}
@@ -275,6 +310,8 @@ mkdir -p "$DISTSRC"
# version symbols for Linux distro back-compatibility.
make -C src --jobs=1 check-symbols ${V:+V=1}
+ mkdir -p "$OUTDIR"
+
# Make the os-specific installers
case "$HOST" in
*mingw*)
@@ -306,9 +343,6 @@ mkdir -p "$DISTSRC"
osx_volname \
contrib/macdeploy/detached-sig-{apply,create}.sh \
"${BASEPREFIX}/${HOST}"/native/bin/dmg
- for util in codesign_allocate pagestuff; do
- cp --no-target-directory {"${BASEPREFIX}/${HOST}/native/bin/${HOST}-","unsigned-app-${HOST}/"}"$util"
- done
mv --target-directory="unsigned-app-${HOST}" dist
(
cd "unsigned-app-${HOST}"
@@ -344,7 +378,7 @@ mkdir -p "$DISTSRC"
{
find "${DISTNAME}/bin" -type f -executable -print0
find "${DISTNAME}/lib" -type f -print0
- } | xargs -0 -n1 -P"$MAX_JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg
+ } | xargs -0 -n1 -P"$JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg
;;
esac
@@ -394,21 +428,23 @@ mkdir -p "$DISTSRC"
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-apple-darwin18/osx64}.tar.gz" && exit 1 )
;;
esac
- )
-)
+ ) # $DISTSRC/installed
-case "$HOST" in
- *mingw*)
- cp -rf --target-directory=. contrib/windeploy
- (
- cd ./windeploy
- mkdir unsigned
- cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-setup-unsigned.exe"
- find . -print0 \
- | sort --zero-terminated \
- | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
- | gzip -9n > "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" \
- || ( rm -f "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" && exit 1 )
- )
- ;;
-esac
+ case "$HOST" in
+ *mingw*)
+ cp -rf --target-directory=. contrib/windeploy
+ (
+ cd ./windeploy
+ mkdir -p unsigned
+ cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-setup-unsigned.exe"
+ find . -print0 \
+ | sort --zero-terminated \
+ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
+ | gzip -9n > "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" \
+ || ( rm -f "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" && exit 1 )
+ )
+ ;;
+ esac
+) # $DISTSRC
+
+mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR"
diff --git a/contrib/guix/libexec/codesign.sh b/contrib/guix/libexec/codesign.sh
new file mode 100755
index 0000000000..46b42a5712
--- /dev/null
+++ b/contrib/guix/libexec/codesign.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+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.
+ set -vx
+ # Set VERBOSE for CMake-based builds
+ export VERBOSE="$V"
+fi
+
+# Check that required environment variables are set
+cat << EOF
+Required environment variables as seen inside the container:
+ UNSIGNED_TARBALL: ${UNSIGNED_TARBALL:?not set}
+ DETACHED_SIGS_REPO: ${DETACHED_SIGS_REPO:?not set}
+ DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set}
+ DISTNAME: ${DISTNAME:?not set}
+ HOST: ${HOST:?not set}
+ SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:?not set}
+ DISTSRC: ${DISTSRC:?not set}
+ OUTDIR: ${OUTDIR:?not set}
+EOF
+
+ACTUAL_OUTDIR="${OUTDIR}"
+OUTDIR="${DISTSRC}/output"
+
+git_head_version() {
+ local recent_tag
+ if recent_tag="$(git -C "$1" describe --exact-match HEAD 2> /dev/null)"; then
+ echo "${recent_tag#v}"
+ else
+ git -C "$1" rev-parse --short=12 HEAD
+ fi
+}
+
+CODESIGNATURE_GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}-codesignatures-$(git_head_version "$DETACHED_SIGS_REPO").tar.gz"
+
+# Create the codesignature tarball if not already there
+if [ ! -e "$CODESIGNATURE_GIT_ARCHIVE" ]; then
+ mkdir -p "$(dirname "$CODESIGNATURE_GIT_ARCHIVE")"
+ git -C "$DETACHED_SIGS_REPO" archive --output="$CODESIGNATURE_GIT_ARCHIVE" HEAD
+fi
+
+mkdir -p "$OUTDIR"
+cat << EOF > "$OUTDIR"/inputs.SHA256SUMS
+$(sha256sum "$UNSIGNED_TARBALL" | cut -d' ' -f1) inputs/$(basename "$UNSIGNED_TARBALL")
+$(sha256sum "$CODESIGNATURE_GIT_ARCHIVE" | cut -d' ' -f1) inputs/$(basename "$CODESIGNATURE_GIT_ARCHIVE")
+EOF
+
+mkdir -p "$DISTSRC"
+(
+ cd "$DISTSRC"
+
+ tar -xf "$UNSIGNED_TARBALL"
+
+ mkdir -p codesignatures
+ tar -C codesignatures -xf "$CODESIGNATURE_GIT_ARCHIVE"
+
+ case "$HOST" in
+ *mingw*)
+ find "$PWD" -name "*-unsigned.exe" | while read -r infile; do
+ infile_base="$(basename "$infile")"
+
+ # Codesigned *-unsigned.exe and output to OUTDIR
+ osslsigncode attach-signature \
+ -in "$infile" \
+ -out "${OUTDIR}/${infile_base/-unsigned}" \
+ -sigin codesignatures/win/"$infile_base".pem
+ done
+ ;;
+ *darwin*)
+ # Apply detached codesignatures to dist/ (in-place)
+ signapple apply dist/Bitcoin-Qt.app codesignatures/osx/dist
+
+ # Make an uncompressed DMG from dist/
+ xorrisofs -D -l -V "$(< osx_volname)" -no-pad -r -dir-mode 0755 \
+ -o uncompressed.dmg \
+ dist \
+ -- -volume_date all_file_dates ="$SOURCE_DATE_EPOCH"
+
+ # Compress uncompressed.dmg and output to OUTDIR
+ ./dmg dmg uncompressed.dmg "${OUTDIR}/${DISTNAME}-osx-signed.dmg"
+ ;;
+ *)
+ exit 1
+ ;;
+ esac
+) # $DISTSRC
+
+mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR"
diff --git a/contrib/guix/libexec/prelude.bash b/contrib/guix/libexec/prelude.bash
new file mode 100644
index 0000000000..9705607119
--- /dev/null
+++ b/contrib/guix/libexec/prelude.bash
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# shellcheck source=../../shell/realpath.bash
+source contrib/shell/realpath.bash
+
+# shellcheck source=../../shell/git-utils.bash
+source contrib/shell/git-utils.bash
+
+################
+# Required non-builtin commands should be invocable
+################
+
+check_tools() {
+ for cmd in "$@"; 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
+ fi
+ done
+}
+
+check_tools cat env readlink dirname basename git
+
+################
+# We should be at the top directory of the repository
+################
+
+same_dir() {
+ local resolved1 resolved2
+ resolved1="$(bash_realpath "${1}")"
+ resolved2="$(bash_realpath "${2}")"
+ [ "$resolved1" = "$resolved2" ]
+}
+
+if ! same_dir "${PWD}" "$(git_root)"; then
+cat << EOF
+ERR: This script must be invoked from the top level of the git repository
+
+Hint: This may look something like:
+ env FOO=BAR ./contrib/guix/guix-<blah>
+
+EOF
+exit 1
+fi
+
+################
+# Set common variables
+################
+
+VERSION="${VERSION:-$(git_head_version)}"
+DISTNAME="${DISTNAME:-bitcoin-${VERSION}}"
+
+version_base_prefix="${PWD}/guix-build-"
+VERSION_BASE="${version_base_prefix}${VERSION}" # TOP
+
+DISTSRC_BASE="${DISTSRC_BASE:-${VERSION_BASE}}"
+
+OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}"
+
+var_base_basename="var"
+VAR_BASE="${VAR_BASE:-${VERSION_BASE}/${var_base_basename}}"
+
+profiles_base_basename="profiles"
+PROFILES_BASE="${PROFILES_BASE:-${VAR_BASE}/${profiles_base_basename}}"
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 4228532cb1..d2bc789b60 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -4,12 +4,14 @@
(gnu packages base)
(gnu packages bash)
(gnu packages bison)
+ (gnu packages certs)
(gnu packages cdrom)
(gnu packages check)
(gnu packages cmake)
(gnu packages commencement)
(gnu packages compression)
(gnu packages cross-base)
+ (gnu packages curl)
(gnu packages file)
(gnu packages gawk)
(gnu packages gcc)
@@ -23,13 +25,17 @@
(gnu packages perl)
(gnu packages pkg-config)
(gnu packages python)
+ (gnu packages python-web)
(gnu packages shells)
+ (gnu packages tls)
(gnu packages version-control)
(guix build-system font)
(guix build-system gnu)
+ (guix build-system python)
(guix build-system trivial)
(guix download)
(guix gexp)
+ (guix git-download)
((guix licenses) #:prefix license:)
(guix packages)
(guix profiles)
@@ -50,28 +56,28 @@ we link against libssp.so, and thus will ensure that this works properly.
Taken from:
http://www.linuxfromscratch.org/hlfs/view/development/chapter05/gcc-pass1.html"
(package
- (inherit xgcc)
- (arguments
- (substitute-keyword-arguments (package-arguments xgcc)
- ((#:make-flags flags)
- `(cons "gcc_cv_libc_provides_ssp=yes" ,flags))))))
+ (inherit xgcc)
+ (arguments
+ (substitute-keyword-arguments (package-arguments xgcc)
+ ((#:make-flags flags)
+ `(cons "gcc_cv_libc_provides_ssp=yes" ,flags))))))
(define (make-gcc-rpath-link xgcc)
"Given a XGCC package, return a modified package that replace each instance of
-rpath in the default system spec that's inserted by Guix with -rpath-link"
(package
- (inherit xgcc)
- (arguments
- (substitute-keyword-arguments (package-arguments xgcc)
- ((#:phases phases)
- `(modify-phases ,phases
- (add-after 'pre-configure 'replace-rpath-with-rpath-link
- (lambda _
- (substitute* (cons "gcc/config/rs6000/sysv4.h"
- (find-files "gcc/config"
- "^gnu-user.*\\.h$"))
- (("-rpath=") "-rpath-link="))
- #t))))))))
+ (inherit xgcc)
+ (arguments
+ (substitute-keyword-arguments (package-arguments xgcc)
+ ((#:phases phases)
+ `(modify-phases ,phases
+ (add-after 'pre-configure 'replace-rpath-with-rpath-link
+ (lambda _
+ (substitute* (cons "gcc/config/rs6000/sysv4.h"
+ (find-files "gcc/config"
+ "^gnu-user.*\\.h$"))
+ (("-rpath=") "-rpath-link="))
+ #t))))))))
(define (make-cross-toolchain target
base-gcc-for-libc
@@ -124,19 +130,23 @@ chain for " target " development."))
(home-page (package-home-page xgcc))
(license (package-license xgcc)))))
+(define base-gcc
+ (package-with-extra-patches gcc-8
+ (search-our-patches "gcc-8-sort-libtool-find-output.patch")))
+
(define* (make-bitcoin-cross-toolchain target
- #:key
- (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)))
+ #:key
+ (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 base-gcc)))
"Convenience wrapper around MAKE-CROSS-TOOLCHAIN with default values
desirable for building Bitcoin Core release binaries."
(make-cross-toolchain target
- base-gcc-for-libc
- base-kernel-headers
- base-libc
- base-gcc))
+ base-gcc-for-libc
+ base-kernel-headers
+ base-libc
+ base-gcc))
(define (make-gcc-with-pthreads gcc)
(package-with-extra-configure-variable gcc "--enable-threads" "posix"))
@@ -147,7 +157,7 @@ desirable for building Bitcoin Core release binaries."
(pthreads-xlibc mingw-w64-x86_64-winpthreads)
(pthreads-xgcc (make-gcc-with-pthreads
(cross-gcc target
- #:xgcc (make-ssp-fixed-gcc gcc-9)
+ #:xgcc (make-ssp-fixed-gcc base-gcc)
#:xbinutils xbinutils
#:libc pthreads-xlibc))))
;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and
@@ -175,22 +185,376 @@ chain for " target " development."))
(define-public font-tuffy
(package
- (name "font-tuffy")
- (version "20120614")
+ (name "font-tuffy")
+ (version "20120614")
+ (source
+ (origin
+ (method url-fetch)
+ (uri (string-append "http://tulrich.com/fonts/tuffy-" version ".tar.gz"))
+ (file-name (string-append name "-" version ".tar.gz"))
+ (sha256
+ (base32
+ "02vf72bgrp30vrbfhxjw82s115z27dwfgnmmzfb0n9wfhxxfpyf6"))))
+ (build-system font-build-system)
+ (home-page "http://tulrich.com/fonts/")
+ (synopsis "The Tuffy Truetype Font Family")
+ (description
+ "Thatcher Ulrich's first outline font design. He started with the goal of producing a neutral, readable sans-serif text font. There are lots of \"expressive\" fonts out there, but he wanted to start with something very plain and clean, something he might want to actually use. ")
+ (license license:public-domain)))
+
+(define-public lief
+ (package
+ (name "python-lief")
+ (version "0.11.4")
(source
(origin
- (method url-fetch)
- (uri (string-append "http://tulrich.com/fonts/tuffy-" version ".tar.gz"))
- (file-name (string-append name "-" version ".tar.gz"))
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/lief-project/LIEF.git")
+ (commit version)))
+ (file-name (git-file-name name version))
(sha256
(base32
- "02vf72bgrp30vrbfhxjw82s115z27dwfgnmmzfb0n9wfhxxfpyf6"))))
- (build-system font-build-system)
- (home-page "http://tulrich.com/fonts/")
- (synopsis "The Tuffy Truetype Font Family")
- (description
- "Thatcher Ulrich's first outline font design. He started with the goal of producing a neutral, readable sans-serif text font. There are lots of \"expressive\" fonts out there, but he wanted to start with something very plain and clean, something he might want to actually use. ")
- (license license:public-domain)))
+ "0h4kcwr9z478almjqhmils8imfpflzk0r7d05g4xbkdyknn162qf"))))
+ (build-system python-build-system)
+ (native-inputs
+ `(("cmake" ,cmake)))
+ (home-page "https://github.com/lief-project/LIEF")
+ (synopsis "Library to Instrument Executable Formats")
+ (description "Python library to to provide a cross platform library which can
+parse, modify and abstract ELF, PE and MachO formats.")
+ (license license:asl2.0)))
+
+(define osslsigncode
+ (package
+ (name "osslsigncode")
+ (version "2.0")
+ (source (origin
+ (method url-fetch)
+ (uri (string-append "https://github.com/mtrojnar/"
+ name "/archive/" version ".tar.gz"))
+ (sha256
+ (base32
+ "0byri6xny770wwb2nciq44j5071122l14bvv65axdd70nfjf0q2s"))))
+ (build-system gnu-build-system)
+ (native-inputs
+ `(("pkg-config" ,pkg-config)
+ ("autoconf" ,autoconf)
+ ("automake" ,automake)
+ ("libtool" ,libtool)))
+ (inputs
+ `(("openssl" ,openssl)))
+ (arguments
+ `(#:configure-flags
+ `("--without-gsf"
+ "--without-curl"
+ "--disable-dependency-tracking")))
+ (home-page "https://github.com/mtrojnar/osslsigncode")
+ (synopsis "Authenticode signing and timestamping tool")
+ (description "osslsigncode is a small tool that implements part of the
+functionality of the Microsoft tool signtool.exe - more exactly the Authenticode
+signing and timestamping. But osslsigncode is based on OpenSSL and cURL, and
+thus should be able to compile on most platforms where these exist.")
+ (license license:gpl3+))) ; license is with openssl exception
+
+(define-public python-asn1crypto
+ (package
+ (name "python-asn1crypto")
+ (version "1.4.0")
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/wbond/asn1crypto")
+ (commit version)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "19abibn6jw20mzi1ln4n9jjvpdka8ygm4m439hplyrdfqbvgm01r"))))
+ (build-system python-build-system)
+ (arguments
+ '(#:phases
+ (modify-phases %standard-phases
+ (replace 'check
+ (lambda _
+ (invoke "python" "run.py" "tests"))))))
+ (home-page "https://github.com/wbond/asn1crypto")
+ (synopsis "ASN.1 parser and serializer in Python")
+ (description "asn1crypto is an ASN.1 parser and serializer with definitions
+for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7,
+PKCS#8, PKCS#12, PKCS#5, X.509 and TSP.")
+ (license license:expat)))
+
+(define-public python-elfesteem
+ (let ((commit "87bbd79ab7e361004c98cc8601d4e5f029fd8bd5"))
+ (package
+ (name "python-elfesteem")
+ (version (git-version "0.1" "1" commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/LRGH/elfesteem")
+ (commit commit)))
+ (file-name (git-file-name name commit))
+ (sha256
+ (base32
+ "1nyvjisvyxyxnd0023xjf5846xd03lwawp5pfzr8vrky7wwm5maz"))))
+ (build-system python-build-system)
+ ;; There are no tests, but attempting to run python setup.py test leads to
+ ;; PYTHONPATH problems, just disable the test
+ (arguments '(#:tests? #f))
+ (home-page "https://github.com/LRGH/elfesteem")
+ (synopsis "ELF/PE/Mach-O parsing library")
+ (description "elfesteem parses ELF, PE and Mach-O files.")
+ (license license:lgpl2.1))))
+
+(define-public python-oscrypto
+ (package
+ (name "python-oscrypto")
+ (version "1.2.1")
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/wbond/oscrypto")
+ (commit version)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "1d4d8s4z340qhvb3g5m5v3436y3a71yc26wk4749q64m09kxqc3l"))
+ (patches (search-our-patches "oscrypto-hard-code-openssl.patch"))))
+ (build-system python-build-system)
+ (native-search-paths
+ (list (search-path-specification
+ (variable "SSL_CERT_FILE")
+ (file-type 'regular)
+ (separator #f) ;single entry
+ (files '("etc/ssl/certs/ca-certificates.crt")))))
+
+ (propagated-inputs
+ `(("python-asn1crypto" ,python-asn1crypto)
+ ("openssl" ,openssl)))
+ (arguments
+ `(#:phases
+ (modify-phases %standard-phases
+ (add-after 'unpack 'hard-code-path-to-libscrypt
+ (lambda* (#:key inputs #:allow-other-keys)
+ (let ((openssl (assoc-ref inputs "openssl")))
+ (substitute* "oscrypto/__init__.py"
+ (("@GUIX_OSCRYPTO_USE_OPENSSL@")
+ (string-append openssl "/lib/libcrypto.so" "," openssl "/lib/libssl.so")))
+ #t)))
+ (add-after 'unpack 'disable-broken-tests
+ (lambda _
+ ;; This test is broken as there is no keyboard interrupt.
+ (substitute* "tests/test_trust_list.py"
+ (("^(.*)class TrustListTests" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "tests/test_tls.py"
+ (("^(.*)class TLSTests" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ #t))
+ (replace 'check
+ (lambda _
+ (invoke "python" "run.py" "tests")
+ #t)))))
+ (home-page "https://github.com/wbond/oscrypto")
+ (synopsis "Compiler-free Python crypto library backed by the OS")
+ (description "oscrypto is a compilation-free, always up-to-date encryption library for Python.")
+ (license license:expat)))
+
+(define-public python-oscryptotests
+ (package (inherit python-oscrypto)
+ (name "python-oscryptotests")
+ (arguments
+ `(#:tests? #f
+ #:phases
+ (modify-phases %standard-phases
+ (add-after 'unpack 'hard-code-path-to-libscrypt
+ (lambda* (#:key inputs #:allow-other-keys)
+ (chdir "tests")
+ #t)))))))
+
+(define-public python-certvalidator
+ (let ((commit "e5bdb4bfcaa09fa0af355eb8867d00dfeecba08c"))
+ (package
+ (name "python-certvalidator")
+ (version (git-version "0.1" "1" commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/achow101/certvalidator")
+ (commit commit)))
+ (file-name (git-file-name name commit))
+ (sha256
+ (base32
+ "18pvxkvpkfkzgvfylv0kx65pmxfcv1hpsg03cip93krfvrrl4c75"))))
+ (build-system python-build-system)
+ (propagated-inputs
+ `(("python-asn1crypto" ,python-asn1crypto)
+ ("python-oscrypto" ,python-oscrypto)
+ ("python-oscryptotests", python-oscryptotests))) ;; certvalidator tests import oscryptotests
+ (arguments
+ `(#:phases
+ (modify-phases %standard-phases
+ (add-after 'unpack 'disable-broken-tests
+ (lambda _
+ (substitute* "tests/test_certificate_validator.py"
+ (("^(.*)class CertificateValidatorTests" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "tests/test_crl_client.py"
+ (("^(.*)def test_fetch_crl" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "tests/test_ocsp_client.py"
+ (("^(.*)def test_fetch_ocsp" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "tests/test_registry.py"
+ (("^(.*)def test_build_paths" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "tests/test_validate.py"
+ (("^(.*)def test_revocation_mode_hard" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ #t))
+ (replace 'check
+ (lambda _
+ (invoke "python" "run.py" "tests")
+ #t)))))
+ (home-page "https://github.com/wbond/certvalidator")
+ (synopsis "Python library for validating X.509 certificates and paths")
+ (description "certvalidator is a Python library for validating X.509
+certificates or paths. Supports various options, including: validation at a
+specific moment in time, whitelisting and revocation checks.")
+ (license license:expat))))
+
+(define-public python-requests-2.25.1
+ (package (inherit python-requests)
+ (version "2.25.1")
+ (source (origin
+ (method url-fetch)
+ (uri (pypi-uri "requests" version))
+ (sha256
+ (base32
+ "015qflyqsgsz09gnar69s6ga74ivq5kch69s4qxz3904m7a3v5r7"))))))
+
+(define-public python-altgraph
+ (package
+ (name "python-altgraph")
+ (version "0.17")
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/ronaldoussoren/altgraph")
+ (commit (string-append "v" version))))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "09sm4srvvkw458pn48ga9q7ykr4xlz7q8gh1h9w7nxpf001qgpwb"))))
+ (build-system python-build-system)
+ (home-page "https://github.com/ronaldoussoren/altgraph")
+ (synopsis "Python graph (network) package")
+ (description "altgraph is a fork of graphlib: a graph (network) package for
+constructing graphs, BFS and DFS traversals, topological sort, shortest paths,
+etc. with graphviz output.")
+ (license license:expat)))
+
+
+(define-public python-macholib
+ (package
+ (name "python-macholib")
+ (version "1.14")
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/ronaldoussoren/macholib")
+ (commit (string-append "v" version))))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "0aislnnfsza9wl4f0vp45ivzlc0pzhp9d4r08700slrypn5flg42"))))
+ (build-system python-build-system)
+ (propagated-inputs
+ `(("python-altgraph" ,python-altgraph)))
+ (arguments
+ '(#:phases
+ (modify-phases %standard-phases
+ (add-after 'unpack 'disable-broken-tests
+ (lambda _
+ ;; This test is broken as there is no keyboard interrupt.
+ (substitute* "macholib_tests/test_command_line.py"
+ (("^(.*)class TestCmdLine" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line)))
+ (substitute* "macholib_tests/test_dyld.py"
+ (("^(.*)def test_\\S+_find" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line))
+ (("^(.*)def testBasic" line indent)
+ (string-append indent
+ "@unittest.skip(\"Disabled by Guix\")\n"
+ line))
+ )
+ #t)))))
+ (home-page "https://github.com/ronaldoussoren/macholib")
+ (synopsis "Python library for analyzing and editing Mach-O headers")
+ (description "macholib is a Macho-O header analyzer and editor. It's
+typically used as a dependency analysis tool, and also to rewrite dylib
+references in Mach-O headers to be @executable_path relative. Though this tool
+targets a platform specific file format, it is pure python code that is platform
+and endian independent.")
+ (license license:expat)))
+
+(define-public python-signapple
+ (let ((commit "4ff1c1754e37042c002a3f6375c47fd931f2030b"))
+ (package
+ (name "python-signapple")
+ (version (git-version "0.1" "1" commit))
+ (source
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/dongcarl/signapple")
+ (commit commit)))
+ (file-name (git-file-name name commit))
+ (sha256
+ (base32
+ "043czyzfm04rcx5xsp59vsppla3vm5g45dbp1npy2hww4066rlnh"))))
+ (build-system python-build-system)
+ (propagated-inputs
+ `(("python-asn1crypto" ,python-asn1crypto)
+ ("python-oscrypto" ,python-oscrypto)
+ ("python-certvalidator" ,python-certvalidator)
+ ("python-elfesteem" ,python-elfesteem)
+ ("python-requests" ,python-requests-2.25.1)
+ ("python-macholib" ,python-macholib)
+ ("libcrypto" ,openssl)))
+ ;; There are no tests, but attempting to run python setup.py test leads to
+ ;; problems, just disable the test
+ (arguments '(#:tests? #f))
+ (home-page "https://github.com/achow101/signapple")
+ (synopsis "Mach-O binary signature tool")
+ (description "signapple is a Python tool for creating, verifying, and
+inspecting signatures in Mach-O binaries.")
+ (license license:expat))))
(packages->manifest
(append
@@ -214,6 +578,7 @@ chain for " target " development."))
gzip
xz
zlib
+ (list zlib "static")
;; Build tools
gnu-make
libtool
@@ -226,16 +591,20 @@ chain for " target " development."))
python-3
;; Git
git
+ ;; Tests
+ lief
;; Native gcc 7 toolchain
- gcc-toolchain-7)
+ gcc-toolchain-7
+ (list gcc-toolchain-7 "static"))
(let ((target (getenv "HOST")))
(cond ((string-suffix? "-mingw32" target)
;; Windows
(list zip
(make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32")
- (make-nsis-with-sde-support nsis-x86_64)))
+ (make-nsis-with-sde-support nsis-x86_64)
+ osslsigncode))
((string-contains target "-linux-")
(list (make-bitcoin-cross-toolchain target)))
((string-contains target "darwin")
- (list clang-8 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso))
+ (list clang-toolchain-10 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso python-signapple))
(else '())))))
diff --git a/contrib/guix/patches/gcc-8-sort-libtool-find-output.patch b/contrib/guix/patches/gcc-8-sort-libtool-find-output.patch
new file mode 100644
index 0000000000..1dfe3ba132
--- /dev/null
+++ b/contrib/guix/patches/gcc-8-sort-libtool-find-output.patch
@@ -0,0 +1,392 @@
+diff --git a/gcc/configure b/gcc/configure
+index 97ba7d7d69c..e37a96f0c0c 100755
+--- a/gcc/configure
++++ b/gcc/configure
+@@ -19720,20 +19720,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libcc1/configure b/libcc1/configure
+index f53a121611c..5740ca90cab 100755
+--- a/libcc1/configure
++++ b/libcc1/configure
+@@ -12221,20 +12221,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libffi/configure b/libffi/configure
+index 790a291011f..54b1ac18306 100755
+--- a/libffi/configure
++++ b/libffi/configure
+@@ -12661,20 +12661,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libgo/config/libtool.m4 b/libgo/config/libtool.m4
+index f7005947454..8a84417b828 100644
+--- a/libgo/config/libtool.m4
++++ b/libgo/config/libtool.m4
+@@ -6010,20 +6010,20 @@ if test "$_lt_caught_CXX_error" != yes; then
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libgo/config/ltmain.sh b/libgo/config/ltmain.sh
+index ce66b44906a..0f81c401407 100644
+--- a/libgo/config/ltmain.sh
++++ b/libgo/config/ltmain.sh
+@@ -2917,7 +2917,7 @@ func_extract_archives ()
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+- darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
++ darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+@@ -2932,7 +2932,7 @@ func_extract_archives ()
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+- my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
++ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
+diff --git a/libhsail-rt/configure b/libhsail-rt/configure
+index a4fcc10c1f9..8e671229fcd 100755
+--- a/libhsail-rt/configure
++++ b/libhsail-rt/configure
+@@ -12244,20 +12244,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libitm/configure b/libitm/configure
+index dbf386db434..29d4f10611f 100644
+--- a/libitm/configure
++++ b/libitm/configure
+@@ -13067,20 +13067,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/liboffloadmic/configure b/liboffloadmic/configure
+index f873716991b..7aa9186b10e 100644
+--- a/liboffloadmic/configure
++++ b/liboffloadmic/configure
+@@ -12379,20 +12379,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/liboffloadmic/plugin/configure b/liboffloadmic/plugin/configure
+index c031eb3e7fa..67fc7368f21 100644
+--- a/liboffloadmic/plugin/configure
++++ b/liboffloadmic/plugin/configure
+@@ -12086,20 +12086,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libsanitizer/configure b/libsanitizer/configure
+index 4695bc7d4f7..cb7d25c07e6 100755
+--- a/libsanitizer/configure
++++ b/libsanitizer/configure
+@@ -13308,20 +13308,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
+index 61457e940ec..21ef1f61e41 100755
+--- a/libstdc++-v3/configure
++++ b/libstdc++-v3/configure
+@@ -13087,20 +13087,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libtool.m4 b/libtool.m4
+index 24d13f34409..940faaa161d 100644
+--- a/libtool.m4
++++ b/libtool.m4
+@@ -6005,20 +6005,20 @@ if test "$_lt_caught_CXX_error" != yes; then
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/libvtv/configure b/libvtv/configure
+index a197f750453..31ab3a0637b 100755
+--- a/libvtv/configure
++++ b/libvtv/configure
+@@ -13339,20 +13339,20 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+- compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
++ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
++ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
++ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+diff --git a/ltmain.sh b/ltmain.sh
+index 9503ec85d70..79f9ba89af5 100644
+--- a/ltmain.sh
++++ b/ltmain.sh
+@@ -2917,7 +2917,7 @@ func_extract_archives ()
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+- darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
++ darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+@@ -2932,7 +2932,7 @@ func_extract_archives ()
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+- my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
++ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
diff --git a/contrib/guix/patches/oscrypto-hard-code-openssl.patch b/contrib/guix/patches/oscrypto-hard-code-openssl.patch
new file mode 100644
index 0000000000..32027f2d09
--- /dev/null
+++ b/contrib/guix/patches/oscrypto-hard-code-openssl.patch
@@ -0,0 +1,13 @@
+diff --git a/oscrypto/__init__.py b/oscrypto/__init__.py
+index eb27313..371ab24 100644
+--- a/oscrypto/__init__.py
++++ b/oscrypto/__init__.py
+@@ -302,3 +302,8 @@ def load_order():
+ 'oscrypto._win.tls',
+ 'oscrypto.tls',
+ ]
++
++
++paths = '@GUIX_OSCRYPTO_USE_OPENSSL@'.split(',')
++assert len(paths) == 2, 'Value for OSCRYPTO_USE_OPENSSL env var must be two paths separated by a comma'
++use_openssl(*paths)
diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md
index 2d9a4a2153..21f6ba2eb3 100644
--- a/contrib/macdeploy/README.md
+++ b/contrib/macdeploy/README.md
@@ -13,9 +13,9 @@ When complete, it will have produced `Bitcoin-Core.dmg`.
### Step 1: Obtaining `Xcode.app`
Our current macOS SDK
-(`Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`) can be
+(`Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz`) can be
extracted from
-[Xcode_11.3.1.xip](https://download.developer.apple.com/Developer_Tools/Xcode_11.3.1/Xcode_11.3.1.xip).
+[Xcode_12.1.xip](https://download.developer.apple.com/Developer_Tools/Xcode_12.1/Xcode_12.1.xip).
An Apple ID is needed to download this.
After Xcode version 7.x, Apple started shipping the `Xcode.app` in a `.xip`
@@ -27,25 +27,25 @@ approach (tested on Debian Buster) is outlined below:
apt install cpio
git clone https://github.com/bitcoin-core/apple-sdk-tools.git
-# Unpack Xcode_11.3.1.xip and place the resulting Xcode.app in your current
+# Unpack Xcode_12.1.xip and place the resulting Xcode.app in your current
# working directory
-python3 apple-sdk-tools/extract_xcode.py -f Xcode_11.3.1.xip | cpio -d -i
+python3 apple-sdk-tools/extract_xcode.py -f Xcode_12.1.xip | cpio -d -i
```
On macOS the process is more straightforward:
```bash
-xip -x Xcode_11.3.1.xip
+xip -x Xcode_12.1.xip
```
-### Step 2: Generating `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz` from `Xcode.app`
+### Step 2: Generating `Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz` from `Xcode.app`
-To generate `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`, run
+To generate `Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz`, run
the script [`gen-sdk`](./gen-sdk) with the path to `Xcode.app` (extracted in the
previous stage) as the first argument.
```bash
-# Generate a Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz from
+# Generate a Xcode-12.1-12A7403-extracted-SDK-with-libcxx-headers.tar.gz from
# the supplied Xcode.app
./contrib/macdeploy/gen-sdk '/path/to/Xcode.app'
```
@@ -56,7 +56,7 @@ Working macOS DMGs are created in Linux by combining a recent `clang`, the Apple
Apple uses `clang` extensively for development and has upstreamed the necessary
functionality so that a vanilla clang can take advantage. It supports the use of `-F`,
-`-target`, `-mmacosx-version-min`, and `--sysroot`, which are all necessary when
+`-target`, `-mmacosx-version-min`, and `-isysroot`, which are all necessary when
building for macOS.
Apple's version of `binutils` (called `cctools`) contains lots of functionality missing in the
diff --git a/contrib/qos/tc.sh b/contrib/qos/tc.sh
index 1cde19efd1..1cde19efd1 100644..100755
--- a/contrib/qos/tc.sh
+++ b/contrib/qos/tc.sh
diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py
index 7630a7a4fa..dbecba7d1d 100755
--- a/contrib/seeds/generate-seeds.py
+++ b/contrib/seeds/generate-seeds.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2017 Wladimir J. van der Laan
+# Copyright (c) 2014-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,19 +13,14 @@ argument:
These files must consist of lines in the format
- <ip>
<ip>:<port>
- [<ipv6>]
[<ipv6>]:<port>
- <onion>.onion
- 0xDDBBCCAA (IPv4 little-endian old pnSeeds format)
+ <onion>.onion:<port>
+ <i2p>.b32.i2p:<port>
The output will be two data structures with the peers in binary format:
- static SeedSpec6 pnSeed6_main[]={
- ...
- }
- static SeedSpec6 pnSeed6_test[]={
+ static const uint8_t chainparams_seed_{main,test}[]={
...
}
@@ -33,24 +28,39 @@ These should be pasted into `src/chainparamsseeds.h`.
'''
from base64 import b32decode
-from binascii import a2b_hex
+from enum import Enum
+import struct
import sys
import os
import re
-# ipv4 in ipv6 prefix
-pchIPv4 = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff])
-# tor-specific ipv6 prefix
-pchOnionCat = bytearray([0xFD,0x87,0xD8,0x7E,0xEB,0x43])
-
-def name_to_ipv6(addr):
- if len(addr)>6 and addr.endswith('.onion'):
+class BIP155Network(Enum):
+ IPV4 = 1
+ IPV6 = 2
+ TORV2 = 3 # no longer supported
+ TORV3 = 4
+ I2P = 5
+ CJDNS = 6
+
+def name_to_bip155(addr):
+ '''Convert address string to BIP155 (networkID, addr) tuple.'''
+ if addr.endswith('.onion'):
vchAddr = b32decode(addr[0:-6], True)
- if len(vchAddr) != 16-len(pchOnionCat):
+ if len(vchAddr) == 35:
+ assert vchAddr[34] == 3
+ return (BIP155Network.TORV3, vchAddr[:32])
+ elif len(vchAddr) == 10:
+ return (BIP155Network.TORV2, vchAddr)
+ else:
raise ValueError('Invalid onion %s' % vchAddr)
- return pchOnionCat + vchAddr
+ elif addr.endswith('.b32.i2p'):
+ vchAddr = b32decode(addr[0:-8] + '====', True)
+ if len(vchAddr) == 32:
+ return (BIP155Network.I2P, vchAddr)
+ else:
+ raise ValueError(f'Invalid I2P {vchAddr}')
elif '.' in addr: # IPv4
- return pchIPv4 + bytearray((int(x) for x in addr.split('.')))
+ return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.'))))
elif ':' in addr: # IPv6
sub = [[], []] # prefix, suffix
x = 0
@@ -67,13 +77,12 @@ def name_to_ipv6(addr):
sub[x].append(val & 0xff)
nullbytes = 16 - len(sub[0]) - len(sub[1])
assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
- return bytearray(sub[0] + ([0] * nullbytes) + sub[1])
- elif addr.startswith('0x'): # IPv4-in-little-endian
- return pchIPv4 + bytearray(reversed(a2b_hex(addr[2:])))
+ return (BIP155Network.IPV6, bytes(sub[0] + ([0] * nullbytes) + sub[1]))
else:
raise ValueError('Could not parse address %s' % addr)
-def parse_spec(s, defaultport):
+def parse_spec(s):
+ '''Convert endpoint string to BIP155 (networkID, addr, port) tuple.'''
match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
if match: # ipv6
host = match.group(1)
@@ -85,17 +94,42 @@ def parse_spec(s, defaultport):
(host,_,port) = s.partition(':')
if not port:
- port = defaultport
+ port = 0
else:
port = int(port)
- host = name_to_ipv6(host)
-
- return (host,port)
+ host = name_to_bip155(host)
-def process_nodes(g, f, structname, defaultport):
- g.write('static SeedSpec6 %s[] = {\n' % structname)
- first = True
+ if host[0] == BIP155Network.TORV2:
+ return None # TORV2 is no longer supported, so we ignore it
+ else:
+ return host + (port, )
+
+def ser_compact_size(l):
+ r = b""
+ if l < 253:
+ r = struct.pack("B", l)
+ elif l < 0x10000:
+ r = struct.pack("<BH", 253, l)
+ elif l < 0x100000000:
+ r = struct.pack("<BI", 254, l)
+ else:
+ r = struct.pack("<BQ", 255, l)
+ return r
+
+def bip155_serialize(spec):
+ '''
+ Serialize (networkID, addr, port) tuple to BIP155 binary format.
+ '''
+ r = b""
+ r += struct.pack('B', spec[0].value)
+ r += ser_compact_size(len(spec[1]))
+ r += spec[1]
+ r += struct.pack('>H', spec[2])
+ return r
+
+def process_nodes(g, f, structname):
+ g.write('static const uint8_t %s[] = {\n' % structname)
for line in f:
comment = line.find('#')
if comment != -1:
@@ -103,14 +137,14 @@ def process_nodes(g, f, structname, defaultport):
line = line.strip()
if not line:
continue
- if not first:
- g.write(',\n')
- first = False
- (host,port) = parse_spec(line, defaultport)
- hoststr = ','.join(('0x%02x' % b) for b in host)
- g.write(' {{%s}, %i}' % (hoststr, port))
- g.write('\n};\n')
+ spec = parse_spec(line)
+ if spec is None: # ignore this entry (e.g. no longer supported addresses like TORV2)
+ continue
+ blob = bip155_serialize(spec)
+ hoststr = ','.join(('0x%02x' % b) for b in blob)
+ g.write(f' {hoststr},\n')
+ g.write('};\n')
def main():
if len(sys.argv)<2:
@@ -124,14 +158,13 @@ def main():
g.write(' * List of fixed seed nodes for the bitcoin network\n')
g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n')
g.write(' *\n')
- g.write(' * Each line contains a 16-byte IPv6 address and a port.\n')
- g.write(' * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.\n')
+ g.write(' * Each line contains a BIP155 serialized (networkID, addr, port) tuple.\n')
g.write(' */\n')
with open(os.path.join(indir,'nodes_main.txt'), 'r', encoding="utf8") as f:
- process_nodes(g, f, 'pnSeed6_main', 8333)
+ process_nodes(g, f, 'chainparams_seed_main')
g.write('\n')
with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f:
- process_nodes(g, f, 'pnSeed6_test', 18333)
+ process_nodes(g, f, 'chainparams_seed_test')
g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n')
if __name__ == '__main__':
diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt
index 7b97436013..a62150a930 100644
--- a/contrib/seeds/nodes_main.txt
+++ b/contrib/seeds/nodes_main.txt
@@ -650,515 +650,39 @@
[2a0f:df00:0:254::46]:8333
[2c0f:f598:5:1:1001::1]:8333
[2c0f:fce8:0:400:b7c::1]:8333
-226eupdnaouu4h2v.onion:8333
-22h7b6f3caabqqsu.onion:8333
-23wdfqkzttmenvki.onion:8333
-23yi3frxymtwdgre.onion:8333
-2ajon3moyf4i2hbb.onion:8333
-2bfmlpk55hffpl6e.onion:8333
-2ckmbf6sglwydeth.onion:8333
-2hkusi5gcaautwqf.onion:8333
-2ivhmlbxbgnkcykl.onion:8333
-2mmxouhv6nebowkq.onion:8333
-2qsnv6exnuuiar7z.onion:8333
-2qudbhlnvqpli3sz.onion:8333
-2ujxdfovfyjpmdto.onion:8333
-2xdgeufrek3eumkw.onion:8333
-2xdzsruhsej4tsiw.onion:8333
-34ran2woq4easmss.onion:8333
-36q7khhej2lxd3wf.onion:8333
-373wjdspuo52utzq.onion:8333
-376klet5xqbrg2jv.onion:8333
-37kwd7fxop766l5k.onion:8333
-3e5t7hq4alt5tovx.onion:8333
-3gbxhebfhouuwgc3.onion:8333
-3hgbjze2nbwyuewf.onion:8333
-3iuuvrd2waha2cxo.onion:8333
-3jtxujdaiwh6iltu.onion:8333
-3l5eq2du7mvscj4a.onion:8333
-3nofngnqlqeehn7o.onion:8333
-3r44ddzjitznyahw.onion:8333
-3vtbuwmton7vq5qz.onion:8333
-46ohzttz4peki43g.onion:8333
-47fl3ivl4v56jstr.onion:8333
-47i6qrl2ijqcwlg6.onion:8333
-47uupgzcnrwahoto.onion:8333
-4c5cki37evofds6d.onion:8333
-4eq36jrx7xuytfpc.onion:8333
-4ewkdxvcg57adrni.onion:8333
-4flvgibnm2nld3na.onion:8333
-4iaontym47imawe4.onion:8333
-4jxz37oou5ag763c.onion:8333
-4mnkvj6ha73eqnbk.onion:8333
-4nnuyxm5k5tlyjq3.onion:8333
-4nz2yg4cnote3ej7.onion:8333
-4pozwh6564ygzddk.onion:8333
-4qgfb56rvpbmesx7.onion:8333
-4rsax23taqzwmimj.onion:8333
-4u5j5ay6rasowt4m.onion:8333
-4vorvtoyegh4zbvr.onion:8333
-52s4j5pldwlpzhtw.onion:8333
-5abpiiqfvekoejro.onion:8333
-5aydzxx6jyoz3nez.onion:8333
-5cxzdsrtok5dgo4a.onion:8333
-5eduikpudie3jyrf.onion:8333
-5epeafkmya4fv5d5.onion:8333
-5fyxlztic3t6notz.onion:8333
-5hd6eyew5ybnq6gb.onion:8333
-5jyfzhwksb6urrp2.onion:8333
-5nooqgct567ig57v.onion:8333
-5nsfm4nqqzzprjrp.onion:8333
-5oqstxspzhlgjef6.onion:8333
-5pzzmd4tfonrqzb2.onion:8333
-5sckmx4yucbnp4io.onion:8333
-5ue7worzbn6hon3e.onion:8333
-5wxhx2tozpovf6z3.onion:8333
-5xk3yun36e32e34i.onion:8333
-5zght2g7vcsapi65.onion:8333
-62dcdpvdolfzkdzl.onion:8333
-63bko2mhixnn2b7d.onion:8333
-67hjvfv6wictalm5.onion:8333
-6g6ko4klkf5atldi.onion:8333
-6k5zreexw4cadxi5.onion:8333
-6kf5ayhlpenywgas.onion:8333
-6maigxjvcet4pite.onion:8333
-6ressv4dvplb5ihh.onion:8333
-6rjex6gyuaui3c5e.onion:8333
-6skgnf43pphdvjua.onion:8333
-6stxaoduwisg5sqh.onion:8333
-6xqy4ts6bo6u5dgm.onion:8333
-7avnl3dqpgu23jva.onion:8333
-7ff4wk266no23txn.onion:8333
-7hipbuzfdcyzqkkg.onion:8333
-7sjmlzrthjlpfydk.onion:8333
-7tut3zt2akwrmw6x.onion:8333
-7uhsjzj6nx3dfnxt.onion:8333
-7wm4wso3wvatxnbt.onion:8333
-7ykmzuybwd2ptzg4.onion:8333
-a27bvhina4y23jxo.onion:8333
-a53vtdm7uiet5vdl.onion:8333
-a56572xjuofnt2dp.onion:8333
-abp25knifdsnc2rv.onion:8333
-aefx7ubzpal7clak.onion:8333
-ai5r2diozoe7rrdz.onion:8333
-aipupphit3enggpj.onion:8333
-algpjjygd3gtnmpp.onion:8333
-alihua7rhyc452hr.onion:8333
-am3gyyfynxzwyxhx.onion:8333
-ankozzfhl2r3uc6u.onion:8333
-at3twjlbtc2lqnq5.onion:8333
-avqobl72pmc64dyi.onion:8333
-awmdz2fs3b5h5ut5.onion:8333
-ayywpiy77butdjrj.onion:8333
-b2i3pj7c24cvprs7.onion:8333
-b4ilebyxcu6nttio.onion:8333
-b4vvkbqipcmkwp4v.onion:8333
-bddfqxps5ibd3ftw.onion:8333
-be5bgcpo4ooux5qy.onion:8333
-bgla4m6zetvtv7ls.onion:8333
-bh32gzw3nyckzqut.onion:8333
-bho4kodpehn7xr3x.onion:8333
-bitcoin4rlfa4wqx.onion:8333
-biw7s6jf6r2mf3cu.onion:8333
-bk7yp6epnmcllq72.onion:8333
-blcktrgve5vetjsk.onion:8333
-blwbp7gfdffdsx4g.onion:8333
-bnxn6qqc55gvn5op.onion:8333
-bp7o22lvcjawelvv.onion:8333
-bqqyqucgj4tchn64.onion:8333
-bvdzmutcqf7gzzn5.onion:8333
-c36zmegjkinftmtf.onion:8333
-c4fn62gnltlgrptv.onion:8333
-caael5yedviooqzk.onion:8333
-caq54ablfbrnumdd.onion:8333
-cernrmrk5zomzozn.onion:8333
-chri6itgjaagof4t.onion:8333
-cncwik3tnd2ejm5z.onion:8333
-cuyjqoziemcmwaxl.onion:8333
-cx7qa2gpqyp7pld5.onion:8333
-czp7wgaus4gvio72.onion:8333
-d2fn54rfyjdangi4.onion:8333
-d2sk45u6ca64yeqh.onion:8333
-d3aowmngvktsziae.onion:8333
-d5iu4aiz3y2kgcgj.onion:8333
-d6zbw2sxnxgj5sv3.onion:8333
-db5rd5e46t7mgini.onion:8333
-dci2gulorl44yj55.onion:8333
-ddpth2mwt3rsvoog.onion:8333
-dfrwza7fcecknnms.onion:8333
-djwhjfj4rh3oz3yj.onion:8333
-dkk5mmpe5jtjodk5.onion:8333
-doj3zgmsbzurmqgp.onion:8333
-dpce4f3rcqddzbx5.onion:8333
-drwo3vnxch5ozfbo.onion:8333
-duikkidxip3lyexn.onion:8333
-duqdliptc22i6hf5.onion:8333
-duyp4coh5d7nh3ud.onion:8333
-duz5two3z7c55lxj.onion:8333
-dvu6dlar6ezc6xen.onion:8333
-dy6zqs46ycleayyp.onion:8333
-dz2ydmj3yqrcm4r7.onion:8333
-e2b2a5suvdawzxud.onion:8333
-e33h57j2ewkkqsn5.onion:8333
-e5kjiay7pzj5qpzv.onion:8333
-e7iko42d2wzcmvy4.onion:8333
-ea6boh4kotq56ws5.onion:8333
-efdx6gc4s5ezyqeg.onion:8333
-efrpuuic6ukeyqcs.onion:8333
-egruc3bi3itru6gq.onion:8333
-erc6tjs2ucyadl23.onion:8333
-eue2n5sk5tktg5bv.onion:8333
-ezkr7stq4w7ohjrt.onion:8333
-f3nyyjba6kpxznhk.onion:8333
-faq73vj4pcs73thu.onion:8333
-fdvtlj3pscbxuh75.onion:8333
-fgdpxov4nzxvhcpv.onion:8333
-fisqq6vzk3m6t225.onion:8333
-fkgp3qwegacrd2bj.onion:8333
-fo3tdfwx27takqq5.onion:8333
-fqkxtchwypispkpv.onion:8333
-fqunuhlwvd7rq6d5.onion:8333
-frwt5mscpyhiuwpe.onion:8333
-fta4gfjiuv6f2le2.onion:8333
-fuoy2ipuqrqwe5cf.onion:8333
-fz6nsij6jiyuwlsc.onion:8333
-g3vlnaaaog5sgui5.onion:8333
-g44i6jwsutkwmspz.onion:8333
-g55t65d5ckjixcnw.onion:8333
-gajd6eyrl2qwkfmg.onion:8333
-gblue3hr53p4grx7.onion:8333
-gbpro5tzduiuff4v.onion:8333
-gc4l3tql32qhfgmi.onion:8333
-gcnlorvtpycuajc6.onion:8333
-gdsib2nk2eeoidgc.onion:8333
-ge5gm7c6w7yahpz7.onion:8333
-gegcteeep4cwftl5.onion:8333
-gfoyraudgv5qjdku.onion:8333
-ggpbuypmxgi26lc6.onion:8333
-ghqivye7cfckisnt.onion:8333
-girakxomne5fby64.onion:8333
-glz5gfk33tuug5ne.onion:8333
-gplatxoyg5nxl5rj.onion:8333
-gripl5xjwy2dcr6c.onion:8333
-gthhzlmqci22nxru.onion:8333
-gto2d64swosfmk6c.onion:8333
-guaciney52mgcbp2.onion:8333
-gwktgrmtwk6nv5sc.onion:8333
-gwoxnokdcwc7hy4p.onion:8333
-h333f4qnwe7mrymn.onion:8333
-h6a32n4blbwwyn4d.onion:8333
-hafwtrbooszoembm.onion:8333
-hbwhgsb3eeinnr6t.onion:8333
-hcv6foxh5mk7fhb5.onion:8333
-hd6hktcl6wamzlzm.onion:8333
-hda6msa4v4rt77gx.onion:8333
-hdgnxkuqsd6wjwwx.onion:8333
-hgh3azn3eesddvcg.onion:8333
-hhyxu6bwkjefejoz.onion:8333
-hizn6rmofsg3upmn.onion:8333
-hjqxxsy2osemfvev.onion:8333
-hkbp7mbgw6klls4s.onion:8333
-hlojuwiwbkoj4kdz.onion:8333
-hlzxsjr7ob3qzzqq.onion:8333
-hniuzplezebyhv7a.onion:8333
-hondewkj4s4rdcwf.onion:8333
-hql5nv6vhceid3bn.onion:8333
-hspjo7mqrre5gyxr.onion:8333
-hu64s2mdr3x7yxka.onion:8333
-hvwvq2swkqw3qvyo.onion:8333
-hwo2biyndrrvpl6f.onion:8333
-hzxj3dth3y2xt45o.onion:8333
-i3ufxuw3t7cxfdpq.onion:8333
-ia3n3q5u45gvpx7a.onion:8333
-icfgs3fctckd4yeo.onion:8333
-icpz6thqvdjcwlvb.onion:8333
-if32zo5u4mhdunfd.onion:8333
-ig4lguql6vxkbmmr.onion:8333
-ihhcr7fhczqdac4y.onion:8333
-ijm2tyxob7vkvazz.onion:8333
-ip3puuqghumfz5ww.onion:8333
-iq3ket72f3y2frpg.onion:8333
-iqagt5co4dt7h6hf.onion:8333
-iugw42ih6hprqr26.onion:8333
-ivf774v4t7k63i6d.onion:8333
-ivfacdf7cig2z2y2.onion:8333
-ivsxdwku5og2zj4l.onion:8333
-ixwgrhaklvu4g6o7.onion:8333
-iz56moo6mkp3g7xo.onion:8333
-j2cp5muw5j3lumcx.onion:8333
-j2lrkrwugldwewws.onion:8333
-j2qtmkd2dablssz4.onion:8333
-j5e2yuan57v2h5el.onion:8333
-j5jfrdthqt5g25xz.onion:8333
-j5lk2uv2bspfqxfk.onion:8333
-janvvzsmzcsj3fil.onion:8333
-jenn2tmyl3xxarmq.onion:8333
-jfoe5f2sczojfp32.onion:8333
-jgcgi6k2pxooi5q3.onion:8333
-jhana24s3dzkitzp.onion:8333
-jitgulb24mvfqrdg.onion:8333
-jjuvwbjfzljmn7t3.onion:8333
-jlcfomgr5xfexaif.onion:8333
-jlehs6ybb26qlnna.onion:8333
-jljzz4tmbqrxq3q5.onion:8333
-joc4oqceedkg77vf.onion:8333
-jr5y6njubcbv6g37.onion:8333
-jroaos6la4vieho4.onion:8333
-jsmphgkay7iihbkr.onion:8333
-jtksnokusbzms7wl.onion:8333
-ju5duo3r6p6diznc.onion:8333
-jw6zymxcnebahuuj.onion:8333
-jxalvhf7w7wevqzw.onion:8333
-jyzhe3ig44ickysb.onion:8333
-jze6ukn4idrh44eo.onion:8333
-k4glotlxnmttb6ct.onion:8333
-k7uy3iwmvguzygd2.onion:8333
-kl23ofag3ukb6hxl.onion:8333
-kokt2qr6d4pmyb2d.onion:8333
-kpalu3h5ydkoaivs.onion:8333
-krdpbdvtqw5c5lee.onion:8333
-kriw6kzjzarzgb3g.onion:8333
-krp2thcmwrpsoue6.onion:8333
-kvyvdwjwtae5mo77.onion:8333
-kyrxri5rbr6ipurs.onion:8333
-kz3oxg7745dxt62q.onion:8333
-l3w5fcki2wbro2qb.onion:8333
-l44bisuxhh7reb5q.onion:8333
-l565g523emjebusj.onion:8333
-l6w5kdeigwsgnf5t.onion:8333
-l7a4emryfxkjgmmb.onion:8333
-l7sloscjqqbifcsw.onion:8333
-laafjqvtog7djfl2.onion:8333
-lah676kxbgbgw3u2.onion:8333
-lbq2a7pnpmviw2qo.onion:8333
-lc4wnpql27vymi35.onion:8333
-ldoffbfpk3j6c7y7.onion:8333
-lehpmglkivobq2qo.onion:8333
-lgewpjz7ie7daqqr.onion:8333
-lgkvbvro67jomosw.onion:8333
-liw5z4ngic6b7vnv.onion:8333
-ljs7gwrmmza6q6ga.onion:8333
-lmvax3e6awaxvhqi.onion:8333
-lrz77dwf7yq4cgnt.onion:8333
-lva54pnbq2nsmjyr.onion:8333
-lxc2uphxyyxflhnf.onion:8333
-lyjybdr4hmj3bqab.onion:8333
-lz2zlnmyynwtgwf2.onion:8333
-m6hcnpikimyh37yp.onion:8333
-md635omjnrgheed3.onion:8333
-mdb3oupwf4f2qyjb.onion:8333
-me6d4esx7ohdnxne.onion:8333
-mecfkik5ci47wckj.onion:8333
-mfrvevn7w6rwsp4r.onion:8333
-mimuutlew5srtduk.onion:8333
-mnysk3izxvra3huv.onion:8333
-mqu6gqtrhm6xzwwh.onion:8333
-mwuc6vom4ngijtb3.onion:8333
-mxdtrjhe2yfsx3pg.onion:8333
-n4ibet4piscv22nj.onion:8333
-n6d46vbzx43bevlb.onion:8333
-n6t6kfgzlvozxhfm.onion:8333
-n7rrochwerf2qxze.onion:8333
-ncsdiqmnxhnnjbsz.onion:8333
-nitxw3ilffngpumv.onion:8333
-njlsvubildehluwr.onion:8333
-njslfsivyyhixbsp.onion:8333
-nkf5e6b7pl4jfd4a.onion:8333
-nkppsb3t3ducje6m.onion:8333
-nlfwyqksmeqe45zz.onion:8333
-nlyjmpcmpaz5b4aa.onion:8333
-nnmv7z65k65mcesr.onion:8333
-nrrfwdmrm3imuebn.onion:8333
-nrrmkgmulpgsbwlt.onion:8333
-nw4h7leckut7eapv.onion:8333
-nwky3wd3ihoidvb5.onion:8333
-ny4kkemmmqv4lptm.onion:8333
-o25wkcw7eorg2toi.onion:8333
-o2gumvbkw6pm45cf.onion:8333
-o4yjshdwlbshylqw.onion:8333
-ofx4qgw6lppnvtgv.onion:8333
-oketipl4gndqcaus.onion:8333
-oq5q4qrqijr2kpun.onion:8333
-oqw3mfoiobqcklxh.onion:8333
-orsy2v63ecrmdj55.onion:8333
-ot4tzmznyimmlszk.onion:8333
-owk6c2jfthwkyahe.onion:8333
-oy7ss3hm2okx4tun.onion:8333
-p2pc6wbaepvdi6ce.onion:8333
-p2x24gdhasmgcl5j.onion:8333
-p6couujr2ndhllv3.onion:8333
-pa7dw5bln5lqmu53.onion:8333
-pasmchtoooj2kchd.onion:8333
-pdapkkhk6pbcy2tj.onion:8333
-peh5ajouuw6mw4sr.onion:8333
-pkuuc5pwl5xygwhr.onion:8333
-pq4wjl7vg7tsfycc.onion:8333
-ptbwqhusps5qieql.onion:8333
-ptwpbwyj5lnyew2f.onion:8333
-pu7w3jfyrzp7sxsi.onion:8333
-pwylbyvfuc62hhvx.onion:8333
-q2fhnnyt5b2ayvce.onion:8333
-q3i3apuionbazmfe.onion:8333
-qd6fcpu3pvbf2y3x.onion:8333
-qfewv3y7a3p4i3bd.onion:8333
-qhytdttflhbc4rsh.onion:8333
-qkn35rb3x2gxbwq4.onion:8333
-qlvlexs7pwac2f4b.onion:8333
-qogcqirtuta6rlxg.onion:8333
-qrzqfxkhrmu5v5ro.onion:8333
-qsyjasq46b2syiys.onion:8333
-quu4b2zjbnr2ue4y.onion:8333
-quycfj2wenz6bfyd.onion:8333
-qvdy3cmocnlv5v7c.onion:8333
-qvwhpqygan2xky5h.onion:8333
-qyutwc26ullujafb.onion:8333
-r45qg2d6iwfdhqwl.onion:8333
-r4xudr6u4r5nyga4.onion:8333
-r6apa5ssujxbwd34.onion:8333
-r6z2gcsu37k3gaah.onion:8333
-rbrjgfcca6v5b7yo.onion:8333
-rcifxibawqt6rxzz.onion:8333
-rdo3xctk3zkzjvln.onion:8333
-rdvlepy6ghgpapzo.onion:8333
-recs3a27chv2lg65.onion:8333
-rfmbiy5vztvn6hyn.onion:8333
-rli5lbje4k77inzw.onion:8333
-roqwnmepcj453vfh.onion:8333
-rpbnx54qniivrmh3.onion:8333
-rsvvogqdlijp77hv.onion:8333
-rwm5d4hg3hc77kdt.onion:8333
-s3yelkvc5f5xeysw.onion:8333
-s6rx52hitmpp4lge.onion:8333
-sa6m3rvycipgemky.onion:8333
-savebeesmkivmfbo.onion:8333
-sbyjr5npk2mlmfw7.onion:8333
-serwj42jme5xhhmw.onion:8333
-sg4vmubv3djrzvuh.onion:8333
-shsgksluz6jkgp6g.onion:8333
-sjyzmwwu6diiit3r.onion:8333
-sk3en3reudg3sdg5.onion:8333
-skoifp4oj7l4osu5.onion:8333
-sle2caplkln33e7y.onion:8333
-smdd7q7gonajdmjq.onion:8333
-spmhuxjb2cd7leun.onion:8333
-srkgyv5edn2pa7il.onion:8333
-sslnjjhnmwllysv4.onion:8333
-su66ygras6rkdtnl.onion:8333
-sundvmbjrtgdfahx.onion:8333
-svd65k5jpal2p3lt.onion:8333
-svua5hiqluw7o2sw.onion:8333
-sxqjubmum4rmfgpu.onion:8333
-t245vi742ti3tnka.onion:8333
-t4fbovvgzpnimd2p.onion:8333
-t4l4wv3erkhpde2p.onion:8333
-t5qchwbr6u5v2agk.onion:8333
-t7jlaj6ggyx7s5vy.onion:8333
-ta6sjeqyb27f4n4a.onion:8333
-tav7utpw4pfy7j6k.onion:8333
-taxg5z2sxfm5c4d6.onion:8333
-tekwvnbodbzrlufs.onion:8333
-tg4uwrjmtr2jlbjy.onion:8333
-th4cjvffjtw6vomu.onion:8333
-th6fxymtwnfifqeu.onion:8333
-thtchhl25u26nglq.onion:8333
-tiiah7csuoklcvi6.onion:8333
-tk63x5fk3337z3ud.onion:8333
-tkgootat6cqn7vyy.onion:8333
-tnj565wwqz5wpjvs.onion:8333
-ts6qx37mmpu6nj5y.onion:8333
-ttjisvxydgbtp56f.onion:8333
-twn54v7ra2xjgd55.onion:8333
-txem5meug24g2ezd.onion:8333
-tyiunn36lmfcq5lr.onion:8333
-tyv56xs6g6ndzqux.onion:8333
-u47f3hxwq65sgs4o.onion:8333
-u4r7fnholrdwwlni.onion:8333
-u556ofb3myarafwn.onion:8333
-u5q3gbz4qpz4wvlr.onion:8333
-uakly3ydrevvpxwi.onion:8333
-ug6hapi4qtekzc7v.onion:8333
-ui553qotd6ron3rf.onion:8333
-uir7f3wltoka6bbb.onion:8333
-ukrjjhwodl44wmof.onion:8333
-ul5gm2ixy7kqdfwg.onion:8333
-undd7rsj4pen3wo4.onion:8333
-uorwpzfehtykrg43.onion:8333
-uovsp2yltnaojq6l.onion:8333
-usazmdcs32ny24dy.onion:8333
-usazs7glm7geyxkl.onion:8333
-uss2kedg7qkwgdr5.onion:8333
-utgyrvw75wv2nymi.onion:8333
-uzwacms7kyzhehbl.onion:8333
-v2kdcetvslmdfcwr.onion:8333
-v5lhnzzv6nngfg5d.onion:8333
-vc44gb4veppobrt3.onion:8333
-vfwyhju43wxhzvux.onion:8333
-vgujufk53lqyolio.onion:8333
-vheejqq2v5dkb4xr.onion:8333
-vj64edev4jnqfdsb.onion:8333
-vmai5uigezr2khkj.onion:8333
-vmuykd7sxbmi7w57.onion:8333
-vomeacttinx3mpml.onion:8333
-vpow2xofg3fwzsdq.onion:8333
-vsawli4l5ifxdzaw.onion:8333
-vunubqkfms7sifok.onion:8333
-vuombnevwul4bqsb.onion:8333
-vxcpvdng65aefz6t.onion:8333
-vyxoizdzavp3obau.onion:8333
-wbeon2ci7lfio6ay.onion:8333
-wbwevew62mgsrrdz.onion:8333
-wfaydlg6zyfzjcu5.onion:8333
-wfz56s5lyn5dysez.onion:8333
-wg3mq4ugyy2gx32b.onion:8333
-whky54bctkf2n4p3.onion:8333
-whmjanqoyzizzc4t.onion:8333
-wlhou2wxgqyi3x3f.onion:8333
-wlvkfrplfiioz22o.onion:8333
-x3ngb3va7dovuenw.onion:8333
-x57x62bmmnylvo7r.onion:8333
-xgvm57mhgv564dka.onion:8333
-xhs3glfwnwiumivn.onion:8333
-xje5fwvyfdue2u6k.onion:8333
-xlgubgyly2blvsg5.onion:8333
-xnlu3tvakngy7tkp.onion:8333
-xo5marilhuyo7but.onion:8333
-xsaaxihdygnwxrix.onion:8333
-xu5mlugdsmzfkvzh.onion:8333
-xvrxqcptqvieedb2.onion:8333
-xwzhrrygftq3q4w4.onion:8333
-y4swmsaxdcos2bnu.onion:8333
-y5tl4lqi365pplud.onion:8333
-y5wzeqyaets5na6t.onion:8333
-y73qk2mzkjkhoky7.onion:8333
-y7oz3ydnvib4xhbb.onion:8333
-yah7qgfqqrteoche.onion:8333
-yba4brm555denlt7.onion:8333
-ygeqkg4inplsace3.onion:8333
-yjhnfu75lazbi34h.onion:8333
-yjw7kqapxx5vggoj.onion:8333
-ym7inmovbrna4gco.onion:8333
-yq5cusnuokscy64z.onion:8333
-yrcaioqrqrdwokqt.onion:8333
-yrcr7pgjuazad254.onion:8333
-yrksvon3tmvoohdv.onion:8333
-ytpus4vx5w7j6wp2.onion:8333
-ytqcigk2hhdl45ho.onion:8333
-yxojl3xmjus3dik2.onion:8333
-yzdqdsqx4fdung6w.onion:8333
-z33nukt7ngik3cpe.onion:8333
-z3ywbadw46ndnxgh.onion:8333
-z6mbqq7llxlrn4kq.onion:8333
-zb3lrcksn4rzhzje.onion:8333
-ze7odp7pzarjplsr.onion:8333
-zgbmhtbja4fy2373.onion:8333
-zh7hvalcgvjpoaqm.onion:8333
-ziztvxehmj5mehpg.onion:8333
-zjii3yecdrmq73y3.onion:8333
-zkrwmgjuvsza6ye2.onion:8333
-zoz2aopwi3wfuqwg.onion:8333
-ztdcfnh46773bivu.onion:8333
-zuxhc6d3nwpgc4af.onion:8333
-zuytrfevzjcpizli.onion:8333
-zvq6dpt3i2ofdp3g.onion:8333
-zwwm6ga7u2hqe2sd.onion:8333
-zyqb4lenfspntj5m.onion:8333
+
+# manually added 2021-03 for minimal torv3 bootstrap support
+2g5qfdkn2vvcbqhzcyvyiitg4ceukybxklraxjnu7atlhd22gdwywaid.onion:8333
+2jmtxvyup3ijr7u6uvu7ijtnojx4g5wodvaedivbv74w4vzntxbrhvad.onion:8333
+37m62wn7dz3uqpathpc4qfmgrbupachj52nt3jbtbjugpbu54kbud7yd.onion:8333
+5g72ppm3krkorsfopcm2bi7wlv4ohhs4u4mlseymasn7g7zhdcyjpfid.onion:8333
+7cgwjuwi5ehvcay4tazy7ya6463bndjk6xzrttw5t3xbpq4p22q6fyid.onion:8333
+7pyrpvqdhmayxggpcyqn5l3m5vqkw3qubnmgwlpya2mdo6x7pih7r7id.onion:8333
+b64xcbleqmwgq2u46bh4hegnlrzzvxntyzbmucn3zt7cssm7y4ubv3id.onion:8333
+ejxefzf5fpst4mg2rib7grksvscl7p6fvjp6agzgfc2yglxnjtxc3aid.onion:8333
+fjdyxicpm4o42xmedlwl3uvk5gmqdfs5j37wir52327vncjzvtpfv7yd.onion:8333
+fpz6r5ppsakkwypjcglz6gcnwt7ytfhxskkfhzu62tnylcknh3eq6pad.onion:8333
+fzhn4uoxfbfss7h7d6ffbn266ca432ekbbzvqtsdd55ylgxn4jucm5qd.onion:8333
+gxo5anvfnffnftfy5frkgvplq3rpga2ie3tcblo2vl754fvnhgorn5yd.onion:8333
+ifdu5qvbofrt4ekui2iyb3kbcyzcsglazhx2hn4wfskkrx2v24qxriid.onion:8333
+itz3oxsihs62muvknc237xabl5f6w6rfznfhbpayrslv2j2ubels47yd.onion:8333
+lrjh6fywjqttmlifuemq3puhvmshxzzyhoqx7uoufali57eypuenzzid.onion:8333
+m7cbpjolo662uel7rpaid46as2otcj44vvwg3gccodnvaeuwbm3anbyd.onion:8333
+opnyfyeiibe5qo5a3wbxzbb4xdiagc32bbce46owmertdknta5mi7uyd.onion:8333
+owjsdxmzla6d7lrwkbmetywqym5cyswpihciesfl5qdv2vrmwsgy4uqd.onion:8333
+q7kgmd7n7h27ds4fg7wocgniuqb3oe2zxp4nfe4skd5da6wyipibqzqd.onion:8333
+rp7k2go3s5lyj3fnj6zn62ktarlrsft2ohlsxkyd7v3e3idqyptvread.onion:8333
+sys54sv4xv3hn3sdiv3oadmzqpgyhd4u4xphv4xqk64ckvaxzm57a7yd.onion:8333
+tddeij4qigtjr6jfnrmq6btnirmq5msgwcsdpcdjr7atftm7cxlqztid.onion:8333
+vi5bnbxkleeqi6hfccjochnn65lcxlfqs4uwgmhudph554zibiusqnad.onion:8333
+xqt25cobm5zqucac3634zfght72he6u3eagfyej5ellbhcdgos7t2had.onion:8333
+
+# manually added 2021-05 for minimal i2p bootstrap support
+72l3ucjkuscrbiiepoehuwqgknyzgo7zuix5ty4puwrkyhtmnsga.b32.i2p:8333
+c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p:8333
+gehtac45oaghz54ypyopim64mql7oad2bqclla74l6tfeolzmodq.b32.i2p:8333
+h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p:8333
+hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p:8333
+pjs7or2ctvteeo5tu4bwyrtydeuhqhvdprtujn4daxr75jpebjxa.b32.i2p:8333
+wwbw7nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p:8333
+zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p:8333
diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt
index 98365ee505..118bec280e 100644
--- a/contrib/seeds/nodes_test.txt
+++ b/contrib/seeds/nodes_test.txt
@@ -1,11 +1,16 @@
# List of fixed seed nodes for testnet
# Onion nodes
-thfsmmn2jbitcoin.onion
-it2pj4f7657g3rhi.onion
-nkf5e6b7pl4jfd4a.onion
-4zhkir2ofl7orfom.onion
-t6xj6wilh4ytvcs7.onion
-i6y6ivorwakd7nw3.onion
-ubqj4rsu3nqtxmtp.onion
+35k2va6vyw4oo5ly2quvcszgdqr56kcnfgcqpnpcffut4jn3mhhwgbid.onion:18333
+blo2esfvk2rr7sr4jspmu3vt2vpgr5rigflsj645fnku7v4qmljurtid.onion:18333
+fuckcswupr5rmlvx2kqqrrosxvjyong4hatmuvxsvtcwe4dsh5rus7qd.onion:18333
+gblylyacjlitd2ywdmo2qqylwtdky7kgeqfvlhiw4zdag4x62tx54hyd.onion:18333
+gzwpduv33l7yze3bcdzj3inebiyjwddjnwvnjhh5wvnv4me76mjt2kad.onion:18333
+h3rphzofxzq52tb63mg5f6kc4my3fkcrgh3m5qryeatts43iljbawiid.onion:18333
+kf4qlhek34b3kgyxyodlmvgm4bxfrjsbjtgayyaiuyhr2eoyfgtm3bad.onion:18333
+mc7k47ndjvvhcgs54wmjzxvate4rtuybbjoryikdssjhcxlx27psbyqd.onion:18333
+mrhiniicugfo7mgrwv3wtolk3tptlcw2uq7ih6sq43fa4k4zbilut3yd.onion:18333
+uiudyws3qizgmepfoh7wwjmsoxoxut4qrmotjjhrn247xnjopr7sfcid.onion:18333
+zc2wvoqcezcrf64trji6jmhtss34a5ds5ntzdhqegzvex3ynrd7nxcad.onion:18333
+zd5m3dgdn46naj36pxvvcalfw2paecle6sdxq64ptwxtxjomkywpklqd.onion:18333
diff --git a/contrib/shell/git-utils.bash b/contrib/shell/git-utils.bash
new file mode 100644
index 0000000000..37bac1f38d
--- /dev/null
+++ b/contrib/shell/git-utils.bash
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+git_root() {
+ git rev-parse --show-toplevel 2> /dev/null
+}
+
+git_head_version() {
+ local recent_tag
+ if recent_tag="$(git describe --exact-match HEAD 2> /dev/null)"; then
+ echo "${recent_tag#v}"
+ else
+ git rev-parse --short=12 HEAD
+ fi
+}
diff --git a/contrib/shell/realpath.bash b/contrib/shell/realpath.bash
new file mode 100644
index 0000000000..389b77b562
--- /dev/null
+++ b/contrib/shell/realpath.bash
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+
+# Based on realpath.sh written by Michael Kropat
+# Found at: https://github.com/mkropat/sh-realpath/blob/65512368b8155b176b67122aa395ac580d9acc5b/realpath.sh
+
+bash_realpath() {
+ canonicalize_path "$(resolve_symlinks "$1")"
+}
+
+resolve_symlinks() {
+ _resolve_symlinks "$1"
+}
+
+_resolve_symlinks() {
+ _assert_no_path_cycles "$@" || return
+
+ local dir_context path
+ if path=$(readlink -- "$1"); then
+ dir_context=$(dirname -- "$1")
+ _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@"
+ else
+ printf '%s\n' "$1"
+ fi
+}
+
+_prepend_dir_context_if_necessary() {
+ if [ "$1" = . ]; then
+ printf '%s\n' "$2"
+ else
+ _prepend_path_if_relative "$1" "$2"
+ fi
+}
+
+_prepend_path_if_relative() {
+ case "$2" in
+ /* ) printf '%s\n' "$2" ;;
+ * ) printf '%s\n' "$1/$2" ;;
+ esac
+}
+
+_assert_no_path_cycles() {
+ local target path
+
+ target=$1
+ shift
+
+ for path in "$@"; do
+ if [ "$path" = "$target" ]; then
+ return 1
+ fi
+ done
+}
+
+canonicalize_path() {
+ if [ -d "$1" ]; then
+ _canonicalize_dir_path "$1"
+ else
+ _canonicalize_file_path "$1"
+ fi
+}
+
+_canonicalize_dir_path() {
+ (cd "$1" 2>/dev/null && pwd -P)
+}
+
+_canonicalize_file_path() {
+ local dir file
+ dir=$(dirname -- "$1")
+ file=$(basename -- "$1")
+ (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
+}
diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys
index 27fede6277..c14f90b04b 100644
--- a/contrib/verify-commits/trusted-keys
+++ b/contrib/verify-commits/trusted-keys
@@ -4,3 +4,4 @@
B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B
CA03882CB1FC067B5D3ACFE4D300116E1C875A3D
E777299FC265DD04793070EB944D35F9AC3DB76A
+D1DBF2C4B96F2DEBF4C16654410108112E7EA81F
diff --git a/contrib/windeploy/detached-sig-create.sh b/contrib/windeploy/detached-sig-create.sh
index 31720e72e7..29802e622e 100755
--- a/contrib/windeploy/detached-sig-create.sh
+++ b/contrib/windeploy/detached-sig-create.sh
@@ -25,7 +25,7 @@ CERTFILE="win-codesign.cert"
mkdir -p "${OUTSUBDIR}"
basename -a $(ls -1 "${SRCDIR}"/*-unsigned.exe) | while read UNSIGNED; do
echo Signing "${UNSIGNED}"
- "${OSSLSIGNCODE}" sign -certs "${CERTFILE}" -t "${TIMESERVER}" -in "${SRCDIR}/${UNSIGNED}" -out "${WORKDIR}/${UNSIGNED}" "$@"
+ "${OSSLSIGNCODE}" sign -certs "${CERTFILE}" -t "${TIMESERVER}" -h sha256 -in "${SRCDIR}/${UNSIGNED}" -out "${WORKDIR}/${UNSIGNED}" "$@"
"${OSSLSIGNCODE}" extract-signature -pem -in "${WORKDIR}/${UNSIGNED}" -out "${OUTSUBDIR}/${UNSIGNED}.pem" && rm "${WORKDIR}/${UNSIGNED}"
done
diff --git a/contrib/windeploy/win-codesign.cert b/contrib/windeploy/win-codesign.cert
index 4023a5b638..e763df5847 100644
--- a/contrib/windeploy/win-codesign.cert
+++ b/contrib/windeploy/win-codesign.cert
@@ -1,100 +1,89 @@
-----BEGIN CERTIFICATE-----
-MIIFdDCCBFygAwIBAgIRAL98pqZb/N9LuNaNxKsHNGQwDQYJKoZIhvcNAQELBQAw
-fDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQD
-ExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMjAwMzI0MDAwMDAwWhcN
-MjEwMzI0MjM1OTU5WjCBtzELMAkGA1UEBhMCQ0gxDTALBgNVBBEMBDgwMDUxDjAM
-BgNVBAgMBVN0YXRlMRAwDgYDVQQHDAdaw7xyaWNoMRcwFQYDVQQJDA5NYXR0ZW5n
-YXNzZSAyNzEuMCwGA1UECgwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3Nv
-Y2lhdGlvbjEuMCwGA1UEAwwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3Nv
-Y2lhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtxC8N4r/jE
-OGOdFy/0UtiUvEczPZf9WYZz/7paAkc75XopHIE5/ssmoEX27gG9K00tf3Q62QAx
-inZUPWkNTh8X0l+6uSGiIBFIV7dDgztIxnPcxaqw0k7Q2TEqKJvb5qm16zX6WfXJ
-R2r6O5utUdQ3AarHnQq9fwdM1j5+ywS5u52te74ENgDMTMKUuB2J3KH1ASg5PAtO
-CjPqPL+ZXJ7eT3M0Z+Lbu5ISZSqZB48BcCwOo/fOO0dAiLT9FE1iVtaCpBKHqGmd
-glRjPzZdgDv8g28etRmk8wQ5pQmfL2gBjt/LtIgMPTdHHETKLxJO5H3y0CNx1vzL
-ql7xNMxELxkCAwEAAaOCAbMwggGvMB8GA1UdIwQYMBaAFA7hOqhTOjHVir7Bu61n
-GgOFrTQOMB0GA1UdDgQWBBSHBbl82FUJiUkXyyYJog1awYRsxjAOBgNVHQ8BAf8E
-BAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgB
-hvhCAQEEBAMCBBAwQAYDVR0gBDkwNzA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEF
-BQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwQwYDVR0fBDwwOjA4oDagNIYy
-aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5j
-cmwwcwYIKwYBBQUHAQEEZzBlMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3Rp
-Z28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNydDAjBggrBgEFBQcwAYYX
-aHR0cDovL29jc3Auc2VjdGlnby5jb20wKwYDVR0RBCQwIoEgam9uYXNAYml0Y29p
-bmNvcmVjb2Rlc2lnbmluZy5vcmcwDQYJKoZIhvcNAQELBQADggEBAAU59qJzQ2ED
-aTMIQTsU01zIhZJ/xwQh78i0v2Mnr46RvzYrZOev+btF3SyUYD8veNnbYlY6yEYq
-Vb+/PQnE3t1xlqR80qiTZCk/Wmxx/qKvQuWeRL5QQgvsCmWBpycQ7PNfwzOWxbPE
-b0Hb2/VFFZfR9iltkfeInRUrzS96CJGYtm7dMf2JtnXYBcwpn1N8BSMH4nXVyN8g
-VEE5KyjE7+/awYiSST7+e6Y7FE5AJ4f3FjqnRm+2XetTVqITwMLKZMoV283nSEeH
-fA4FNAMGz9QeV38ol65NNqFP2vSSgVoPK79orqH9OOW2LSobt2qun+euddJIQeYV
-CMP90b/2WPc=
+MIIGQzCCBSugAwIBAgIQBSN7Cm16Z0UT9p7lA2jiKDANBgkqhkiG9w0BAQsFADBy
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQg
+SUQgQ29kZSBTaWduaW5nIENBMB4XDTIxMDUyMTAwMDAwMFoXDTIyMDUyNjIzNTk1
+OVowgYAxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMF
+TGV3ZXMxJjAkBgNVBAoTHUJpdGNvaW4gQ29yZSBDb2RlIFNpZ25pbmcgTExDMSYw
+JAYDVQQDEx1CaXRjb2luIENvcmUgQ29kZSBTaWduaW5nIExMQzCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAKe6xtFgKAQ68MvxwCjNtpgPobfDQCLKvCAN
+uBKGYuub6ufQB5dhCLN9fjMgfg33AyauvU3PcEUDUWD3/k925bPqgxHC3E7YqoB+
+11b/2Y7a86okqUgcGgvKhaKoHmXxElpM9EjQHjJ0yL4QAR1Lp+9CMMW3wIulBYKt
+wLIArFvbuQhMO/6rxL8frpK049v//WfQzB16GXuFnzN/6fDK7oOt5IrKTg4H6EY2
+fj4+QaUj0lNX7aHnZ6Ki45h2RUPDgN1ipRIuhM67npyZ/tdzPPjI3PUgfXCccN6D
++qWWnbbbvPuOht4ziPciVnPd57PqJmAOnLI86gisDfd7VKlcpOSEaagdUGvMbU6f
+uAps818GwnJzwCGllxlKASCgXDAckLLvMuit4RfYAhhdhw5R0AsaWK0HW88oHOqi
+U7eWlMCbSGk34x9hBrxYl7tvcNcLPWIPYrrhFWNFpkV8bVVIoV5rUNRgWvBcdOq1
+CCPTfsJp3nEH2WCoBghZquDZLSW12wMw2UsQyEojBeGhrR1inn8uK93wSnVCC8F4
+21yWNRMNe/LQVhmZDgFOen9r/WijBsBdQw1bL8N4zGdYv8+soqkrWzW417FfSx81
+pj4j5FEXYXXV5k/4/eBpIARXVRR8xya0nGkhNJmBk0jjDGD8fPW2gFQbqnUwAQ34
+vOr8NUqHAgMBAAGjggHEMIIBwDAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32
+ZXUOWDAdBgNVHQ4EFgQUVSLtZnifEHvd8z3E7AyLYNuDiaMwDgYDVR0PAQH/BAQD
+AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Ax
+hi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNy
+bDBLBgNVHSAERDBCMDYGCWCGSAGG/WwDATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYw
+JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcw
+AoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3Vy
+ZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
+BQADggEBAOaJneI91NJgqghUxgc0AWQ01SAJTgN4z7xMQ3W0ZAtwGbA0byT7YRlj
+j7h+j+hMX/JYkRJETTh8Nalq2tPWJBiMMEPOGFVttFER1pwouHkK9pSKyp4xRvNU
+L0LPh7fE4EYMJoynys6ZTpMCHLku+X3jFat1+1moh9TJRvK5+ETZYGl0seFNU3mJ
+dZzusObm4scffIGgi40kmmISKd5ZRuooRTu9FFR/3vpfbA+7Vg4RSH3CcQPo9bfk
++h/qRQhSfQInTBn7obRpIlvEcK782qivqseJGdtnTmcdVRShD5ckTVza1yv25uQz
+l/yTqmG2LXlYjl5iMSdF0C1xYq6IsOA=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
-iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
-cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
-BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
-MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
-BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
-aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
-dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
-3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
-tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
-Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
-VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
-79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
-c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
-Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
-c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
-UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
-Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
-BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
-A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
-Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
-VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
-ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
-8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
-iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
-Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
-XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
-qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
-VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
-L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
-jjxDah2nGN59PRbxYvnKkKj9
+MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBT
+aWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8O
+Ea9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq
+8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRp
+wsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/p
+fMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3
+jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczye
+n6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV
+HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBr
+MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUH
+MAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
+RFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj
+ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmww
+TwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsq
+CqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgP
+MA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHX
+fgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddf
+RHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8Al
+EeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+
+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8
+B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHv
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIF9TCCA92gAwIBAgIQHaJIMG+bJhjQguCWfTPTajANBgkqhkiG9w0BAQwFADCB
-iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
-cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
-BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
-MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjB8MQswCQYDVQQGEwJHQjEbMBkGA1UE
-CBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQK
-Ew9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2ln
-bmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIYijTKFehif
-SfCWL2MIHi3cfJ8Uz+MmtiVmKUCGVEZ0MWLFEO2yhyemmcuVMMBW9aR1xqkOUGKl
-UZEQauBLYq798PgYrKf/7i4zIPoMGYmobHutAMNhodxpZW0fbieW15dRhqb0J+V8
-aouVHltg1X7XFpKcAC9o95ftanK+ODtj3o+/bkxBXRIgCFnoOc2P0tbPBrRXBbZO
-oT5Xax+YvMRi1hsLjcdmG0qfnYHEckC14l/vC0X/o84Xpi1VsLewvFRqnbyNVlPG
-8Lp5UEks9wO5/i9lNfIi6iwHr0bZ+UYc3Ix8cSjz/qfGFN1VkW6KEQ3fBiSVfQ+n
-oXw62oY1YdMCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvA
-nfKyA2bLMB0GA1UdDgQWBBQO4TqoUzox1Yq+wbutZxoDha00DjAOBgNVHQ8BAf8E
-BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAwYI
-KwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0
-dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9u
-QXV0aG9yaXR5LmNybDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6
-Ly9jcnQudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAl
-BggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0B
-AQwFAAOCAgEATWNQ7Uc0SmGk295qKoyb8QAAHh1iezrXMsL2s+Bjs/thAIiaG20Q
-BwRPvrjqiXgi6w9G7PNGXkBGiRL0C3danCpBOvzW9Ovn9xWVM8Ohgyi33i/klPeF
-M4MtSkBIv5rCT0qxjyT0s4E307dksKYjalloUkJf/wTr4XRleQj1qZPea3FAmZa6
-ePG5yOLDCBaxq2NayBWAbXReSnV+pbjDbLXP30p5h1zHQE1jNfYw08+1Cg4LBH+g
-S667o6XQhACTPlNdNKUANWlsvp8gJRANGftQkGG+OY96jk32nw4e/gdREmaDJhlI
-lc5KycF/8zoFm/lv34h/wCOe0h5DekUxwZxNqfBZslkZ6GqNKQQCd3xLS81wvjqy
-VVp4Pry7bwMQJXcVNIr5NsxDkuS6T/FikyglVyn7URnHoSVAaoRXxrKdsbwcCtp8
-Z359LukoTBh+xHsxQXGaSynsCz1XUNLK3f2eBVHlRHjdAd6xdZgNVCT98E7j4viD
-vXK6yz067vBeF5Jobchh+abxKgoLpbn0nu6YMgWFnuv5gynTxix9vTp3Los3QqBq
-gu07SqqUEKThDfgXxbZaeTMYkuO1dfih6Y4KJR7kHvGfWocj/5+kUZ77OYARzdu1
-xKeogG/lU9Tg46LC0lsa+jImLWpXcBw8pFguo/NbSwfcMlnzh6cabVg=
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
+JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
+mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
+VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
+AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
+AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
+pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
+dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
+fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
+NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
+H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----
+
diff --git a/depends/Makefile b/depends/Makefile
index 4cd4d72fc2..ac12e91e49 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.
@@ -34,6 +34,8 @@ BASE_CACHE ?= $(BASEDIR)/built
SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
NO_QR ?=
+NO_BDB ?=
+NO_SQLITE ?=
NO_WALLET ?=
NO_ZMQ ?=
NO_UPNP ?=
@@ -112,37 +114,31 @@ include builders/$(build_os).mk
include builders/default.mk
include packages/packages.mk
-full_env=$(shell printenv)
-
-build_id_string:=$(BUILD_ID_SALT)
-
-# 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.
+# Previously, we directly invoked the well-known programs using $(shell ...)
+# to contruct build_id_string. However, that was problematic because:
#
-# '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) -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
-# depends-managed, pinned clang
-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)
+# 1. When invoking a shell, GNU Make special-cases exit code 127 (command not
+# found) by not capturing the output but instead passing it through. This is
+# not done for any other exit code.
+#
+# 2. Characters like '#' (from these programs' output) would end up in make
+# variables like build_id_string, which would be wrongly interpreted by make
+# when these variables were used.
+#
+# Therefore, we should avoid having arbitrary strings in make variables where
+# possible. The gen_id script used here hashes the output to construct a
+# "make-safe" id.
+#
+# Also note that these lines need to be:
+#
+# 1. After including {hosts,builders}/*.mk, since they rely on the tool
+# variables (e.g. build_CC, host_STRIP, etc.) to be set.
+#
+# 2. Before including packages/*.mk (excluding packages/packages.mk), since
+# they rely on the build_id variables
+#
+build_id:=$(shell env CC='$(build_CC)' CXX='$(build_CXX)' AR='$(build_AR)' RANLIB='$(build_RANLIB)' STRIP='$(build_STRIP)' SHA256SUM='$(build_SHA256SUM)' DEBUG='$(DEBUG)' ./gen_id '$(BUILD_ID_SALT)' 'GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))')
+$(host_arch)_$(host_os)_id:=$(shell env CC='$(host_CC)' CXX='$(host_CXX)' AR='$(host_AR)' RANLIB='$(host_RANLIB)' STRIP='$(host_STRIP)' SHA256SUM='$(build_SHA256SUM)' DEBUG='$(DEBUG)' ./gen_id '$(HOST_ID_SALT)' 'GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))')
qrencode_packages_$(NO_QR) = $(qrencode_packages)
@@ -233,6 +229,8 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_
-e 's|@no_qr@|$(NO_QR)|' \
-e 's|@no_zmq@|$(NO_ZMQ)|' \
-e 's|@no_wallet@|$(NO_WALLET)|' \
+ -e 's|@no_bdb@|$(NO_BDB)|' \
+ -e 's|@no_sqlite@|$(NO_SQLITE)|' \
-e 's|@no_upnp@|$(NO_UPNP)|' \
-e 's|@no_natpmp@|$(NO_NATPMP)|' \
-e 's|@multiprocess@|$(MULTIPROCESS)|' \
diff --git a/depends/README.md b/depends/README.md
index a1d4a99084..50e1a32c70 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -12,15 +12,18 @@ For example:
make HOST=x86_64-w64-mingw32 -j4
-**Bitcoin Core's configure script by default will ignore the depends output.** In
+**Bitcoin Core's `configure` script by default will ignore the depends output.** In
order for it to pick up libraries, tools, and settings from the depends build,
-you must point it at the appropriate `--prefix` directory generated by the
-build. In the above example, a prefix dir named x86_64-w64-mingw32 will be
-created. To use it for Bitcoin:
+you must set the `CONFIG_SITE` environment variable to point to a `config.site` settings file.
+In the above example, a file named `depends/x86_64-w64-mingw32/share/config.site` will be
+created. To use it during compilation:
- ./configure --prefix=$PWD/depends/x86_64-w64-mingw32
+ CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure
-Common `host-platform-triplets` for cross compilation are:
+The default install prefix when using `config.site` is `--prefix=depends/<host-platform-triplet>`,
+so depends build outputs will be installed in that location.
+
+Common `host-platform-triplet`s for cross compilation are:
- `i686-pc-linux-gnu` for Linux 32 bit
- `x86_64-pc-linux-gnu` for x86 Linux
@@ -46,6 +49,11 @@ The paths are automatically configured and no other options are needed unless ta
sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libz-dev python3-setuptools libtinfo5 xorriso
+Note: You must obtain the macOS SDK before proceeding with a cross-compile.
+Under the depends directory, create a subdirectory named `SDKs`.
+Then, place the extracted SDK under this new directory.
+For more information, see [SDK Extraction](../contrib/macdeploy/README.md#sdk-extraction).
+
#### For Win64 cross compilation
- see [build-windows.md](../doc/build-windows.md#cross-compilation-for-ubuntu-and-windows-subsystem-for-linux)
@@ -133,4 +141,3 @@ This is an example command for a default build with no disabled dependencies:
- [description.md](description.md): General description of the depends system
- [packages.md](packages.md): Steps for adding packages
-
diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk
index f4103fc1f2..001c928424 100644
--- a/depends/builders/darwin.mk
+++ b/depends/builders/darwin.mk
@@ -1,5 +1,5 @@
-build_darwin_CC:=$(shell xcrun -f clang) --sysroot $(shell xcrun --show-sdk-path)
-build_darwin_CXX:=$(shell xcrun -f clang++) --sysroot $(shell xcrun --show-sdk-path)
+build_darwin_CC:=$(shell xcrun -f clang) -isysroot$(shell xcrun --show-sdk-path)
+build_darwin_CXX:=$(shell xcrun -f clang++) -isysroot$(shell xcrun --show-sdk-path)
build_darwin_AR:=$(shell xcrun -f ar)
build_darwin_RANLIB:=$(shell xcrun -f ranlib)
build_darwin_STRIP:=$(shell xcrun -f strip)
@@ -10,8 +10,8 @@ build_darwin_SHA256SUM=shasum -a 256
build_darwin_DOWNLOAD=curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o
#darwin host on darwin builder. overrides darwin host preferences.
-darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(shell xcrun --show-sdk-path)
-darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ --sysroot $(shell xcrun --show-sdk-path)
+darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) -isysroot$(shell xcrun --show-sdk-path)
+darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ -isysroot$(shell xcrun --show-sdk-path)
darwin_AR:=$(shell xcrun -f ar)
darwin_RANLIB:=$(shell xcrun -f ranlib)
darwin_STRIP:=$(shell xcrun -f strip)
diff --git a/depends/config.guess b/depends/config.guess
index 7f9ebbe310..dc0a6b2997 100755
--- a/depends/config.guess
+++ b/depends/config.guess
@@ -1,8 +1,8 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2019 Free Software Foundation, Inc.
+# Copyright 1992-2021 Free Software Foundation, Inc.
-timestamp='2019-09-10'
+timestamp='2021-05-24'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -27,12 +27,12 @@ timestamp='2019-09-10'
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
-# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
#
# Please send patches to <config-patches@gnu.org>.
-me=`echo "$0" | sed -e 's,.*/,,'`
+me=$(echo "$0" | sed -e 's,.*/,,')
usage="\
Usage: $0 [OPTION]
@@ -50,7 +50,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2019 Free Software Foundation, Inc.
+Copyright 1992-2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -99,9 +99,11 @@ tmp=
trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
: "${TMPDIR=/tmp}"
# shellcheck disable=SC2039
- { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
@@ -129,16 +131,14 @@ if test -f /.attbin/uname ; then
PATH=$PATH:/.attbin ; export PATH
fi
-UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
-UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
-UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
-UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown
+UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown
+UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown
+UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown
-case "$UNAME_SYSTEM" in
+case $UNAME_SYSTEM in
Linux|GNU|GNU/*)
- # If the system lacks a compiler, then just pick glibc.
- # We could probably try harder.
- LIBC=gnu
+ LIBC=unknown
set_cc_for_build
cat <<-EOF > "$dummy.c"
@@ -147,24 +147,36 @@ Linux|GNU|GNU/*)
LIBC=uclibc
#elif defined(__dietlibc__)
LIBC=dietlibc
- #else
+ #elif defined(__GLIBC__)
LIBC=gnu
+ #else
+ #include <stdarg.h>
+ /* First heuristic to detect musl libc. */
+ #ifdef __DEFINED_va_list
+ LIBC=musl
+ #endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+ eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')"
- # If ldd exists, use it to detect musl libc.
- if command -v ldd >/dev/null && \
- ldd --version 2>&1 | grep -q ^musl
- then
- LIBC=musl
+ # Second heuristic to detect musl libc.
+ if [ "$LIBC" = unknown ] &&
+ command -v ldd >/dev/null &&
+ ldd --version 2>&1 | grep -q ^musl; then
+ LIBC=musl
+ fi
+
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ if [ "$LIBC" = unknown ]; then
+ LIBC=gnu
fi
;;
esac
# Note: order is significant - the case branches are not exclusive.
-case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
@@ -176,27 +188,27 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
#
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
- sysctl="sysctl -n hw.machine_arch"
- UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
- "/sbin/$sysctl" 2>/dev/null || \
- "/usr/sbin/$sysctl" 2>/dev/null || \
- echo unknown)`
- case "$UNAME_MACHINE_ARCH" in
+ UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
+ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+ echo unknown))
+ case $UNAME_MACHINE_ARCH in
+ aarch64eb) machine=aarch64_be-unknown ;;
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
earmv*)
- arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
- endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,')
+ endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p')
machine="${arch}${endian}"-unknown
;;
*) machine="$UNAME_MACHINE_ARCH"-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently (or will in the future) and ABI.
- case "$UNAME_MACHINE_ARCH" in
+ case $UNAME_MACHINE_ARCH in
earm*)
os=netbsdelf
;;
@@ -217,10 +229,10 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
;;
esac
# Determine ABI tags.
- case "$UNAME_MACHINE_ARCH" in
+ case $UNAME_MACHINE_ARCH in
earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
- abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr")
;;
esac
# The OS release
@@ -228,12 +240,12 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
# thus, need a distinct triplet. However, they do not need
# kernel version information, so it can be replaced with a
# suitable tag, in the style of linux-gnu.
- case "$UNAME_VERSION" in
+ case $UNAME_VERSION in
Debian*)
release='-gnu'
;;
*)
- release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2)
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
@@ -242,15 +254,19 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
echo "$machine-${os}${release}${abi-}"
exit ;;
*:Bitrig:*:*)
- UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//')
echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
exit ;;
*:OpenBSD:*:*)
- UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//')
echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
exit ;;
+ *:SecBSD:*:*)
+ UNAME_MACHINE_ARCH=$(arch | sed 's/SecBSD.//')
+ echo "$UNAME_MACHINE_ARCH"-unknown-secbsd"$UNAME_RELEASE"
+ exit ;;
*:LibertyBSD:*:*)
- UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//')
echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
exit ;;
*:MidnightBSD:*:*)
@@ -284,20 +300,22 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
echo mips-dec-osf1
exit ;;
alpha:OSF1:*:*)
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ trap '' 0
case $UNAME_RELEASE in
*4.0)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}')
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}')
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
# OSF/1 and Tru64 systems produced since 1995. I hope that
# covers most systems running today. This code pipes the CPU
# types through head -n 1, so we only detect the type of CPU 0.
- ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
- case "$ALPHA_CPU_TYPE" in
+ ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1)
+ case $ALPHA_CPU_TYPE in
"EV4 (21064)")
UNAME_MACHINE=alpha ;;
"EV4.5 (21064)")
@@ -334,11 +352,8 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
- echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
- # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
- exitcode=$?
- trap '' 0
- exit $exitcode ;;
+ echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)"
+ exit ;;
Amiga*:UNIX_System_V:4.0:*)
echo m68k-unknown-sysv4
exit ;;
@@ -368,7 +383,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
exit ;;
Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
- if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ if test "$( (/bin/universe) 2>/dev/null)" = att ; then
echo pyramid-pyramid-sysv3
else
echo pyramid-pyramid-bsd
@@ -381,17 +396,17 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
echo sparc-icl-nx6
exit ;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
- case `/usr/bin/uname -p` in
+ case $(/usr/bin/uname -p) in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
s390x:SunOS:*:*)
- echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
exit ;;
sun4H:SunOS:5.*:*)
- echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
- echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
exit ;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
echo i386-pc-auroraux"$UNAME_RELEASE"
@@ -402,7 +417,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
- if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
@@ -410,30 +425,30 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
SUN_ARCH=x86_64
fi
fi
- echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4.
- echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;;
sun4*:SunOS:*:*)
- case "`/usr/bin/arch -k`" in
+ case $(/usr/bin/arch -k) in
Series*|S4*)
- UNAME_RELEASE=`uname -v`
+ UNAME_RELEASE=$(uname -v)
;;
esac
# Japanese Language versions have a version number like `4.1.3-JL'.
- echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+ echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')"
exit ;;
sun3*:SunOS:*:*)
echo m68k-sun-sunos"$UNAME_RELEASE"
exit ;;
sun*:*:4.2BSD:*)
- UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null)
test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
- case "`/bin/arch`" in
+ case $(/bin/arch) in
sun3)
echo m68k-sun-sunos"$UNAME_RELEASE"
;;
@@ -513,8 +528,8 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
- dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
- SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') &&
+ SYSTEM_NAME=$("$dummy" "$dummyarg") &&
{ echo "$SYSTEM_NAME"; exit; }
echo mips-mips-riscos"$UNAME_RELEASE"
exit ;;
@@ -541,11 +556,11 @@ EOF
exit ;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
- if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
then
- if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
- [ "$TARGET_BINARY_INTERFACE"x = x ]
+ if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+ test "$TARGET_BINARY_INTERFACE"x = x
then
echo m88k-dg-dgux"$UNAME_RELEASE"
else
@@ -569,17 +584,17 @@ EOF
echo m68k-tektronix-bsd
exit ;;
*:IRIX*:*:*)
- echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+ echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')"
exit ;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
- exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX '
i*86:AIX:*:*)
echo i386-ibm-aix
exit ;;
ia64:AIX:*:*)
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if test -x /usr/bin/oslevel ; then
+ IBM_REV=$(/usr/bin/oslevel)
else
IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
@@ -599,7 +614,7 @@ EOF
exit(0);
}
EOF
- if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy")
then
echo "$SYSTEM_NAME"
else
@@ -612,15 +627,15 @@ EOF
fi
exit ;;
*:AIX:*:[4567])
- IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }')
if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
- if [ -x /usr/bin/lslpp ] ; then
- IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
- awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ if test -x /usr/bin/lslpp ; then
+ IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/)
else
IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
@@ -648,26 +663,26 @@ EOF
echo m68k-hp-bsd4.4
exit ;;
9000/[34678]??:HP-UX:*:*)
- HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
- case "$UNAME_MACHINE" in
+ HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
+ case $UNAME_MACHINE in
9000/31?) HP_ARCH=m68000 ;;
9000/[34]??) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
- if [ -x /usr/bin/getconf ]; then
- sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
- case "$sc_cpu_version" in
+ if test -x /usr/bin/getconf; then
+ sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null)
+ sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null)
+ case $sc_cpu_version in
523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
- case "$sc_kernel_bits" in
+ case $sc_kernel_bits in
32) HP_ARCH=hppa2.0n ;;
64) HP_ARCH=hppa2.0w ;;
'') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
esac ;;
esac
fi
- if [ "$HP_ARCH" = "" ]; then
+ if test "$HP_ARCH" = ""; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
@@ -702,11 +717,11 @@ EOF
exit (0);
}
EOF
- (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy")
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
- if [ "$HP_ARCH" = hppa2.0w ]
+ if test "$HP_ARCH" = hppa2.0w
then
set_cc_for_build
@@ -730,7 +745,7 @@ EOF
echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
exit ;;
ia64:HP-UX:*:*)
- HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
echo ia64-hp-hpux"$HPUX_REV"
exit ;;
3050*:HI-UX:*:*)
@@ -760,7 +775,7 @@ EOF
exit (0);
}
EOF
- $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") &&
{ echo "$SYSTEM_NAME"; exit; }
echo unknown-hitachi-hiuxwe2
exit ;;
@@ -780,7 +795,7 @@ EOF
echo hppa1.0-hp-osf
exit ;;
i*86:OSF1:*:*)
- if [ -x /usr/sbin/sysversion ] ; then
+ if test -x /usr/sbin/sysversion ; then
echo "$UNAME_MACHINE"-unknown-osf1mk
else
echo "$UNAME_MACHINE"-unknown-osf1
@@ -829,14 +844,14 @@ EOF
echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
- FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
- FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
- FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)
+ FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
+ FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/')
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
5000:UNIX_System_V:4.*:*)
- FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
- FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
+ FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/')
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
@@ -849,25 +864,25 @@ EOF
echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
exit ;;
arm:FreeBSD:*:*)
- UNAME_PROCESSOR=`uname -p`
+ UNAME_PROCESSOR=$(uname -p)
set_cc_for_build
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
- echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi
else
- echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf
fi
exit ;;
*:FreeBSD:*:*)
- UNAME_PROCESSOR=`/usr/bin/uname -p`
- case "$UNAME_PROCESSOR" in
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
i386)
UNAME_PROCESSOR=i586 ;;
esac
- echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
exit ;;
i*:CYGWIN*:*)
echo "$UNAME_MACHINE"-pc-cygwin
@@ -885,7 +900,7 @@ EOF
echo "$UNAME_MACHINE"-pc-pw32
exit ;;
*:Interix*:*)
- case "$UNAME_MACHINE" in
+ case $UNAME_MACHINE in
x86)
echo i586-pc-interix"$UNAME_RELEASE"
exit ;;
@@ -903,15 +918,15 @@ EOF
echo x86_64-pc-cygwin
exit ;;
prep*:SunOS:5.*:*)
- echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;;
*:GNU:*:*)
# the GNU system
- echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+ echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')"
exit ;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
- echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC"
exit ;;
*:Minix:*:*)
echo "$UNAME_MACHINE"-unknown-minix
@@ -924,7 +939,7 @@ EOF
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
alpha:Linux:*:*)
- case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
@@ -937,7 +952,7 @@ EOF
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- arc:Linux:*:* | arceb:Linux:*:*)
+ arc:Linux:*:* | arceb:Linux:*:* | arc64:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
arm*:Linux:*:*)
@@ -983,6 +998,9 @@ EOF
k1om:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
+ loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
m32r*:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
@@ -1033,7 +1051,7 @@ EOF
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`"
+ eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')"
test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
;;
mips64el:Linux:*:*)
@@ -1053,7 +1071,7 @@ EOF
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
- case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in
PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
*) echo hppa-unknown-linux-"$LIBC" ;;
@@ -1071,7 +1089,7 @@ EOF
ppcle:Linux:*:*)
echo powerpcle-unknown-linux-"$LIBC"
exit ;;
- riscv32:Linux:*:* | riscv64:Linux:*:*)
+ riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
@@ -1093,7 +1111,17 @@ EOF
echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
exit ;;
x86_64:Linux:*:*)
- echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ set_cc_for_build
+ LIBCABI=$LIBC
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_X32 >/dev/null
+ then
+ LIBCABI="$LIBC"x32
+ fi
+ fi
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI"
exit ;;
xtensa*:Linux:*:*)
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
@@ -1133,7 +1161,7 @@ EOF
echo "$UNAME_MACHINE"-pc-msdosdjgpp
exit ;;
i*86:*:4.*:*)
- UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//')
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
else
@@ -1142,7 +1170,7 @@ EOF
exit ;;
i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6.
- case `/bin/uname -X | grep "^Machine"` in
+ case $(/bin/uname -X | grep "^Machine") in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;;
@@ -1151,10 +1179,10 @@ EOF
exit ;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
- UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ UNAME_REL=$(sed -n 's/.*Version //p' </usr/options/cb.name)
echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
elif /bin/uname -X 2>/dev/null >/dev/null ; then
- UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //'))
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586
@@ -1204,7 +1232,7 @@ EOF
3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
OS_REL=''
test -r /etc/.relid \
- && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
@@ -1215,7 +1243,7 @@ EOF
NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3'
test -r /etc/.relid \
- && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
@@ -1248,7 +1276,7 @@ EOF
exit ;;
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ UNAME_MACHINE=$( (uname -p) 2>/dev/null)
echo "$UNAME_MACHINE"-sni-sysv4
else
echo ns32k-sni-sysv
@@ -1282,7 +1310,7 @@ EOF
echo mips-sony-newsos6
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
- if [ -d /usr/nec ]; then
+ if test -d /usr/nec; then
echo mips-nec-sysv"$UNAME_RELEASE"
else
echo mips-unknown-sysv"$UNAME_RELEASE"
@@ -1330,8 +1358,11 @@ EOF
*:Rhapsody:*:*)
echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
exit ;;
+ arm64:Darwin:*:*)
+ echo aarch64-apple-darwin"$UNAME_RELEASE"
+ exit ;;
*:Darwin:*:*)
- UNAME_PROCESSOR=`uname -p`
+ UNAME_PROCESSOR=$(uname -p)
case $UNAME_PROCESSOR in
unknown) UNAME_PROCESSOR=powerpc ;;
esac
@@ -1344,7 +1375,7 @@ EOF
else
set_cc_for_build
fi
- if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
@@ -1368,7 +1399,7 @@ EOF
echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
- UNAME_PROCESSOR=`uname -p`
+ UNAME_PROCESSOR=$(uname -p)
if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
@@ -1406,10 +1437,9 @@ EOF
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
- # shellcheck disable=SC2154
- if test "$cputype" = 386; then
+ if test "${cputype-}" = 386; then
UNAME_MACHINE=i386
- else
+ elif test "x${cputype-}" != x; then
UNAME_MACHINE="$cputype"
fi
echo "$UNAME_MACHINE"-unknown-plan9
@@ -1436,11 +1466,11 @@ EOF
echo mips-sei-seiux"$UNAME_RELEASE"
exit ;;
*:DragonFly:*:*)
- echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
exit ;;
*:*VMS:*:*)
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- case "$UNAME_MACHINE" in
+ UNAME_MACHINE=$( (uname -p) 2>/dev/null)
+ case $UNAME_MACHINE in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
V*) echo vax-dec-vms ; exit ;;
@@ -1449,13 +1479,13 @@ EOF
echo i386-pc-xenix
exit ;;
i*86:skyos:*:*)
- echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+ echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')"
exit ;;
i*86:rdos:*:*)
echo "$UNAME_MACHINE"-pc-rdos
exit ;;
- i*86:AROS:*:*)
- echo "$UNAME_MACHINE"-pc-aros
+ *:AROS:*:*)
+ echo "$UNAME_MACHINE"-unknown-aros
exit ;;
x86_64:VMkernel:*:*)
echo "$UNAME_MACHINE"-unknown-esx
@@ -1507,7 +1537,7 @@ main ()
#define __ARCHITECTURE__ "m68k"
#endif
int version;
- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null);
if (version < 4)
printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
else
@@ -1599,7 +1629,7 @@ main ()
}
EOF
-$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) &&
{ echo "$SYSTEM_NAME"; exit; }
# Apollos put the system type in the environment.
@@ -1607,7 +1637,7 @@ test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
echo "$0: unable to guess system type" >&2
-case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+case $UNAME_MACHINE:$UNAME_SYSTEM in
mips:Linux | mips64:Linux)
# If we got here on MIPS GNU/Linux, output extra information.
cat >&2 <<EOF
@@ -1624,9 +1654,15 @@ This script (version $timestamp), has failed to recognize the
operating system you are using. If your script is old, overwrite *all*
copies of config.guess and config.sub with the latest versions from:
- https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
and
- https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+year=$(echo $timestamp | sed 's,-.*,,')
+# shellcheck disable=SC2003
+if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then
+ cat >&2 <<EOF
If $0 has already been updated, send the following data and any
information you think might be pertinent to config-patches@gnu.org to
@@ -1634,26 +1670,27 @@ provide the necessary information to handle your system.
config.guess timestamp = $timestamp
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
+uname -m = $( (uname -m) 2>/dev/null || echo unknown)
+uname -r = $( (uname -r) 2>/dev/null || echo unknown)
+uname -s = $( (uname -s) 2>/dev/null || echo unknown)
+uname -v = $( (uname -v) 2>/dev/null || echo unknown)
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
-/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null)
+/bin/uname -X = $( (/bin/uname -X) 2>/dev/null)
-hostinfo = `(hostinfo) 2>/dev/null`
-/bin/universe = `(/bin/universe) 2>/dev/null`
-/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
-/bin/arch = `(/bin/arch) 2>/dev/null`
-/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+hostinfo = $( (hostinfo) 2>/dev/null)
+/bin/universe = $( (/bin/universe) 2>/dev/null)
+/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null)
+/bin/arch = $( (/bin/arch) 2>/dev/null)
+/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null)
+/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null)
UNAME_MACHINE = "$UNAME_MACHINE"
UNAME_RELEASE = "$UNAME_RELEASE"
UNAME_SYSTEM = "$UNAME_SYSTEM"
UNAME_VERSION = "$UNAME_VERSION"
EOF
+fi
exit 1
diff --git a/depends/config.site.in b/depends/config.site.in
index f1a59a5861..5cf107f19b 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -38,6 +38,14 @@ if test -z "$enable_wallet" && test -n "@no_wallet@"; then
enable_wallet=no
fi
+if test -z "$with_bdb" && test -n "@no_bdb@"; then
+ with_bdb=no
+fi
+
+if test -z "$with_sqlite" && test -n "@no_sqlite@"; then
+ with_sqlite=no
+fi
+
if test -z "$enable_multiprocess" && test -n "@multiprocess@"; then
enable_multiprocess=yes
fi
@@ -62,7 +70,7 @@ if test -z "$enable_zmq" && test -n "@no_zmq@"; then
enable_zmq=no
fi
-if test "x@host_os@" = xdarwin; then
+if test "@host_os@" = darwin; then
BREW=no
PORT=no
fi
diff --git a/depends/config.sub b/depends/config.sub
index a318a46868..7384e9198b 100755
--- a/depends/config.sub
+++ b/depends/config.sub
@@ -1,8 +1,8 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2019 Free Software Foundation, Inc.
+# Copyright 1992-2021 Free Software Foundation, Inc.
-timestamp='2019-06-30'
+timestamp='2021-04-30'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ timestamp='2019-06-30'
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
-# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
@@ -50,7 +50,7 @@ timestamp='2019-06-30'
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.
-me=`echo "$0" | sed -e 's,.*/,,'`
+me=$(echo "$0" | sed -e 's,.*/,,')
usage="\
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
@@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2019 Free Software Foundation, Inc.
+Copyright 1992-2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -124,28 +124,27 @@ case $1 in
;;
*-*-*-*)
basic_machine=$field1-$field2
- os=$field3-$field4
+ basic_os=$field3-$field4
;;
*-*-*)
# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
# parts
maybe_os=$field2-$field3
case $maybe_os in
- nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
- | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ nto-qnx* | linux-* | uclinux-uclibc* \
| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
| storm-chaos* | os2-emx* | rtmk-nova*)
basic_machine=$field1
- os=$maybe_os
+ basic_os=$maybe_os
;;
android-linux)
basic_machine=$field1-unknown
- os=linux-android
+ basic_os=linux-android
;;
*)
basic_machine=$field1-$field2
- os=$field3
+ basic_os=$field3
;;
esac
;;
@@ -154,7 +153,7 @@ case $1 in
case $field1-$field2 in
decstation-3100)
basic_machine=mips-dec
- os=
+ basic_os=
;;
*-*)
# Second component is usually, but not always the OS
@@ -162,7 +161,7 @@ case $1 in
# Prevent following clause from handling this valid os
sun*os*)
basic_machine=$field1
- os=$field2
+ basic_os=$field2
;;
# Manufacturers
dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
@@ -175,11 +174,11 @@ case $1 in
| microblaze* | sim | cisco \
| oki | wec | wrs | winbond)
basic_machine=$field1-$field2
- os=
+ basic_os=
;;
*)
basic_machine=$field1
- os=$field2
+ basic_os=$field2
;;
esac
;;
@@ -191,447 +190,451 @@ case $1 in
case $field1 in
386bsd)
basic_machine=i386-pc
- os=bsd
+ basic_os=bsd
;;
a29khif)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
adobe68k)
basic_machine=m68010-adobe
- os=scout
+ basic_os=scout
;;
alliant)
basic_machine=fx80-alliant
- os=
+ basic_os=
;;
altos | altos3068)
basic_machine=m68k-altos
- os=
+ basic_os=
;;
am29k)
basic_machine=a29k-none
- os=bsd
+ basic_os=bsd
;;
amdahl)
basic_machine=580-amdahl
- os=sysv
+ basic_os=sysv
;;
amiga)
basic_machine=m68k-unknown
- os=
+ basic_os=
;;
amigaos | amigados)
basic_machine=m68k-unknown
- os=amigaos
+ basic_os=amigaos
;;
amigaunix | amix)
basic_machine=m68k-unknown
- os=sysv4
+ basic_os=sysv4
;;
apollo68)
basic_machine=m68k-apollo
- os=sysv
+ basic_os=sysv
;;
apollo68bsd)
basic_machine=m68k-apollo
- os=bsd
+ basic_os=bsd
;;
aros)
basic_machine=i386-pc
- os=aros
+ basic_os=aros
;;
aux)
basic_machine=m68k-apple
- os=aux
+ basic_os=aux
;;
balance)
basic_machine=ns32k-sequent
- os=dynix
+ basic_os=dynix
;;
blackfin)
basic_machine=bfin-unknown
- os=linux
+ basic_os=linux
;;
cegcc)
basic_machine=arm-unknown
- os=cegcc
+ basic_os=cegcc
;;
convex-c1)
basic_machine=c1-convex
- os=bsd
+ basic_os=bsd
;;
convex-c2)
basic_machine=c2-convex
- os=bsd
+ basic_os=bsd
;;
convex-c32)
basic_machine=c32-convex
- os=bsd
+ basic_os=bsd
;;
convex-c34)
basic_machine=c34-convex
- os=bsd
+ basic_os=bsd
;;
convex-c38)
basic_machine=c38-convex
- os=bsd
+ basic_os=bsd
;;
cray)
basic_machine=j90-cray
- os=unicos
+ basic_os=unicos
;;
crds | unos)
basic_machine=m68k-crds
- os=
+ basic_os=
;;
da30)
basic_machine=m68k-da30
- os=
+ basic_os=
;;
decstation | pmax | pmin | dec3100 | decstatn)
basic_machine=mips-dec
- os=
+ basic_os=
;;
delta88)
basic_machine=m88k-motorola
- os=sysv3
+ basic_os=sysv3
;;
dicos)
basic_machine=i686-pc
- os=dicos
+ basic_os=dicos
;;
djgpp)
basic_machine=i586-pc
- os=msdosdjgpp
+ basic_os=msdosdjgpp
;;
ebmon29k)
basic_machine=a29k-amd
- os=ebmon
+ basic_os=ebmon
;;
es1800 | OSE68k | ose68k | ose | OSE)
basic_machine=m68k-ericsson
- os=ose
+ basic_os=ose
;;
gmicro)
basic_machine=tron-gmicro
- os=sysv
+ basic_os=sysv
;;
go32)
basic_machine=i386-pc
- os=go32
+ basic_os=go32
;;
h8300hms)
basic_machine=h8300-hitachi
- os=hms
+ basic_os=hms
;;
h8300xray)
basic_machine=h8300-hitachi
- os=xray
+ basic_os=xray
;;
h8500hms)
basic_machine=h8500-hitachi
- os=hms
+ basic_os=hms
;;
harris)
basic_machine=m88k-harris
- os=sysv3
+ basic_os=sysv3
;;
hp300 | hp300hpux)
basic_machine=m68k-hp
- os=hpux
+ basic_os=hpux
;;
hp300bsd)
basic_machine=m68k-hp
- os=bsd
+ basic_os=bsd
;;
hppaosf)
basic_machine=hppa1.1-hp
- os=osf
+ basic_os=osf
;;
hppro)
basic_machine=hppa1.1-hp
- os=proelf
+ basic_os=proelf
;;
i386mach)
basic_machine=i386-mach
- os=mach
+ basic_os=mach
;;
isi68 | isi)
basic_machine=m68k-isi
- os=sysv
+ basic_os=sysv
;;
m68knommu)
basic_machine=m68k-unknown
- os=linux
+ basic_os=linux
;;
magnum | m3230)
basic_machine=mips-mips
- os=sysv
+ basic_os=sysv
;;
merlin)
basic_machine=ns32k-utek
- os=sysv
+ basic_os=sysv
;;
mingw64)
basic_machine=x86_64-pc
- os=mingw64
+ basic_os=mingw64
;;
mingw32)
basic_machine=i686-pc
- os=mingw32
+ basic_os=mingw32
;;
mingw32ce)
basic_machine=arm-unknown
- os=mingw32ce
+ basic_os=mingw32ce
;;
monitor)
basic_machine=m68k-rom68k
- os=coff
+ basic_os=coff
;;
morphos)
basic_machine=powerpc-unknown
- os=morphos
+ basic_os=morphos
;;
moxiebox)
basic_machine=moxie-unknown
- os=moxiebox
+ basic_os=moxiebox
;;
msdos)
basic_machine=i386-pc
- os=msdos
+ basic_os=msdos
;;
msys)
basic_machine=i686-pc
- os=msys
+ basic_os=msys
;;
mvs)
basic_machine=i370-ibm
- os=mvs
+ basic_os=mvs
;;
nacl)
basic_machine=le32-unknown
- os=nacl
+ basic_os=nacl
;;
ncr3000)
basic_machine=i486-ncr
- os=sysv4
+ basic_os=sysv4
;;
netbsd386)
basic_machine=i386-pc
- os=netbsd
+ basic_os=netbsd
;;
netwinder)
basic_machine=armv4l-rebel
- os=linux
+ basic_os=linux
;;
news | news700 | news800 | news900)
basic_machine=m68k-sony
- os=newsos
+ basic_os=newsos
;;
news1000)
basic_machine=m68030-sony
- os=newsos
+ basic_os=newsos
;;
necv70)
basic_machine=v70-nec
- os=sysv
+ basic_os=sysv
;;
nh3000)
basic_machine=m68k-harris
- os=cxux
+ basic_os=cxux
;;
nh[45]000)
basic_machine=m88k-harris
- os=cxux
+ basic_os=cxux
;;
nindy960)
basic_machine=i960-intel
- os=nindy
+ basic_os=nindy
;;
mon960)
basic_machine=i960-intel
- os=mon960
+ basic_os=mon960
;;
nonstopux)
basic_machine=mips-compaq
- os=nonstopux
+ basic_os=nonstopux
;;
os400)
basic_machine=powerpc-ibm
- os=os400
+ basic_os=os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
- os=ose
+ basic_os=ose
;;
os68k)
basic_machine=m68k-none
- os=os68k
+ basic_os=os68k
;;
paragon)
basic_machine=i860-intel
- os=osf
+ basic_os=osf
;;
parisc)
basic_machine=hppa-unknown
- os=linux
+ basic_os=linux
+ ;;
+ psp)
+ basic_machine=mipsallegrexel-sony
+ basic_os=psp
;;
pw32)
basic_machine=i586-unknown
- os=pw32
+ basic_os=pw32
;;
rdos | rdos64)
basic_machine=x86_64-pc
- os=rdos
+ basic_os=rdos
;;
rdos32)
basic_machine=i386-pc
- os=rdos
+ basic_os=rdos
;;
rom68k)
basic_machine=m68k-rom68k
- os=coff
+ basic_os=coff
;;
sa29200)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
sei)
basic_machine=mips-sei
- os=seiux
+ basic_os=seiux
;;
sequent)
basic_machine=i386-sequent
- os=
+ basic_os=
;;
sps7)
basic_machine=m68k-bull
- os=sysv2
+ basic_os=sysv2
;;
st2000)
basic_machine=m68k-tandem
- os=
+ basic_os=
;;
stratus)
basic_machine=i860-stratus
- os=sysv4
+ basic_os=sysv4
;;
sun2)
basic_machine=m68000-sun
- os=
+ basic_os=
;;
sun2os3)
basic_machine=m68000-sun
- os=sunos3
+ basic_os=sunos3
;;
sun2os4)
basic_machine=m68000-sun
- os=sunos4
+ basic_os=sunos4
;;
sun3)
basic_machine=m68k-sun
- os=
+ basic_os=
;;
sun3os3)
basic_machine=m68k-sun
- os=sunos3
+ basic_os=sunos3
;;
sun3os4)
basic_machine=m68k-sun
- os=sunos4
+ basic_os=sunos4
;;
sun4)
basic_machine=sparc-sun
- os=
+ basic_os=
;;
sun4os3)
basic_machine=sparc-sun
- os=sunos3
+ basic_os=sunos3
;;
sun4os4)
basic_machine=sparc-sun
- os=sunos4
+ basic_os=sunos4
;;
sun4sol2)
basic_machine=sparc-sun
- os=solaris2
+ basic_os=solaris2
;;
sun386 | sun386i | roadrunner)
basic_machine=i386-sun
- os=
+ basic_os=
;;
sv1)
basic_machine=sv1-cray
- os=unicos
+ basic_os=unicos
;;
symmetry)
basic_machine=i386-sequent
- os=dynix
+ basic_os=dynix
;;
t3e)
basic_machine=alphaev5-cray
- os=unicos
+ basic_os=unicos
;;
t90)
basic_machine=t90-cray
- os=unicos
+ basic_os=unicos
;;
toad1)
basic_machine=pdp10-xkl
- os=tops20
+ basic_os=tops20
;;
tpf)
basic_machine=s390x-ibm
- os=tpf
+ basic_os=tpf
;;
udi29k)
basic_machine=a29k-amd
- os=udi
+ basic_os=udi
;;
ultra3)
basic_machine=a29k-nyu
- os=sym1
+ basic_os=sym1
;;
v810 | necv810)
basic_machine=v810-nec
- os=none
+ basic_os=none
;;
vaxv)
basic_machine=vax-dec
- os=sysv
+ basic_os=sysv
;;
vms)
basic_machine=vax-dec
- os=vms
+ basic_os=vms
;;
vsta)
basic_machine=i386-pc
- os=vsta
+ basic_os=vsta
;;
vxworks960)
basic_machine=i960-wrs
- os=vxworks
+ basic_os=vxworks
;;
vxworks68)
basic_machine=m68k-wrs
- os=vxworks
+ basic_os=vxworks
;;
vxworks29k)
basic_machine=a29k-wrs
- os=vxworks
+ basic_os=vxworks
;;
xbox)
basic_machine=i686-pc
- os=mingw32
+ basic_os=mingw32
;;
ymp)
basic_machine=ymp-cray
- os=unicos
+ basic_os=unicos
;;
*)
basic_machine=$1
- os=
+ basic_os=
;;
esac
;;
@@ -683,17 +686,17 @@ case $basic_machine in
bluegene*)
cpu=powerpc
vendor=ibm
- os=cnk
+ basic_os=cnk
;;
decsystem10* | dec10*)
cpu=pdp10
vendor=dec
- os=tops10
+ basic_os=tops10
;;
decsystem20* | dec20*)
cpu=pdp10
vendor=dec
- os=tops20
+ basic_os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
@@ -703,7 +706,7 @@ case $basic_machine in
dpx2*)
cpu=m68k
vendor=bull
- os=sysv3
+ basic_os=sysv3
;;
encore | umax | mmax)
cpu=ns32k
@@ -712,7 +715,7 @@ case $basic_machine in
elxsi)
cpu=elxsi
vendor=elxsi
- os=${os:-bsd}
+ basic_os=${basic_os:-bsd}
;;
fx2800)
cpu=i860
@@ -725,7 +728,7 @@ case $basic_machine in
h3050r* | hiux*)
cpu=hppa1.1
vendor=hitachi
- os=hiuxwe2
+ basic_os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
cpu=hppa1.0
@@ -766,38 +769,38 @@ case $basic_machine in
vendor=hp
;;
i*86v32)
- cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
vendor=pc
- os=sysv32
+ basic_os=sysv32
;;
i*86v4*)
- cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
vendor=pc
- os=sysv4
+ basic_os=sysv4
;;
i*86v)
- cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
vendor=pc
- os=sysv
+ basic_os=sysv
;;
i*86sol2)
- cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
vendor=pc
- os=solaris2
+ basic_os=solaris2
;;
j90 | j90-cray)
cpu=j90
vendor=cray
- os=${os:-unicos}
+ basic_os=${basic_os:-unicos}
;;
iris | iris4d)
cpu=mips
vendor=sgi
- case $os in
+ case $basic_os in
irix*)
;;
*)
- os=irix4
+ basic_os=irix4
;;
esac
;;
@@ -808,26 +811,26 @@ case $basic_machine in
*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
cpu=m68k
vendor=atari
- os=mint
+ basic_os=mint
;;
news-3600 | risc-news)
cpu=mips
vendor=sony
- os=newsos
+ basic_os=newsos
;;
next | m*-next)
cpu=m68k
vendor=next
- case $os in
+ case $basic_os in
openstep*)
;;
nextstep*)
;;
ns2*)
- os=nextstep2
+ basic_os=nextstep2
;;
*)
- os=nextstep3
+ basic_os=nextstep3
;;
esac
;;
@@ -838,12 +841,12 @@ case $basic_machine in
op50n-* | op60c-*)
cpu=hppa1.1
vendor=oki
- os=proelf
+ basic_os=proelf
;;
pa-hitachi)
cpu=hppa1.1
vendor=hitachi
- os=hiuxwe2
+ basic_os=hiuxwe2
;;
pbd)
cpu=sparc
@@ -880,12 +883,12 @@ case $basic_machine in
sde)
cpu=mipsisa32
vendor=sde
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
simso-wrs)
cpu=sparclite
vendor=wrs
- os=vxworks
+ basic_os=vxworks
;;
tower | tower-32)
cpu=m68k
@@ -902,7 +905,7 @@ case $basic_machine in
w89k-*)
cpu=hppa1.1
vendor=winbond
- os=proelf
+ basic_os=proelf
;;
none)
cpu=none
@@ -914,7 +917,7 @@ case $basic_machine in
;;
leon-*|leon[3-9]-*)
cpu=sparc
- vendor=`echo "$basic_machine" | sed 's/-.*//'`
+ vendor=$(echo "$basic_machine" | sed 's/-.*//')
;;
*-*)
@@ -955,11 +958,11 @@ case $cpu-$vendor in
# some cases the only manufacturer, in others, it is the most popular.
craynv-unknown)
vendor=cray
- os=${os:-unicosmp}
+ basic_os=${basic_os:-unicosmp}
;;
c90-unknown | c90-cray)
vendor=cray
- os=${os:-unicos}
+ basic_os=${Basic_os:-unicos}
;;
fx80-unknown)
vendor=alliant
@@ -1003,7 +1006,7 @@ case $cpu-$vendor in
dpx20-unknown | dpx20-bull)
cpu=rs6000
vendor=bull
- os=${os:-bosx}
+ basic_os=${basic_os:-bosx}
;;
# Here we normalize CPU types irrespective of the vendor
@@ -1012,7 +1015,7 @@ case $cpu-$vendor in
;;
blackfin-*)
cpu=bfin
- os=linux
+ basic_os=linux
;;
c54x-*)
cpu=tic54x
@@ -1025,7 +1028,7 @@ case $cpu-$vendor in
;;
e500v[12]-*)
cpu=powerpc
- os=$os"spe"
+ basic_os=${basic_os}"spe"
;;
mips3*-*)
cpu=mips64
@@ -1035,7 +1038,7 @@ case $cpu-$vendor in
;;
m68knommu-*)
cpu=m68k
- os=linux
+ basic_os=linux
;;
m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
cpu=s12z
@@ -1045,7 +1048,7 @@ case $cpu-$vendor in
;;
parisc-*)
cpu=hppa
- os=linux
+ basic_os=linux
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
@@ -1081,7 +1084,7 @@ case $cpu-$vendor in
cpu=mipsisa64sb1el
;;
sh5e[lb]-*)
- cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+ cpu=$(echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/')
;;
spur-*)
cpu=spur
@@ -1099,13 +1102,16 @@ case $cpu-$vendor in
cpu=x86_64
;;
xscale-* | xscalee[bl]-*)
- cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+ cpu=$(echo "$cpu" | sed 's/^xscale/arm/')
+ ;;
+ arm64-*)
+ cpu=aarch64
;;
# Recognize the canonical CPU Types that limit and/or modify the
# company names they are paired with.
cr16-*)
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
crisv32-* | etraxfs*-*)
cpu=crisv32
@@ -1116,7 +1122,7 @@ case $cpu-$vendor in
vendor=axis
;;
crx-*)
- os=${os:-elf}
+ basic_os=${basic_os:-elf}
;;
neo-tandem)
cpu=neo
@@ -1138,16 +1144,12 @@ case $cpu-$vendor in
cpu=nsx
vendor=tandem
;;
- s390-*)
- cpu=s390
- vendor=ibm
- ;;
- s390x-*)
- cpu=s390x
- vendor=ibm
+ mipsallegrexel-sony)
+ cpu=mipsallegrexel
+ vendor=sony
;;
tile*-*)
- os=${os:-linux-gnu}
+ basic_os=${basic_os:-linux-gnu}
;;
*)
@@ -1163,8 +1165,8 @@ case $cpu-$vendor in
| alphapca5[67] | alpha64pca5[67] \
| am33_2.0 \
| amdgcn \
- | arc | arceb \
- | arm | arm[lb]e | arme[lb] | armv* \
+ | arc | arceb | arc64 \
+ | arm | arm[lb]e | arme[lb] | armv* \
| avr | avr32 \
| asmjs \
| ba \
@@ -1183,6 +1185,7 @@ case $cpu-$vendor in
| k1om \
| le32 | le64 \
| lm32 \
+ | loongarch32 | loongarch64 | loongarchx32 \
| m32c | m32r | m32rle \
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
@@ -1201,9 +1204,13 @@ case $cpu-$vendor in
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r3 | mipsisa32r3el \
+ | mipsisa32r5 | mipsisa32r5el \
| mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r3 | mipsisa64r3el \
+ | mipsisa64r5 | mipsisa64r5el \
| mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
@@ -1227,8 +1234,9 @@ case $cpu-$vendor in
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
| pru \
| pyramid \
- | riscv | riscv32 | riscv64 \
+ | riscv | riscv32 | riscv32be | riscv64 | riscv64be \
| rl78 | romp | rs6000 | rx \
+ | s390 | s390x \
| score \
| sh | shl \
| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
@@ -1238,6 +1246,7 @@ case $cpu-$vendor in
| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
| spu \
| tahoe \
+ | thumbv7* \
| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
| tron \
| ubicom32 \
@@ -1275,8 +1284,47 @@ esac
# Decode manufacturer-specific aliases for certain operating systems.
-if [ x$os != x ]
+if test x$basic_os != x
then
+
+# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+ gnu/linux*)
+ kernel=linux
+ os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|')
+ ;;
+ os2-emx)
+ kernel=os2
+ os=$(echo $basic_os | sed -e 's|os2-emx|emx|')
+ ;;
+ nto-qnx*)
+ kernel=nto
+ os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|')
+ ;;
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+ ;;
+ # Default OS when just kernel was specified
+ nto*)
+ kernel=nto
+ os=$(echo $basic_os | sed -e 's|nto|qnx|')
+ ;;
+ linux*)
+ kernel=linux
+ os=$(echo $basic_os | sed -e 's|linux|gnu|')
+ ;;
+ *)
+ kernel=
+ os=$basic_os
+ ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
case $os in
# First match some system type aliases that might get confused
# with valid system types.
@@ -1288,7 +1336,7 @@ case $os in
os=cnk
;;
solaris1 | solaris1.*)
- os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ os=$(echo $os | sed -e 's|solaris1|sunos4|')
;;
solaris)
os=solaris2
@@ -1296,9 +1344,6 @@ case $os in
unixware*)
os=sysv4.2uw
;;
- gnu/linux*)
- os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
- ;;
# es1800 is here to avoid being matched by es* (a different OS)
es1800*)
os=ose
@@ -1320,12 +1365,9 @@ case $os in
os=sco3.2v4
;;
sco3.2.[4-9]*)
- os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ os=$(echo $os | sed -e 's/sco3.2./sco3.2v/')
;;
- sco3.2v[4-9]* | sco5v6*)
- # Don't forget version if it is 3.2v4 or newer.
- ;;
- scout)
+ sco*v* | scout)
# Don't match below
;;
sco*)
@@ -1334,79 +1376,26 @@ case $os in
psos*)
os=psos
;;
- # Now accept the basic system types.
- # The portable systems comes first.
- # Each alternative MUST end in a * to match a version number.
- # sysv* is not here because it comes later, after sysvr4.
- gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
- | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
- | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
- | sym* | kopensolaris* | plan9* \
- | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
- | aos* | aros* | cloudabi* | sortix* \
- | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
- | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
- | knetbsd* | mirbsd* | netbsd* \
- | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
- | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
- | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
- | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
- | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
- | chorusrdb* | cegcc* | glidix* \
- | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
- | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
- | linux-newlib* | linux-musl* | linux-uclibc* \
- | uxpv* | beos* | mpeix* | udk* | moxiebox* \
- | interix* | uwin* | mks* | rhapsody* | darwin* \
- | openstep* | oskit* | conix* | pw32* | nonstopux* \
- | storm-chaos* | tops10* | tenex* | tops20* | its* \
- | os2* | vos* | palmos* | uclinux* | nucleus* \
- | morphos* | superux* | rtmk* | windiss* \
- | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
- | skyos* | haiku* | rdos* | toppers* | drops* | es* \
- | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
- | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
- | nsk* | powerunix)
- # Remember, each alternative MUST END IN *, to match a version number.
- ;;
qnx*)
- case $cpu in
- x86 | i*86)
- ;;
- *)
- os=nto-$os
- ;;
- esac
+ os=qnx
;;
hiux*)
os=hiuxwe2
;;
- nto-qnx*)
- ;;
- nto*)
- os=`echo $os | sed -e 's|nto|nto-qnx|'`
- ;;
- sim | xray | os68k* | v88r* \
- | windows* | osx | abug | netware* | os9* \
- | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
- ;;
- linux-dietlibc)
- os=linux-dietlibc
- ;;
- linux*)
- os=`echo $os | sed -e 's|linux|linux-gnu|'`
- ;;
lynx*178)
os=lynxos178
;;
lynx*5)
os=lynxos5
;;
+ lynxos*)
+ # don't get caught up in next wildcard
+ ;;
lynx*)
os=lynxos
;;
- mac*)
- os=`echo "$os" | sed -e 's|mac|macos|'`
+ mac[0-9]*)
+ os=$(echo "$os" | sed -e 's|mac|macos|')
;;
opened*)
os=openedition
@@ -1415,10 +1404,10 @@ case $os in
os=os400
;;
sunos5*)
- os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ os=$(echo "$os" | sed -e 's|sunos5|solaris2|')
;;
sunos6*)
- os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ os=$(echo "$os" | sed -e 's|sunos6|solaris3|')
;;
wince*)
os=wince
@@ -1452,7 +1441,7 @@ case $os in
;;
# Preserve the version number of sinix5.
sinix5.*)
- os=`echo $os | sed -e 's|sinix|sysv|'`
+ os=$(echo $os | sed -e 's|sinix|sysv|')
;;
sinix*)
os=sysv4
@@ -1475,18 +1464,12 @@ case $os in
sysvr4)
os=sysv4
;;
- # This must come after sysvr4.
- sysv*)
- ;;
ose*)
os=ose
;;
*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
os=mint
;;
- zvmoe)
- os=zvmoe
- ;;
dicos*)
os=dicos
;;
@@ -1503,19 +1486,11 @@ case $os in
;;
esac
;;
- nacl*)
- ;;
- ios)
- ;;
- none)
- ;;
- *-eabi)
- ;;
*)
- echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
- exit 1
+ # No normalization, but not necessarily accepted, that comes below.
;;
esac
+
else
# Here we handle the default operating systems that come with various machines.
@@ -1528,6 +1503,7 @@ else
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
+kernel=
case $cpu-$vendor in
score-*)
os=elf
@@ -1539,7 +1515,8 @@ case $cpu-$vendor in
os=riscix1.2
;;
arm*-rebel)
- os=linux
+ kernel=linux
+ os=gnu
;;
arm*-semi)
os=aout
@@ -1705,84 +1682,178 @@ case $cpu-$vendor in
os=none
;;
esac
+
fi
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+ # Sometimes we do "kernel-libc", so those need to count as OSes.
+ musl* | newlib* | uclibc*)
+ ;;
+ # Likewise for "kernel-abi"
+ eabi* | gnueabi*)
+ ;;
+ # VxWorks passes extra cpu info in the 4th filed.
+ simlinux | simwindows | spe)
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
+ | hiux* | abug | nacl* | netware* | windows* \
+ | os9* | macos* | osx* | ios* \
+ | mpw* | magic* | mmixware* | mon960* | lnews* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+ | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* | serenity* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | mint* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+ | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*)
+ ;;
+ # This one is extra strict with allowed versions
+ sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ none)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+ linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* )
+ ;;
+ uclinux-uclibc* )
+ ;;
+ -dietlibc* | -newlib* | -musl* | -uclibc* )
+ # These are just libc implementations, not actual OSes, and thus
+ # require a kernel.
+ echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+ exit 1
+ ;;
+ kfreebsd*-gnu* | kopensolaris*-gnu*)
+ ;;
+ vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+ ;;
+ nto-qnx*)
+ ;;
+ os2-emx)
+ ;;
+ *-eabi* | *-gnueabi*)
+ ;;
+ -*)
+ # Blank kernel with real OS is always fine.
+ ;;
+ *-*)
+ echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+ exit 1
+ ;;
+esac
+
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
case $vendor in
unknown)
- case $os in
- riscix*)
+ case $cpu-$os in
+ *-riscix*)
vendor=acorn
;;
- sunos*)
+ *-sunos*)
vendor=sun
;;
- cnk*|-aix*)
+ *-cnk* | *-aix*)
vendor=ibm
;;
- beos*)
+ *-beos*)
vendor=be
;;
- hpux*)
+ *-hpux*)
vendor=hp
;;
- mpeix*)
+ *-mpeix*)
vendor=hp
;;
- hiux*)
+ *-hiux*)
vendor=hitachi
;;
- unos*)
+ *-unos*)
vendor=crds
;;
- dgux*)
+ *-dgux*)
vendor=dg
;;
- luna*)
+ *-luna*)
vendor=omron
;;
- genix*)
+ *-genix*)
vendor=ns
;;
- clix*)
+ *-clix*)
vendor=intergraph
;;
- mvs* | opened*)
+ *-mvs* | *-opened*)
+ vendor=ibm
+ ;;
+ *-os400*)
vendor=ibm
;;
- os400*)
+ s390-* | s390x-*)
vendor=ibm
;;
- ptx*)
+ *-ptx*)
vendor=sequent
;;
- tpf*)
+ *-tpf*)
vendor=ibm
;;
- vxsim* | vxworks* | windiss*)
+ *-vxsim* | *-vxworks* | *-windiss*)
vendor=wrs
;;
- aux*)
+ *-aux*)
vendor=apple
;;
- hms*)
+ *-hms*)
vendor=hitachi
;;
- mpw* | macos*)
+ *-mpw* | *-macos*)
vendor=apple
;;
- *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
vendor=atari
;;
- vos*)
+ *-vos*)
vendor=stratus
;;
esac
;;
esac
-echo "$cpu-$vendor-$os"
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
exit
# Local variables:
diff --git a/depends/funcs.mk b/depends/funcs.mk
index c0159f0e38..34a030fab7 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -49,7 +49,7 @@ define int_get_build_id
$(eval $(1)_dependencies += $($(1)_$(host_arch)_$(host_os)_dependencies) $($(1)_$(host_os)_dependencies))
$(eval $(1)_all_dependencies:=$(call int_get_all_dependencies,$(1),$($($(1)_type)_native_toolchain) $($($(1)_type)_native_binutils) $($(1)_dependencies)))
$(foreach dep,$($(1)_all_dependencies),$(eval $(1)_build_id_deps+=$(dep)-$($(dep)_version)-$($(dep)_recipe_hash)))
-$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps) $($($(1)_type)_id_string))
+$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps) $($($(1)_type)_id))
$(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)))
final_build_id_long+=$($(package)_build_id_long)
@@ -140,7 +140,13 @@ $(1)_config_env+=CMAKE_MODULE_PATH=$($($(1)_type)_prefix)/lib/cmake
$(1)_config_env+=PATH=$(build_prefix)/bin:$(PATH)
$(1)_build_env+=PATH=$(build_prefix)/bin:$(PATH)
$(1)_stage_env+=PATH=$(build_prefix)/bin:$(PATH)
-$(1)_autoconf=./configure --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
+
+# Setting a --build type that differs from --host will explicitly enable
+# cross-compilation mode. Note that --build defaults to the output of
+# config.guess, which is what we set it too here. This also quells autoconf
+# warnings, "If you wanted to set the --build type, don't use --host.",
+# when using versions older than 2.70.
+$(1)_autoconf=./configure --build=$(BUILD) --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
ifneq ($($(1)_nm),)
$(1)_autoconf += NM="$$($(1)_nm)"
endif
diff --git a/depends/gen_id b/depends/gen_id
new file mode 100755
index 0000000000..ac69ca7ee1
--- /dev/null
+++ b/depends/gen_id
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+# Usage: env [ CC=... ] [ CXX=... ] [ AR=... ] [ RANLIB=... ] [ STRIP=... ] \
+# [ DEBUG=... ] ./build-id [ID_SALT]...
+#
+# Prints to stdout a SHA256 hash representing the current toolset, used by
+# depends/Makefile as a build id for caching purposes (detecting when the
+# toolset has changed and the cache needs to be invalidated).
+#
+# If the DEBUG environment variable is non-empty and the system has `tee`
+# available in its $PATH, the pre-image to the SHA256 hash will be printed to
+# stderr. This is to help developers debug caching issues in depends.
+
+# This script explicitly does not `set -e` because id determination is mostly
+# opportunistic: it is fine that things fail, as long as they fail consistently.
+
+# Command variables (CC/CXX/AR) which can be blank are invoked with `bash -c`,
+# because the "command not found" error message printed by shells often include
+# the line number, like so:
+#
+# ./depends/gen_id: line 43: --version: command not found
+#
+# By invoking with `bash -c`, we ensure that the line number is always 1
+
+(
+ # Redirect stderr to stdout
+ exec 2>&1
+
+ echo "BEGIN ALL"
+
+ # Include any ID salts supplied via command line
+ echo "BEGIN ID SALT"
+ echo "$@"
+ echo "END ID SALT"
+
+ # 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.
+ echo "BEGIN CC"
+ bash -c "${CC} -v"
+ bash -c "${CC} -v -E -xc -o /dev/null - < /dev/null"
+ bash -c "${CC} -v -E -xobjective-c -o /dev/null - < /dev/null"
+ echo "END CC"
+
+ echo "BEGIN CXX"
+ bash -c "${CXX} -v"
+ bash -c "${CXX} -v -E -xc++ -o /dev/null - < /dev/null"
+ bash -c "${CXX} -v -E -xobjective-c++ -o /dev/null - < /dev/null"
+ echo "END CXX"
+
+ echo "BEGIN AR"
+ bash -c "${AR} --version"
+ env | grep '^AR_'
+ echo "ZERO_AR_DATE=${ZERO_AR_DATE}"
+ echo "END AR"
+
+ echo "BEGIN RANLIB"
+ bash -c "${RANLIB} --version"
+ env | grep '^RANLIB_'
+ echo "END RANLIB"
+
+ echo "BEGIN STRIP"
+ bash -c "${STRIP} --version"
+ env | grep '^STRIP_'
+ echo "END STRIP"
+
+ echo "END ALL"
+) | if [ -n "$DEBUG" ] && command -v tee > /dev/null 2>&1; then
+ # When debugging and `tee` is available, output the preimage to stderr
+ # in addition to passing through stdin to stdout
+ tee >(cat 1>&2)
+ else
+ # Otherwise, passthrough stdin to stdout
+ cat
+ fi | ${SHA256SUM} - | cut -d' ' -f1
diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk
index dd71697f0f..5a7ae2df9a 100644
--- a/depends/hosts/darwin.mk
+++ b/depends/hosts/darwin.mk
@@ -1,8 +1,8 @@
OSX_MIN_VERSION=10.14
-OSX_SDK_VERSION=10.15.1
-XCODE_VERSION=11.3.1
-XCODE_BUILD_ID=11C505
-LD64_VERSION=530
+OSX_SDK_VERSION=10.15.6
+XCODE_VERSION=12.1
+XCODE_BUILD_ID=12A7403
+LD64_VERSION=609
OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with-libcxx-headers
@@ -60,16 +60,11 @@ $(foreach TOOL,$(cctools_TOOLS),$(eval darwin_$(TOOL) = $$(build_prefix)/bin/$$(
# Explicitly point to our binaries (e.g. cctools) so that they are
# ensured to be found and preferred over other possibilities.
#
-# -stdlib=libc++ -nostdinc++ -Xclang -cxx-isystem$(OSX_SDK)/usr/include/c++/v1
+# -stdlib=libc++ -stdlib++-isystem$(OSX_SDK)/usr/include/c++/v1
#
# Forces clang to use the libc++ headers from our SDK and completely
# forget about the libc++ headers from the standard directories
#
-# TODO: Once we start requiring a clang version that has the
-# -stdlib++-isystem<directory> flag first introduced here:
-# https://reviews.llvm.org/D64089, we should use that instead. Read the
-# differential summary there for more details.
-#
# -Xclang -*system<path_a> \
# -Xclang -*system<path_b> \
# -Xclang -*system<path_c> ...
@@ -100,7 +95,7 @@ darwin_CC=env -u C_INCLUDE_PATH -u CPLUS_INCLUDE_PATH \
-u LIBRARY_PATH \
$(clang_prog) --target=$(host) -mmacosx-version-min=$(OSX_MIN_VERSION) \
-B$(build_prefix)/bin -mlinker-version=$(LD64_VERSION) \
- --sysroot=$(OSX_SDK) \
+ -isysroot$(OSX_SDK) \
-Xclang -internal-externc-isystem$(clang_resource_dir)/include \
-Xclang -internal-externc-isystem$(OSX_SDK)/usr/include
darwin_CXX=env -u C_INCLUDE_PATH -u CPLUS_INCLUDE_PATH \
@@ -108,9 +103,9 @@ darwin_CXX=env -u C_INCLUDE_PATH -u CPLUS_INCLUDE_PATH \
-u LIBRARY_PATH \
$(clangxx_prog) --target=$(host) -mmacosx-version-min=$(OSX_MIN_VERSION) \
-B$(build_prefix)/bin -mlinker-version=$(LD64_VERSION) \
- --sysroot=$(OSX_SDK) \
- -stdlib=libc++ -nostdinc++ \
- -Xclang -cxx-isystem$(OSX_SDK)/usr/include/c++/v1 \
+ -isysroot$(OSX_SDK) \
+ -stdlib=libc++ \
+ -stdlib++-isystem$(OSX_SDK)/usr/include/c++/v1 \
-Xclang -internal-externc-isystem$(clang_resource_dir)/include \
-Xclang -internal-externc-isystem$(OSX_SDK)/usr/include
diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk
index 8ab448ce5f..07da752492 100644
--- a/depends/hosts/linux.mk
+++ b/depends/hosts/linux.mk
@@ -7,7 +7,7 @@ linux_release_CXXFLAGS=$(linux_release_CFLAGS)
linux_debug_CFLAGS=-O1
linux_debug_CXXFLAGS=$(linux_debug_CFLAGS)
-linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
+linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -D_LIBCPP_DEBUG=1
ifeq (86,$(findstring 86,$(build_arch)))
i686_linux_CC=gcc -m32
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index 29a3efdfe6..f879d176f5 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -1,6 +1,6 @@
package=boost
$(package)_version=1_71_0
-$(package)_download_path=https://dl.bintray.com/boostorg/release/$(subst _,.,$($(package)_version))/source/
+$(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/
$(package)_file_name=boost_$($(package)_version).tar.bz2
$(package)_sha256_hash=d73a8da01e8bf8c7eda40b4c84915071a8c8a0df4a6734537ddde4a8580524ee
$(package)_dependencies=native_b2
@@ -26,6 +26,7 @@ $(package)_config_libraries=filesystem,system,test
$(package)_cxxflags=-std=c++17 -fvisibility=hidden
$(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_android=-fPIC
+$(package)_cxxflags_x86_64_darwin=-fcf-protection=full
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk
index 1cd5a1749a..dad317193c 100644
--- a/depends/packages/libevent.mk
+++ b/depends/packages/libevent.mk
@@ -1,14 +1,8 @@
package=libevent
-$(package)_version=2.1.11-stable
-$(package)_download_path=https://github.com/libevent/libevent/archive/
-$(package)_file_name=release-$($(package)_version).tar.gz
-$(package)_sha256_hash=229393ab2bf0dc94694f21836846b424f3532585bac3468738b7bf752c03901e
-$(package)_patches=0001-fix-windows-getaddrinfo.patch
-
-define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/0001-fix-windows-getaddrinfo.patch && \
- ./autogen.sh
-endef
+$(package)_version=2.1.12-stable
+$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-$($(package)_version)/
+$(package)_file_name=$(package)-$($(package)_version).tar.gz
+$(package)_sha256_hash=92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb
# When building for Windows, we set _WIN32_WINNT to target the same Windows
# version as we do in configure. Due to quirks in libevents build system, this
diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk
index c789dab74c..885207fce9 100644
--- a/depends/packages/native_cctools.mk
+++ b/depends/packages/native_cctools.mk
@@ -1,10 +1,9 @@
package=native_cctools
-$(package)_version=55562e4073dea0fbfd0b20e0bf69ffe6390c7f97
+$(package)_version=2ef2e931cf641547eb8a68cfebde61003587c9fd
$(package)_download_path=https://github.com/tpoechtrager/cctools-port/archive
$(package)_file_name=$($(package)_version).tar.gz
-$(package)_sha256_hash=e51995a843533a3dac155dd0c71362dd471597a2d23f13dff194c6285362f875
+$(package)_sha256_hash=6b73269efdf5c58a070e7357b66ee760501388549d6a12b423723f45888b074b
$(package)_build_subdir=cctools
-$(package)_patches=ld64_disable_threading.patch
$(package)_dependencies=native_libtapi
define $(package)_set_vars
@@ -17,10 +16,6 @@ define $(package)_set_vars
$(package)_cxx=$(clangxx_prog)
endef
-define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/ld64_disable_threading.patch
-endef
-
define $(package)_config_cmds
$($(package)_autoconf)
endef
diff --git a/depends/packages/native_clang.mk b/depends/packages/native_clang.mk
index 741ddacf09..36adeb196d 100644
--- a/depends/packages/native_clang.mk
+++ b/depends/packages/native_clang.mk
@@ -1,9 +1,9 @@
package=native_clang
-$(package)_version=8.0.0
-$(package)_download_path=https://releases.llvm.org/$($(package)_version)
-$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz
-$(package)_file_name=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz
-$(package)_sha256_hash=9ef854b71949f825362a119bf2597f744836cb571131ae6b721cd102ffea8cd0
+$(package)_version=10.0.1
+$(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
+$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+$(package)_file_name=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+$(package)_sha256_hash=48b83ef827ac2c213d5b64f5ad7ed082c8bcb712b46644e0dc5045c6f462c231
define $(package)_preprocess_cmds
rm -f $($(package)_extract_dir)/lib/libc++abi.so*
diff --git a/depends/packages/native_libdmg-hfsplus.mk b/depends/packages/native_libdmg-hfsplus.mk
index 035b767188..c7c8adef41 100644
--- a/depends/packages/native_libdmg-hfsplus.mk
+++ b/depends/packages/native_libdmg-hfsplus.mk
@@ -12,7 +12,7 @@ define $(package)_preprocess_cmds
endef
define $(package)_config_cmds
- $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -Wl,--build-id=none" ..
+ $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -Wl,--build-id=none" -DCMAKE_SKIP_RPATH="ON" -DCMAKE_EXE_LINKER_FLAGS="-static" -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" ..
endef
define $(package)_build_cmds
diff --git a/depends/packages/native_libmultiprocess.mk b/depends/packages/native_libmultiprocess.mk
index c50fdc3f6b..14653ce9fb 100644
--- a/depends/packages/native_libmultiprocess.mk
+++ b/depends/packages/native_libmultiprocess.mk
@@ -1,8 +1,8 @@
package=native_libmultiprocess
-$(package)_version=5741d750a04e644a03336090d8979c6d033e32c0
+$(package)_version=d576d975debdc9090bd2582f83f49c76c0061698
$(package)_download_path=https://github.com/chaincodelabs/libmultiprocess/archive
$(package)_file_name=$($(package)_version).tar.gz
-$(package)_sha256_hash=ac848db49a6ed53e423c62d54bd87f1f08cbb0326254a8667e10bbfe5bf032a4
+$(package)_sha256_hash=9f8b055c8bba755dc32fe799b67c20b91e7b13e67cadafbc54c0f1def057a370
$(package)_dependencies=native_capnp
define $(package)_config_cmds
diff --git a/depends/packages/native_libtapi.mk b/depends/packages/native_libtapi.mk
index d7ac4156a7..60b898da5f 100644
--- a/depends/packages/native_libtapi.mk
+++ b/depends/packages/native_libtapi.mk
@@ -1,9 +1,9 @@
package=native_libtapi
-$(package)_version=3efb201881e7a76a21e0554906cf306432539cef
+$(package)_version=664b8414f89612f2dfd35a9b679c345aa5389026
$(package)_download_path=https://github.com/tpoechtrager/apple-libtapi/archive
$(package)_download_file=$($(package)_version).tar.gz
$(package)_file_name=$($(package)_version).tar.gz
-$(package)_sha256_hash=380c1ca37cfa04a8699d0887a8d3ee1ad27f3d08baba78887c73b09485c0fbd3
+$(package)_sha256_hash=62e419c12d1c9fad67cc1cd523132bc00db050998337c734c15bc8d73cc02b61
ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),)
$(package)_dependencies=native_clang
diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk
index 5fe027fb8a..783f87ca7c 100644
--- a/depends/packages/native_mac_alias.mk
+++ b/depends/packages/native_mac_alias.mk
@@ -1,8 +1,8 @@
package=native_mac_alias
-$(package)_version=2.1.1
+$(package)_version=2.2.0
$(package)_download_path=https://github.com/al45tair/mac_alias/archive/
$(package)_file_name=v$($(package)_version).tar.gz
-$(package)_sha256_hash=c0ffceee14f7d04a6eb323fb7b8217dc3f373b346198d2ca42300a8362db7efa
+$(package)_sha256_hash=421e6d7586d1f155c7db3e7da01ca0dacc9649a509a253ad7077b70174426499
$(package)_install_libdir=$(build_prefix)/lib/python3/dist-packages
define $(package)_build_cmds
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index ee6796e2ad..52df26eb50 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -1,22 +1,22 @@
PACKAGE=qt
-$(package)_version=5.12.10
+$(package)_version=5.12.11
$(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=8088f174e6d28e779516c083b6087b6a9e3c8322b4bc161fd1b54195e3c86940
+$(package)_sha256_hash=1c1b4e33137ca77881074c140d54c3c9747e845a31338cfe8680f171f0bc3a39
$(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_no_printer.patch no-xlib.patch
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch
-$(package)_patches+= fix_qpainter_non_determinism.patch fix_lib_paths.patch fix_android_pch.patch
-$(package)_patches+= fix_bigsur_drawing.patch
+$(package)_patches+= fix_lib_paths.patch fix_android_pch.patch
+$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
-$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de
+$(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce9df06b6dee6c814164b7c3
$(package)_qttools_file_name=qttools-$($(package)_suffix)
-$(package)_qttools_sha256_hash=b0cfa6e7aac41b7c61fc59acc04843d7a98f9e1840370611751bcfc1834a636c
+$(package)_qttools_sha256_hash=98b2aaca230458f65996f3534fd471d2ffd038dd58ac997c0589c06dc2385b4f
$(package)_extra_sources = $($(package)_qttranslations_file_name)
$(package)_extra_sources += $($(package)_qttools_file_name)
@@ -126,7 +126,7 @@ $(package)_config_opts_darwin += -device-option XCODE_VERSION=$(XCODE_VERSION)
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_aarch64_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64
$(package)_config_opts_linux = -qt-xcb
$(package)_config_opts_linux += -no-xcb-xlib
@@ -148,6 +148,9 @@ $(package)_config_opts_s390x_linux = -platform linux-g++ -xplatform bitcoin-linu
$(package)_config_opts_mingw32 = -no-opengl
$(package)_config_opts_mingw32 += -no-dbus
$(package)_config_opts_mingw32 += -xplatform win32-g++
+$(package)_config_opts_mingw32 += "QMAKE_CFLAGS = '$($(package)_cflags) $($(package)_cppflags)'"
+$(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cflags) $($(package)_cppflags)'"
+$(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'"
$(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-"
$(package)_config_opts_mingw32 += -pch
@@ -172,9 +175,6 @@ $(package)_config_opts_aarch64_android += -android-arch arm64-v8a
$(package)_config_opts_armv7a_android += -android-arch armeabi-v7a
$(package)_config_opts_x86_64_android += -android-arch x86_64
$(package)_config_opts_i686_android += -android-arch i686
-
-$(package)_build_env = QT_RCC_TEST=1
-$(package)_build_env += QT_RCC_SOURCE_DATE_OVERRIDE=1
endef
define $(package)_fetch_cmds
@@ -228,10 +228,9 @@ define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_android_pch.patch && \
patch -p1 -i $($(package)_patch_dir)/no-xlib.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 && \
- patch -p1 -i $($(package)_patch_dir)/fix_bigsur_drawing.patch && \
+ patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.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/ &&\
@@ -241,9 +240,6 @@ define $(package)_preprocess_cmds
echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
- 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 = \$$$$\$$$${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
@@ -260,13 +256,15 @@ define $(package)_config_cmds
qtbase/bin/qmake -o qttranslations/Makefile qttranslations/qttranslations.pro && \
qtbase/bin/qmake -o qttranslations/translations/Makefile qttranslations/translations/translations.pro && \
qtbase/bin/qmake -o qttools/src/linguist/lrelease/Makefile qttools/src/linguist/lrelease/lrelease.pro && \
- qtbase/bin/qmake -o qttools/src/linguist/lupdate/Makefile qttools/src/linguist/lupdate/lupdate.pro
+ qtbase/bin/qmake -o qttools/src/linguist/lupdate/Makefile qttools/src/linguist/lupdate/lupdate.pro && \
+ qtbase/bin/qmake -o qttools/src/linguist/lconvert/Makefile qttools/src/linguist/lconvert/lconvert.pro
endef
define $(package)_build_cmds
$(MAKE) -C qtbase/src $(addprefix sub-,$($(package)_qt_libs)) && \
$(MAKE) -C qttools/src/linguist/lrelease && \
$(MAKE) -C qttools/src/linguist/lupdate && \
+ $(MAKE) -C qttools/src/linguist/lconvert && \
$(MAKE) -C qttranslations
endef
@@ -274,6 +272,7 @@ define $(package)_stage_cmds
$(MAKE) -C qtbase/src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && \
$(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \
$(MAKE) -C qttools/src/linguist/lupdate INSTALL_ROOT=$($(package)_staging_dir) install_target && \
+ $(MAKE) -C qttools/src/linguist/lconvert INSTALL_ROOT=$($(package)_staging_dir) install_target && \
$(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets
endef
diff --git a/depends/patches/libevent/0001-fix-windows-getaddrinfo.patch b/depends/patches/libevent/0001-fix-windows-getaddrinfo.patch
deleted file mode 100644
index a98cd90bd5..0000000000
--- a/depends/patches/libevent/0001-fix-windows-getaddrinfo.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-diff -ur libevent-2.1.8-stable.orig/configure.ac libevent-2.1.8-stable/configure.ac
---- libevent-2.1.8-stable.orig/configure.ac 2017-01-29 17:51:00.000000000 +0000
-+++ libevent-2.1.8-stable/configure.ac 2020-03-07 01:11:16.311335005 +0000
-@@ -389,6 +389,10 @@
- #ifdef HAVE_NETDB_H
- #include <netdb.h>
- #endif
-+#ifdef _WIN32
-+#include <winsock2.h>
-+#include <ws2tcpip.h>
-+#endif
- ]],
- [[
- getaddrinfo;
-Only in libevent-2.1.8-stable: configure.ac~
diff --git a/depends/patches/native_cctools/ld64_disable_threading.patch b/depends/patches/native_cctools/ld64_disable_threading.patch
deleted file mode 100644
index 2de6874cd4..0000000000
--- a/depends/patches/native_cctools/ld64_disable_threading.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-commit 584668415039adeed073decee7e04de28248afd3
-Author: fanquake <fanquake@gmail.com>
-Date: Tue Aug 18 01:20:24 2020 +0000
-
- Disable threading to fix non-determinism
-
- A bug in the file parser can cause dependencies to be calculated
- differently based on which files have already been parsed. This is more
- likely to occur on systems with more CPUs.
-
- Just disable threading for now. There is no noticeable slowdown.
-
- See #9891.
-
-diff --git a/cctools/ld64/src/ld/InputFiles.h b/cctools/ld64/src/ld/InputFiles.h
-index ef9c756..90a70b6 100644
---- a/cctools/ld64/src/ld/InputFiles.h
-+++ b/cctools/ld64/src/ld/InputFiles.h
-@@ -25,7 +25,6 @@
- #ifndef __INPUT_FILES_H__
- #define __INPUT_FILES_H__
-
--#define HAVE_PTHREADS 1
-
- #include <stdlib.h>
- #include <sys/types.h>
diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch
index f891da6ddf..a186aeb8f6 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
-@@ -897,6 +897,14 @@
+@@ -898,6 +898,14 @@
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}
diff --git a/depends/patches/qt/fix_bigsur_drawing.patch b/depends/patches/qt/fix_bigsur_drawing.patch
deleted file mode 100644
index 98c0c6be30..0000000000
--- a/depends/patches/qt/fix_bigsur_drawing.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-Fix GUI stuck on Big Sur
-
-See:
- - https://github.com/bitcoin-core/gui/issues/249
- - https://github.com/bitcoin/bitcoin/pull/21495
- - https://bugreports.qt.io/browse/QTBUG-87014
-
-We should be able to drop this once we are using one of the following versions:
- - Qt 5.12.11 or later, see upstream commit: c5d904639dbd690a36306e2b455610029704d821
- - Qt 5.15.3 or later, see upstream commit: 2cae34354bd41ae286258c7a6b3653b746e786ae
-
---- a/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm
-+++ b/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm
-@@ -95,8 +95,15 @@
- // by AppKit at a point where we've already set up other parts of the platform plugin
- // based on the presence of layers or not. Once we've rewritten these parts to support
- // dynamically picking up layer enablement we can let AppKit do its thing.
-- return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave
-- && QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave;
-+
-+ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur)
-+ return true; // Big Sur always enables layer-backing, regardless of SDK
-+
-+ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave
-+ && QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave)
-+ return true; // Mojave and Catalina enable layers based on the app's SDK
-+
-+ return false; // Prior versions needed explicitly enabled layer backing
- }
-
- - (BOOL)layerExplicitlyRequested
diff --git a/depends/patches/qt/fix_qpainter_non_determinism.patch b/depends/patches/qt/fix_qpainter_non_determinism.patch
deleted file mode 100644
index 44c45187c5..0000000000
--- a/depends/patches/qt/fix_qpainter_non_determinism.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-commit 2a8f7dc6ddfc414a66491522501c1574a1343ee1
-Author: Andrew Chow <achow101-github@achow101.com>
-Date: Sat Nov 21 01:11:04 2020 -0500
-
- build: Fix determinism issue when building with Clang 8
-
- When building Qt with LLVM/Clang 8 under -O3 (the default), we run into
- a determinism issue in `qt_interset_spans`. The issue has been fixed for
- LLVM/Clang 9, see
- https://github.com/llvm/llvm-project/commit/db101864bdc938deb1d63fe4f7da761bd38e5cae
- and https://reviews.llvm.org/D64601, however this fix was not backported
- to 8.x. Once LLVM/Clang 9 is used, this patch can be dropped.
-
- The particular issue appears to be an optimization done by -O3 which
- adds a temporary variable for `spans->y` in `qt_intersect_spans`. When
- it does this, sometimes it chooses to use a 32-bit movs instruction
- (movswl), and other times it chooses a 64-bit movs instruction (movswq).
- By patching `qt_intersect_spans` to always make a temporary variable for
- `spans->y`, we are able to sidestep this problem.
-
-diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/painting/qpaintengine_raster.cpp
-index 92ab6e8375..f018009e0b 100644
---- a/qtbase/src/gui/painting/qpaintengine_raster.cpp
-+++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp
-@@ -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 ) {
-+ const short spans_y = spans->y;
- if (clipSpans >= clipEnd) {
- spans = end;
- break;
- }
-- if (clipSpans->y > spans->y) {
-+ if (clipSpans->y > spans_y) {
- ++spans;
- continue;
- }
-- if (spans->y != clipSpans->y) {
-- if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
-- clipSpans = clip->m_clipLines[spans->y].spans;
-+ if (spans_y != clipSpans->y) {
-+ if (spans_y < clip->count && clip->m_clipLines[spans_y].spans)
-+ clipSpans = clip->m_clipLines[spans_y].spans;
- else
- ++clipSpans;
- continue;
- }
-- Q_ASSERT(spans->y == clipSpans->y);
-+ Q_ASSERT(spans_y == clipSpans->y);
-
- int sx1 = spans->x;
- int sx2 = sx1 + spans->len;
-@@ -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;
-- out->y = spans->y;
-+ out->y = spans_y;
- out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
- ++out;
- --available;
-
diff --git a/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch
new file mode 100644
index 0000000000..0358bea6e9
--- /dev/null
+++ b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch
@@ -0,0 +1,17 @@
+The moc executable loops through headers on CPLUS_INCLUDE_PATH and stumbles
+on the GCC internal _GLIBCXX_VISIBILITY macro. Tell it to ignore it as it is
+not supposed to be looking there to begin with.
+
+Upstream report: https://bugreports.qt.io/browse/QTBUG-83160
+
+diff --git a/qtbase/src/tools/moc/main.cpp b/qtbase/src/tools/moc/main.cpp
+--- a/qtbase/src/tools/moc/main.cpp
++++ b/qtbase/src/tools/moc/main.cpp
+@@ -188,6 +188,7 @@ int runMoc(int argc, char **argv)
+ dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__");
+ pp.macros["__attribute__"] = dummyVariadicFunctionMacro;
+ pp.macros["__declspec"] = dummyVariadicFunctionMacro;
++ pp.macros["_GLIBCXX_VISIBILITY"] = dummyVariadicFunctionMacro;
+
+ QString filename;
+ QString output;
diff --git a/doc/README.md b/doc/README.md
index f32600d009..c629c2ccfa 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -30,6 +30,7 @@ Drag Bitcoin Core to your applications folder, and then run Bitcoin Core.
* See the documentation at the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page)
for help and more information.
+* Ask for help on the [Bitcoin StackExchange](https://bitcoin.stackexchange.com)
* Ask for help on [#bitcoin](https://webchat.freenode.net/#bitcoin) on Freenode. If you don't have an IRC client, use [webchat here](https://webchat.freenode.net/#bitcoin).
* Ask for help on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [Technical Support board](https://bitcointalk.org/index.php?board=4.0).
@@ -67,7 +68,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th
### Resources
* Discuss on the [BitcoinTalk](https://bitcointalk.org/) forums, in the [Development & Technical Discussion board](https://bitcointalk.org/index.php?board=6.0).
-* Discuss project-specific development on #bitcoin-core-dev on Freenode. If you don't have an IRC client, use [webchat here](https://webchat.freenode.net/#bitcoin-core-dev).
+* Discuss project-specific development on #bitcoin-core-dev on Libera Chat. If you don't have an IRC client, use [webchat here](https://web.libera.chat/#bitcoin-core-dev).
* Discuss general Bitcoin development on #bitcoin-dev on Freenode. If you don't have an IRC client, use [webchat here](https://webchat.freenode.net/#bitcoin-dev).
### Miscellaneous
diff --git a/doc/bips.md b/doc/bips.md
index f72dbead9d..45954bcfd8 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -49,5 +49,11 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v22.0**):
* [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)).
* [`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 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)),
+ with mainnet activation as of **v0.21.1** ([PR 21377](https://github.com/bitcoin/bitcoin/pull/21377),
+ [PR 21686](https://github.com/bitcoin/bitcoin/pull/21686)).
* [`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/build-freebsd.md b/doc/build-freebsd.md
index 18ea84c579..da2ab61c2a 100644
--- a/doc/build-freebsd.md
+++ b/doc/build-freebsd.md
@@ -124,6 +124,6 @@ This explicitly enables the GUI and disables legacy wallet support. If `qt5` is
**Important**: Use `gmake` (the non-GNU `make` will exit with an error).
```bash
-gmake # use -jX here for parallelism
+gmake # use "-j N" for N parallel jobs
gmake check # Run tests if Python 3 is available
```
diff --git a/doc/build-netbsd.md b/doc/build-netbsd.md
index 47049a780e..edabd71611 100644
--- a/doc/build-netbsd.md
+++ b/doc/build-netbsd.md
@@ -76,6 +76,6 @@ Without wallet:
Build and run the tests:
```bash
-gmake # use -jX here for parallelism
+gmake # use "-j N" here for N parallel jobs
gmake check
```
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index dccd7b1335..613aea438f 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -90,7 +90,7 @@ To configure with GUI:
Build and run the tests:
```bash
-gmake # use -jX here for parallelism
+gmake # use "-j N" here for N parallel jobs
gmake check
```
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 16c6da66d5..467feff410 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -138,6 +138,14 @@ Skip if you don't intend to use the GUI.
brew install qt@5
```
+Ensure that the `qt@5` package is installed, not the `qt` package.
+If 'qt' is installed, the build process will fail.
+if installed, remove the `qt` package with the following command:
+
+``` bash
+brew uninstall qt
+```
+
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).
@@ -270,7 +278,7 @@ 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 # use "-j N" here for N parallel jobs
make check # Run tests if Python 3 is available
```
diff --git a/doc/build-unix.md b/doc/build-unix.md
index d7e0ff705d..73c0bf8779 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -20,7 +20,7 @@ To Build
```bash
./autogen.sh
./configure
-make
+make # use "-j N" for N parallel jobs
make install # optional
```
@@ -331,7 +331,7 @@ To build executables for ARM:
make HOST=arm-linux-gnueabihf NO_QT=1
cd ..
./autogen.sh
- ./configure --prefix=$PWD/depends/arm-linux-gnueabihf --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++
+ CONFIG_SITE=$PWD/depends/arm-linux-gnueabihf/share/config.site ./configure --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++
make
diff --git a/doc/build-windows.md b/doc/build-windows.md
index d1b84eef42..f88b9739de 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -81,9 +81,26 @@ The first step is to install the mingw-w64 cross-compilation tool chain:
sudo apt install g++-mingw-w64-x86-64
-Ubuntu Bionic 18.04 <sup>[1](#footnote1)</sup>:
+Next, set the default `mingw32 g++` compiler option to POSIX<sup>[1](#footnote1)</sup>:
- sudo update-alternatives --config x86_64-w64-mingw32-g++ # Set the default mingw32 g++ compiler option to posix.
+```
+sudo update-alternatives --config x86_64-w64-mingw32-g++
+```
+
+After running the above command, you should see output similar to that below.
+Choose the option that ends with `posix`.
+
+```
+There are 2 choices for the alternative x86_64-w64-mingw32-g++ (providing /usr/bin/x86_64-w64-mingw32-g++).
+
+ Selection Path Priority Status
+------------------------------------------------------------
+ 0 /usr/bin/x86_64-w64-mingw32-g++-win32 60 auto mode
+* 1 /usr/bin/x86_64-w64-mingw32-g++-posix 30 manual mode
+ 2 /usr/bin/x86_64-w64-mingw32-g++-win32 60 manual mode
+
+Press <enter> to keep the current choice[*], or type selection number:
+```
Once the toolchain is installed the build steps are common:
@@ -105,7 +122,7 @@ Build using:
cd ..
./autogen.sh
CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/
- make
+ make # use "-j N" for N parallel jobs
sudo bash -c "echo 1 > /proc/sys/fs/binfmt_misc/status" # Enable WSL support for Win32 applications.
## Depends system
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 22161856ce..9754952221 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -13,7 +13,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Android only) |
| 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 | | |
+| libevent | [2.1.12-stable](https://github.com/libevent/libevent/releases) | [2.0.21](https://github.com/bitcoin/bitcoin/pull/18676) | 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 | | | | | |
@@ -21,7 +21,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| 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.12.10](https://download.qt.io/official_releases/qt/) | [5.9.5](https://github.com/bitcoin/bitcoin/issues/20104) | No | | |
+| Qt | [5.12.11](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) |
diff --git a/doc/descriptors.md b/doc/descriptors.md
index c4fc2a66bf..e27ff87546 100644
--- a/doc/descriptors.md
+++ b/doc/descriptors.md
@@ -30,6 +30,7 @@ Output descriptors currently support:
- Pay-to-witness-pubkey-hash scripts (P2WPKH), through the `wpkh` function.
- Pay-to-script-hash scripts (P2SH), through the `sh` function.
- Pay-to-witness-script-hash scripts (P2WSH), through the `wsh` function.
+- Pay-to-taproot outputs (P2TR), through the `tr` function.
- Multisig scripts, through the `multi` function.
- Multisig scripts where the public keys are sorted lexicographically, through the `sortedmulti` function.
- Any type of supported address through the `addr` function.
@@ -54,6 +55,7 @@ Output descriptors currently support:
- `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)` describes a set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`.
- `wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default).
- `wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*))` describes a set of *1-of-2* P2WSH multisig outputs where one multisig key is the *1/0/`i`* child of the first specified xpub and the other multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). The order of public keys in the resulting witnessScripts is determined by the lexicographic order of the public keys at that index.
+- `tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{pk(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),pk(e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)})` describes a P2TR output with the `c6...` x-only pubkey as internal key, and two script paths.
## Reference
@@ -61,13 +63,14 @@ Descriptors consist of several types of expressions. The top level expression is
`SCRIPT` expressions:
- `sh(SCRIPT)` (top level only): P2SH embed the argument.
-- `wsh(SCRIPT)` (not inside another 'wsh'): P2WSH embed the argument.
+- `wsh(SCRIPT)` (top level or inside `sh` only): P2WSH embed the argument.
- `pk(KEY)` (anywhere): P2PK output for the given public key.
-- `pkh(KEY)` (anywhere): P2PKH output for the given public key (use `addr` if you only know the pubkey hash).
-- `wpkh(KEY)` (not inside `wsh`): P2WPKH output for the given compressed pubkey.
+- `pkh(KEY)` (not inside `tr`): P2PKH output for the given public key (use `addr` if you only know the pubkey hash).
+- `wpkh(KEY)` (top level or inside `sh` only): P2WPKH output for the given compressed pubkey.
- `combo(KEY)` (top level only): an alias for the collection of `pk(KEY)` and `pkh(KEY)`. If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
-- `multi(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script.
-- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script with keys sorted lexicographically in the resulting script.
+- `multi(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script.
+- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script with keys sorted lexicographically in the resulting script.
+- `tr(KEY)` or `tr(KEY,TREE)` (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths.
- `addr(ADDR)` (top level only): the script which ADDR expands to.
- `raw(HEX)` (top level only): the script whose hex encoding is HEX.
@@ -80,12 +83,17 @@ Descriptors consist of several types of expressions. The top level expression is
- Followed by the actual key, which is either:
- Hex encoded public keys (either 66 characters starting with `02` or `03` for a compressed pubkey, or 130 characters starting with `04` for an uncompressed pubkey).
- Inside `wpkh` and `wsh`, only compressed public keys are permitted.
+ - Inside `tr`, x-only pubkeys are also permitted (64 hex characters).
- [WIF](https://en.bitcoin.it/wiki/Wallet_import_format) encoded private keys may be specified instead of the corresponding public key, with the same meaning.
- `xpub` encoded extended public key or `xprv` encoded extended private key (as defined in [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)).
- Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps.
- Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
- The usage of hardened derivation steps requires providing the private key.
+`TREE` expressions:
+- any `SCRIPT` expression
+- An open brace `{`, a `TREE` expression, a comma `,`, a `TREE` expression, and a closing brace `}`
+
(Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.)
`ADDR` expressions are any type of supported address:
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 0a78cdff20..2130332eec 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -812,7 +812,7 @@ class ChainstateManager
{
public:
...
- bool ProcessNewBlock(...) EXCLUSIVE_LOCKS_REQUIRED(!::cs_main);
+ bool ProcessNewBlock(...) LOCKS_EXCLUDED(::cs_main);
...
}
diff --git a/doc/external-signer.md b/doc/external-signer.md
index 2b3b378bee..de44cdd880 100644
--- a/doc/external-signer.md
+++ b/doc/external-signer.md
@@ -46,7 +46,7 @@ Display an address on the device:
```sh
$ bitcoin-cli -rpcwallet=<wallet> getnewaddress
-$ bitcoin-cli -rpcwallet=<wallet> signerdisplayaddress <address>
+$ bitcoin-cli -rpcwallet=<wallet> walletdisplayaddress <address>
```
Replace `<address>` with the result of `getnewaddress`.
@@ -166,6 +166,6 @@ The `createwallet` RPC calls:
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>`.
+The `walletdisplayaddress` 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 a80adf30ea..353efe348d 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -53,13 +53,14 @@ Subdirectory | File(s) | Description
`indexes/txindex/` | LevelDB database | Transaction index; *optional*, used if `-txindex=1`
`indexes/blockfilter/basic/db/` | LevelDB database | Blockfilter index LevelDB database for the basic filtertype; *optional*, used if `-blockfilterindex=basic`
`indexes/blockfilter/basic/` | `fltrNNNNN.dat`<sup>[\[2\]](#note2)</sup> | Blockfilter index filters for the basic filtertype; *optional*, used if `-blockfilterindex=basic`
+`indexes/coinstats/db/` | LevelDB database | Coinstats index; *optional*, used if `-coinstatsindex=1`
`wallets/` | | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, wallets reside in the [data directory](#data-directory-location)
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
`./` | `banlist.dat` | Stores the IPs/subnets of banned nodes
`./` | `bitcoin.conf` | User-defined [configuration settings](bitcoin-conf.md) for `bitcoind` or `bitcoin-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option
`./` | `bitcoind.pid` | Stores the process ID (PID) of `bitcoind` or `bitcoin-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option
`./` | `debug.log` | Contains debug information and general logging generated by `bitcoind` or `bitcoin-qt`; can be specified by `-debuglogfile` option
-`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees and priorities required for confirmation
+`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees required for confirmation
`./` | `guisettings.ini.bak` | Backup of former [GUI settings](#gui-settings) after `-resetguisettings` option is used
`./` | `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
diff --git a/doc/fuzzing.md b/doc/fuzzing.md
index 4d8825f4c2..6fc9077e4c 100644
--- a/doc/fuzzing.md
+++ b/doc/fuzzing.md
@@ -16,7 +16,7 @@ $ FUZZ=process_message src/test/fuzz/fuzz
# abort fuzzing using ctrl-c
```
-## Fuzzing harnesses, fuzzing output and fuzzing corpora
+## Fuzzing harnesses and output
[`process_message`](https://github.com/bitcoin/bitcoin/blob/master/src/test/fuzz/process_message.cpp) is a fuzzing harness for the [`ProcessMessage(...)` function (`net_processing`)](https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp). The available fuzzing harnesses are found in [`src/test/fuzz/`](https://github.com/bitcoin/bitcoin/tree/master/src/test/fuzz).
@@ -64,6 +64,8 @@ block^@M-^?M-^?M-^?M-^?M-^?nM-^?M-^?
In this case the fuzzer managed to create a `block` message which when passed to `ProcessMessage(...)` increased coverage.
+## Fuzzing corpora
+
The project's collection of seed corpora is found in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo.
To fuzz `process_message` using the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) seed corpus:
@@ -81,6 +83,20 @@ INFO: seed corpus: files: 991 min: 1b max: 1858b total: 288291b rss: 150Mb
```
+## Reproduce a fuzzer crash reported by the CI
+
+- `cd` into the `qa-assets` directory and update it with `git pull qa-assets`
+- locate the crash case described in the CI output, e.g. `Test unit written to
+ ./crash-1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`
+- make sure to compile with all sanitizers, if they are needed (fuzzing runs
+ more slowly with sanitizers enabled, but a crash should be reproducible very
+ quickly from a crash case)
+- run the fuzzer with the case number appended to the seed corpus path:
+ `FUZZ=process_message src/test/fuzz/fuzz
+ qa-assets/fuzz_seed_corpus/process_message/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`
+
+## Submit improved coverage
+
If you find coverage increasing inputs when fuzzing you are highly encouraged to submit them for inclusion in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo.
Every single pull request submitted against the Bitcoin Core repo is automatically tested against all inputs in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Contributing new coverage increasing inputs is an easy way to help make Bitcoin Core more robust.
@@ -230,3 +246,17 @@ $ honggfuzz/honggfuzz --exit_upon_crash --quiet --timeout 4 -n 1 -Q \
-nodebuglogfile -bind=127.0.0.1:18444 -logthreadnames \
-debug
```
+
+# OSS-Fuzz
+
+Bitcoin Core participates in Google's [OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/bitcoin-core)
+program, which includes a dashboard of [publicly disclosed vulnerabilities](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=bitcoin-core).
+Generally, we try to disclose vulnerabilities as soon as possible after they
+are fixed to give users the knowledge they need to be protected. However,
+because Bitcoin is a live P2P network, and not just standalone local software,
+we might not fully disclose every issue within Google's standard
+[90-day disclosure window](https://google.github.io/oss-fuzz/getting-started/bug-disclosure-guidelines/)
+if a partial or delayed disclosure is important to protect users or the
+function of the network.
+
+OSS-Fuzz also produces [a fuzzing coverage report](https://oss-fuzz.com/coverage-report/job/libfuzzer_asan_bitcoin-core/latest).
diff --git a/doc/multiprocess.md b/doc/multiprocess.md
index 471d8561f7..e3f389a6d3 100644
--- a/doc/multiprocess.md
+++ b/doc/multiprocess.md
@@ -15,7 +15,7 @@ Specific next steps after [#10102](https://github.com/bitcoin/bitcoin/pull/10102
## Debugging
-After [#10102](https://github.com/bitcoin/bitcoin/pull/10102), the `-debug=ipc` command line option can be used to see requests and responses between processes.
+The `-debug=ipc` command line option can be used to see requests and responses between processes.
## Installation
@@ -24,7 +24,7 @@ The multiprocess feature requires [Cap'n Proto](https://capnproto.org/) and [lib
```
cd <BITCOIN_SOURCE_DIRECTORY>
make -C depends NO_QT=1 MULTIPROCESS=1
-./configure --prefix=$PWD/depends/x86_64-pc-linux-gnu
+CONFIG_SITE=$PWD/depends/x86_64-pc-linux-gnu/share/config.site ./configure
make
src/bitcoin-node -regtest -printtoconsole -debug=ipc
BITCOIND=bitcoin-node test/functional/test_runner.py
@@ -33,3 +33,40 @@ BITCOIND=bitcoin-node test/functional/test_runner.py
The configure script will pick up settings and library locations from the depends directory, so there is no need to pass `--enable-multiprocess` as a separate flag when using the depends system (it's controlled by the `MULTIPROCESS=1` option).
Alternately, you can install [Cap'n Proto](https://capnproto.org/) and [libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) packages on your system, and just run `./configure --enable-multiprocess` without using the depends system. The configure script will be able to locate the installed packages via [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/). See [Installation](https://github.com/chaincodelabs/libmultiprocess#installation) section of the libmultiprocess readme for install steps. See [build-unix.md](build-unix.md) and [build-osx.md](build-osx.md) for information about installing dependencies in general.
+
+## IPC implementation details
+
+Cross process Node, Wallet, and Chain interfaces are defined in
+[`src/interfaces/`](../src/interfaces/). These are C++ classes which follow
+[conventions](developer-notes.md#internal-interface-guidelines), like passing
+serializable arguments so they can be called from different processes, and
+making methods pure virtual so they can have proxy implementations that forward
+calls between processes.
+
+When Wallet, Node, and Chain code is running in the same process, calling any
+interface method invokes the implementation directly. When code is running in
+different processes, calling an interface method invokes a proxy interface
+implementation that communicates with a remote process and invokes the real
+implementation in the remote process. The
+[libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) code
+generation tool internally generates proxy client classes and proxy server
+classes for this purpose that are thin wrappers around Cap'n Proto
+[client](https://capnproto.org/cxxrpc.html#clients) and
+[server](https://capnproto.org/cxxrpc.html#servers) classes, which handle the
+actual serialization and socket communication.
+
+As much as possible, calls between processes are meant to work the same as
+calls within a single process without adding limitations or requiring extra
+implementation effort. Processes communicate with each other by calling regular
+[C++ interface methods](../src/interfaces/README.md). Method arguments and
+return values are automatically serialized and sent between processes. Object
+references and `std::function` arguments are automatically tracked and mapped
+to allow invoked code to call back into invoking code at any time, and there is
+a 1:1 threading model where any thread invoking a method in another process has
+a corresponding thread in the invoked process responsible for executing all
+method calls from the source thread, without blocking I/O or holding up another
+call, and using the same thread local variables, locks, and callbacks between
+calls. The forwarding, tracking, and threading is implemented inside the
+[libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) library
+which has the design goal of making calls between processes look like calls in
+the same process to the extent possible.
diff --git a/doc/reduce-memory.md b/doc/reduce-memory.md
index 6e7a578ecc..296b172bde 100644
--- a/doc/reduce-memory.md
+++ b/doc/reduce-memory.md
@@ -24,9 +24,13 @@ The size of some in-memory caches can be reduced. As caches trade off memory usa
## Number of peers
-- `-maxconnections=<n>` - the maximum number of connections, this defaults to 125. Each active connection takes up some
- memory. This option applies only if incoming connections are enabled, otherwise the number of connections will never
- be more than 10. Of the 10 outbound peers, there can be 8 full-relay connections and 2 block-relay-only ones.
+- `-maxconnections=<n>` - the maximum number of connections, which defaults to 125. Each active connection takes up some
+ memory. This option applies only if inbound connections are enabled; otherwise, the number of connections will not
+ be more than 11. Of the 11 outbound peers, there can be 8 full-relay connections, 2 block-relay-only ones,
+ and occasionally 1 short-lived feeler or extra outbound block-relay-only connection.
+
+- These limits do not apply to connections added manually with the `-addnode` configuration option or
+ the `addnode` RPC, which have a separate limit of 8 connections.
## Thread configuration
diff --git a/doc/release-notes-20833.md b/doc/release-notes-20833.md
new file mode 100644
index 0000000000..9a02bbd275
--- /dev/null
+++ b/doc/release-notes-20833.md
@@ -0,0 +1,12 @@
+Updated RPCs
+------------
+
+- The `testmempoolaccept` RPC now accepts multiple transactions (still experimental at the moment,
+ API may be unstable). This is intended for testing transaction packages with dependency
+ relationships; it is not recommended for batch-validating independent transactions. In addition to
+ mempool policy, package policies apply: the list cannot contain more than 25 transactions or have a
+ total size exceeding 101K virtual bytes, and cannot conflict with (spend the same inputs as) each other or
+ the mempool, even if it would be a valid BIP125 replace-by-fee. There are some known limitations to
+ the accuracy of the test accept: it's possible for `testmempoolaccept` to return "allowed"=True for a
+ group of transactions, but "too-long-mempool-chain" if they are actually submitted. (#20833)
+
diff --git a/doc/release-notes-20867.md b/doc/release-notes-20867.md
new file mode 100644
index 0000000000..60eed6838f
--- /dev/null
+++ b/doc/release-notes-20867.md
@@ -0,0 +1,11 @@
+Wallet
+------
+
+- We now support up to 20 keys in `multi()` and `sortedmulti()` descriptors
+ under `wsh()`. (#20867)
+
+Updated RPCs
+------------
+
+- `addmultisigaddress` and `createmultisig` now support up to 20 keys for
+ Segwit addresses.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 334dfa80a4..53106c9f82 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -104,6 +104,16 @@ Updated RPCs
with the `-json` option set, the following fields: `addresses`, `reqSigs` are no longer
returned in the tx output of the response. (#20286)
+- The `listbanned` RPC now returns two new numeric fields: `ban_duration` and `time_remaining`.
+ Respectively, these new fields indicate the duration of a ban and the time remaining until a ban expires,
+ both in seconds. Additionally, the `ban_created` field is repositioned to come before `banned_until`. (#21602)
+
+- The `getnodeaddresses` RPC now returns a "network" field indicating the
+ network type (ipv4, ipv6, onion, or i2p) for each address. (#21594)
+
+- `getnodeaddresses` now also accepts a "network" argument (ipv4, ipv6, onion,
+ or i2p) to return only addresses of the specified network. (#21843)
+
Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section below.
New RPCs
@@ -129,6 +139,12 @@ Changes to Wallet or GUI related settings can be found in the GUI or Wallet sect
Tools and Utilities
-------------------
+- A new CLI `-addrinfo` command returns the number of addresses known to the
+ node per network type (including Tor v2 versus v3) and total. This can be
+ useful to see if the node knows enough addresses in a network to use options
+ like `-onlynet=<network>` or to upgrade to current and future Tor releases
+ that support Tor v3 addresses only. (#21595)
+
Wallet
------
@@ -139,6 +155,11 @@ Wallet
- The `bumpfee` RPC is not available with wallets that have private keys
disabled. `psbtbumpfee` can be used instead. (#20891)
+- The `fundrawtransaction`, `send` and `walletcreatefundedpsbt` RPCs now support an `include_unsafe` option
+ that when `true` allows using unsafe inputs to fund the transaction.
+ Note that the resulting transaction may become invalid if one of the unsafe inputs disappears.
+ If that happens, the transaction must be funded with different inputs and republished. (#21359)
+
GUI changes
-----------
diff --git a/doc/release-notes/release-notes-0.21.1.md b/doc/release-notes/release-notes-0.21.1.md
new file mode 100644
index 0000000000..d032fa8429
--- /dev/null
+++ b/doc/release-notes/release-notes-0.21.1.md
@@ -0,0 +1,203 @@
+0.21.1 Release Notes
+====================
+
+Bitcoin Core version 0.21.1 is now available from:
+
+ <https://bitcoincore.org/bin/bitcoin-core-0.21.1/>
+
+This minor release includes various bug fixes and performance
+improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+ <https://github.com/bitcoin/bitcoin/issues>
+
+To receive security and update notifications, please subscribe to:
+
+ <https://bitcoincore.org/en/list/announcements/join/>
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes in some cases), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the data directory needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 10.12+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
+
+From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no
+longer supported. Additionally, Bitcoin Core does not yet change appearance
+when macOS "dark mode" is activated.
+
+Notable changes
+===============
+
+## Taproot Soft Fork
+
+Included in this release are the mainnet and testnet activation
+parameters for the taproot soft fork (BIP341) which also adds support
+for schnorr signatures (BIP340) and tapscript (BIP342).
+
+If activated, these improvements will allow users of single-signature
+scripts, multisignature scripts, and complex contracts to all use
+identical-appearing commitments that enhance their privacy and the
+fungibility of all bitcoins. Spenders will enjoy lower fees and the
+ability to resolve many multisig scripts and complex contracts with the
+same efficiency, low fees, and large anonymity set as single-sig users.
+Taproot and schnorr also include efficiency improvements for full nodes
+such as the ability to batch signature verification. Together, the
+improvements lay the groundwork for future potential
+upgrades that may improve efficiency, privacy, and fungibility further.
+
+Activation for taproot is being managed using a variation of BIP9
+versionbits called Speedy Trial (described in BIP341). Taproot's
+versionbit is bit 2, and nodes will begin tracking which blocks signal
+support for taproot at the beginning of the first retarget period after
+taproot’s start date of 24 April 2021. If 90% of blocks within a
+2,016-block retarget period (about two weeks) signal support for taproot
+prior to the first retarget period beginning after the time of 11 August
+2021, the soft fork will be locked in, and taproot will then be active
+as of block 709632 (expected in early or mid November).
+
+Should taproot not be locked in via Speedy Trial activation, it is
+expected that a follow-up activation mechanism will be deployed, with
+changes to address the reasons the Speedy Trial method failed.
+
+This release includes the ability to pay taproot addresses, although
+payments to such addresses are not secure until taproot activates.
+It also includes the ability to relay and mine taproot transactions
+after activation. Beyond those two basic capabilities, this release
+does not include any code that allows anyone to directly use taproot.
+The addition of taproot-related features to Bitcoin Core's wallet is
+expected in later releases once taproot activation is assured.
+
+All users, businesses, and miners are encouraged to upgrade to this
+release (or a subsequent compatible release) unless they object to
+activation of taproot. If taproot is locked in, then upgrading before
+block 709632 is highly recommended to help enforce taproot's new rules
+and to avoid the unlikely case of seeing falsely confirmed transactions.
+
+Miners who want to activate Taproot should preferably use this release
+to control their signaling. The `getblocktemplate` RPC results will
+automatically be updated to signal once the appropriate start has been
+reached and continue signaling until the timeout occurs or taproot
+activates. Alternatively, miners may manually start signaling on bit 2
+at any time; if taproot activates, they will need to ensure they update
+their nodes before block 709632 or non-upgraded nodes could cause them to mine on
+an invalid chain. See the [versionbits
+FAQ](https://bitcoincore.org/en/2016/06/08/version-bits-miners-faq/) for
+details.
+
+
+For more information about taproot, please see the following resources:
+
+- Technical specifications
+ - [BIP340 Schnorr signatures for secp256k1](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
+ - [BIP341 Taproot: SegWit version 1 spending rules](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
+ - [BIP342 Validation of Taproot scripts](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki)
+
+- Popular articles;
+ - [Taproot Is Coming: What It Is, and How It Will Benefit Bitcoin](https://bitcoinmagazine.com/technical/taproot-coming-what-it-and-how-it-will-benefit-bitcoin)
+ - [What do Schnorr Signatures Mean for Bitcoin?](https://academy.binance.com/en/articles/what-do-schnorr-signatures-mean-for-bitcoin)
+ - [The Schnorr Signature & Taproot Softfork Proposal](https://blog.bitmex.com/the-schnorr-signature-taproot-softfork-proposal/)
+
+- Development history overview
+ - [Taproot](https://bitcoinops.org/en/topics/taproot/)
+ - [Schnorr signatures](https://bitcoinops.org/en/topics/schnorr-signatures/)
+ - [Tapscript](https://bitcoinops.org/en/topics/tapscript/)
+ - [Soft fork activation](https://bitcoinops.org/en/topics/soft-fork-activation/)
+
+- Other
+ - [Questions and answers related to taproot](https://bitcoin.stackexchange.com/questions/tagged/taproot)
+ - [Taproot review](https://github.com/ajtowns/taproot-review)
+
+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).
+
+0.21.1 change log
+=================
+
+### Consensus
+- #21377 Speedy trial support for versionbits (ajtowns)
+- #21686 Speedy trial activation parameters for Taproot (achow101)
+
+### P2P protocol and network code
+- #20852 allow CSubNet of non-IP networks (vasild)
+- #21043 Avoid UBSan warning in ProcessMessage(…) (practicalswift)
+
+### Wallet
+- #21166 Introduce DeferredSignatureChecker and have SignatureExtractorClass subclass it (achow101)
+- #21083 Avoid requesting fee rates multiple times during coin selection (achow101)
+
+### RPC and other APIs
+- #21201 Disallow sendtoaddress and sendmany when private keys disabled (achow101)
+
+### Build system
+- #21486 link against -lsocket if required for `*ifaddrs` (fanquake)
+- #20983 Fix MSVC build after gui#176 (hebasto)
+
+### Tests and QA
+- #21380 Add fuzzing harness for versionbits (ajtowns)
+- #20812 fuzz: Bump FuzzedDataProvider.h (MarcoFalke)
+- #20740 fuzz: Update FuzzedDataProvider.h from upstream (LLVM) (practicalswift)
+- #21446 Update vcpkg checkout commit (sipsorcery)
+- #21397 fuzz: Bump FuzzedDataProvider.h (MarcoFalke)
+- #21081 Fix the unreachable code at `feature_taproot` (brunoerg)
+- #20562 Test that a fully signed tx given to signrawtx is unchanged (achow101)
+- #21571 Make sure non-IP peers get discouraged and disconnected (vasild, MarcoFalke)
+- #21489 fuzz: cleanups for versionbits fuzzer (ajtowns)
+
+### Miscellaneous
+- #20861 BIP 350: Implement Bech32m and use it for v1+ segwit addresses (sipa)
+
+### Documentation
+- #21384 add signet to bitcoin.conf documentation (jonatack)
+- #21342 Remove outdated comment (hebasto)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- Aaron Clauson
+- Andrew Chow
+- Anthony Towns
+- Bruno Garcia
+- Fabian Jahr
+- fanquake
+- Hennadii Stepanov
+- Jon Atack
+- Luke Dashjr
+- MarcoFalke
+- Pieter Wuille
+- practicalswift
+- randymcmillan
+- Sjors Provoost
+- Vasil Dimov
+- W. J. van der Laan
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/doc/release-process.md b/doc/release-process.md
index 84b208a0d8..3ead1181b9 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -373,9 +373,7 @@ bitcoin.org (see below for bitcoin.org update instructions).
- Bitcoin Core announcements list https://bitcoincore.org/en/list/announcements/join/
- - Update title of #bitcoin on Freenode IRC
-
- - Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself
+ - Bitcoin Core Twitter https://twitter.com/bitcoincoreorg
- Celebrate
diff --git a/doc/tor.md b/doc/tor.md
index e38ada5bd6..2640a6109b 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -15,6 +15,10 @@ There are several ways to see your local onion address in Bitcoin Core:
You may set the `-debug=tor` config logging option to have additional
information in the debug log about your Tor configuration.
+CLI `-addrinfo` returns the number of addresses known to your node per network
+type, including Tor v2 and v3. This is useful to see how many onion addresses
+are known to your node for `-onlynet=onion` and how many Tor v3 addresses it
+knows when upgrading to current and future Tor releases that support Tor v3 only.
## 1. Run Bitcoin Core behind a Tor proxy
diff --git a/doc/translation_process.md b/doc/translation_process.md
index 39f878cea3..f132693264 100644
--- a/doc/translation_process.md
+++ b/doc/translation_process.md
@@ -22,8 +22,6 @@ cd src/
make translate
```
-`contrib/bitcoin-qt.pro` takes care of generating `.qm` (binary compiled) files from `.ts` (source files) files. It’s mostly automated, and you shouldn’t need to worry about it.
-
**Example Qt translation**
```cpp
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
@@ -104,6 +102,5 @@ To create a new language template, you will need to edit the languages manifest
**Note:** that the language translation file **must end in `.qm`** (the compiled extension), and not `.ts`.
### Questions and general assistance
-The Bitcoin-Core translation maintainers include *tcatm, seone, Diapolo, wumpus and luke-jr*. You can find them, and others, in the Freenode IRC chatroom - `irc.freenode.net #bitcoin-core-dev`.
If you are a translator, you should also subscribe to the mailing list, https://groups.google.com/forum/#!forum/bitcoin-translators. Announcements will be posted during application pre-releases to notify translators to check for updates.
diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf
index 5b7fc776a4..4a947001fa 100644
--- a/share/examples/bitcoin.conf
+++ b/share/examples/bitcoin.conf
@@ -63,9 +63,20 @@
# Port on which to listen for connections (default: 8333, testnet: 18333, signet: 38333, regtest: 18444)
#port=
-# Maximum number of inbound+outbound connections.
+# Maximum number of inbound + outbound connections (default: 125). This option
+# applies only if inbound connections are enabled; otherwise, the number of connections
+# will not be more than 11: 8 full-relay connections, 2 block-relay-only ones, and
+# occasionally 1 short-lived feeler or extra outbound block-relay-only connection.
+# These limits do not apply to connections added manually with the -addnode
+# configuration option or the addnode RPC, which have a separate limit of 8 connections.
#maxconnections=
+# Maximum upload bandwidth target in MiB per day (e.g. 'maxuploadtarget=1024' is 1 GiB per day).
+# This limits the upload bandwidth for those with bandwidth limits. 0 = no limit (default: 0).
+# -maxuploadtarget does not apply to peers with 'download' permission.
+# For more information on reducing bandwidth utilization, see: doc/reduce-traffic.md.
+#maxuploadtarget=
+
#
# JSON-RPC options (for controlling a running Bitcoin/bitcoind process)
#
@@ -142,8 +153,11 @@
# both prior transactions and several dozen future transactions.
#keypool=100
+# Maintain coinstats index used by the gettxoutsetinfo RPC (default: 0).
+#coinstatsindex=1
+
# Enable pruning to reduce storage requirements by deleting old blocks.
-# This mode is incompatible with -txindex and -rescan.
+# This mode is incompatible with -txindex, -coinstatsindex and -rescan.
# 0 = default (no pruning).
# 1 = allows manual pruning via RPC.
# >=550 = target to stay under in MiB.
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index 4efc4bd6f5..e4d438fdb0 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -58,7 +58,7 @@ if not XGETTEXT:
print('Cannot extract strings: xgettext utility is not installed or not configured.',file=sys.stderr)
print('Please install package "gettext" and re-run \'./configure\'.',file=sys.stderr)
sys.exit(1)
-child = Popen([XGETTEXT,'--output=-','-n','--keyword=_'] + files, stdout=PIPE)
+child = Popen([XGETTEXT,'--output=-','--from-code=utf-8','-n','--keyword=_'] + files, stdout=PIPE)
(out, err) = child.communicate()
messages = parse_po(out.decode('utf-8'))
diff --git a/src/Makefile.am b/src/Makefile.am
index 4e09c86ebd..66cb7cec2a 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
@@ -74,6 +74,7 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_CLI) \
+ $(LIBBITCOIN_IPC) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_WALLET_TOOL) \
$(LIBBITCOIN_ZMQ)
@@ -144,6 +145,7 @@ BITCOIN_CORE_H = \
core_memusage.h \
cuckoocache.h \
dbwrapper.h \
+ external_signer.h \
flatfile.h \
fs.h \
httprpc.h \
@@ -151,12 +153,17 @@ BITCOIN_CORE_H = \
i2p.h \
index/base.h \
index/blockfilterindex.h \
+ index/coinstatsindex.h \
index/disktxpos.h \
index/txindex.h \
indirectmap.h \
init.h \
+ init/common.h \
interfaces/chain.h \
+ interfaces/echo.h \
interfaces/handler.h \
+ interfaces/init.h \
+ interfaces/ipc.h \
interfaces/node.h \
interfaces/wallet.h \
key.h \
@@ -174,6 +181,7 @@ BITCOIN_CORE_H = \
netaddress.h \
netbase.h \
netmessagemaker.h \
+ node/blockstorage.h \
node/coin.h \
node/coinstats.h \
node/context.h \
@@ -185,6 +193,7 @@ BITCOIN_CORE_H = \
outputtype.h \
policy/feerate.h \
policy/fees.h \
+ policy/packages.h \
policy/policy.h \
policy/rbf.h \
policy/settings.h \
@@ -197,6 +206,7 @@ BITCOIN_CORE_H = \
rpc/blockchain.h \
rpc/client.h \
rpc/mining.h \
+ rpc/net.h \
rpc/protocol.h \
rpc/rawtransaction_util.h \
rpc/register.h \
@@ -237,17 +247,20 @@ BITCOIN_CORE_H = \
util/fees.h \
util/getuniquepath.h \
util/golombrice.h \
+ util/hash_type.h \
util/hasher.h \
util/macros.h \
util/message.h \
util/moneystr.h \
util/rbf.h \
util/readwritefile.h \
+ util/serfloat.h \
util/settings.h \
util/sock.h \
util/spanparsing.h \
util/string.h \
util/system.h \
+ util/thread.h \
util/threadnames.h \
util/time.h \
util/tokenpipe.h \
@@ -267,17 +280,18 @@ 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/receive.h \
wallet/rpcwallet.h \
wallet/salvage.h \
wallet/scriptpubkeyman.h \
+ wallet/spend.h \
wallet/sqlite.h \
+ wallet/transaction.h \
wallet/wallet.h \
wallet/walletdb.h \
wallet/wallettool.h \
@@ -297,6 +311,8 @@ obj/build.h: FORCE
"$(abs_top_srcdir)"
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
+ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
+
# server: shared between bitcoind and bitcoin-qt
# 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
@@ -318,12 +334,14 @@ libbitcoin_server_a_SOURCES = \
i2p.cpp \
index/base.cpp \
index/blockfilterindex.cpp \
+ index/coinstatsindex.cpp \
index/txindex.cpp \
init.cpp \
mapport.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
+ node/blockstorage.cpp \
node/coin.cpp \
node/coinstats.cpp \
node/context.cpp \
@@ -387,15 +405,16 @@ libbitcoin_wallet_a_SOURCES = \
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/receive.cpp \
wallet/rpcdump.cpp \
- wallet/rpcsigner.cpp \
wallet/rpcwallet.cpp \
wallet/scriptpubkeyman.cpp \
+ wallet/spend.cpp \
+ wallet/transaction.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
wallet/walletutil.cpp \
@@ -520,6 +539,8 @@ libbitcoin_common_a_SOURCES = \
compressor.cpp \
core_read.cpp \
core_write.cpp \
+ external_signer.cpp \
+ init/common.cpp \
key.cpp \
key_io.cpp \
merkleblock.cpp \
@@ -532,6 +553,7 @@ libbitcoin_common_a_SOURCES = \
protocol.cpp \
psbt.cpp \
rpc/rawtransaction_util.cpp \
+ rpc/external_signer.cpp \
rpc/util.cpp \
scheduler.cpp \
script/descriptor.cpp \
@@ -554,7 +576,9 @@ libbitcoin_util_a_SOURCES = \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
+ interfaces/echo.cpp \
interfaces/handler.cpp \
+ interfaces/init.cpp \
logging.cpp \
random.cpp \
randomenv.cpp \
@@ -576,7 +600,9 @@ libbitcoin_util_a_SOURCES = \
util/rbf.cpp \
util/readwritefile.cpp \
util/settings.cpp \
+ util/thread.cpp \
util/threadnames.cpp \
+ util/serfloat.cpp \
util/spanparsing.cpp \
util/strencodings.cpp \
util/string.cpp \
@@ -630,17 +656,17 @@ bitcoin_bin_ldadd = \
bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
-bitcoind_SOURCES = $(bitcoin_daemon_sources)
+bitcoind_SOURCES = $(bitcoin_daemon_sources) init/bitcoind.cpp
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoind_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoind_LDFLAGS = $(bitcoin_bin_ldflags)
bitcoind_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
-bitcoin_node_SOURCES = $(bitcoin_daemon_sources)
+bitcoin_node_SOURCES = $(bitcoin_daemon_sources) init/bitcoin-node.cpp
bitcoin_node_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_node_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_node_LDFLAGS = $(bitcoin_bin_ldflags)
-bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
+bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -804,6 +830,39 @@ if HARDEN
$(AM_V_at) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
endif
+libbitcoin_ipc_mpgen_input = \
+ ipc/capnp/echo.capnp \
+ ipc/capnp/init.capnp
+EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
+%.capnp:
+
+if BUILD_MULTIPROCESS
+LIBBITCOIN_IPC=libbitcoin_ipc.a
+libbitcoin_ipc_a_SOURCES = \
+ ipc/capnp/init-types.h \
+ ipc/capnp/protocol.cpp \
+ ipc/capnp/protocol.h \
+ ipc/exception.h \
+ ipc/interfaces.cpp \
+ ipc/process.cpp \
+ ipc/process.h \
+ ipc/protocol.h
+libbitcoin_ipc_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+libbitcoin_ipc_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(LIBMULTIPROCESS_CFLAGS)
+
+include $(MPGEN_PREFIX)/include/mpgen.mk
+libbitcoin_ipc_mpgen_output = \
+ $(libbitcoin_ipc_mpgen_input:=.c++) \
+ $(libbitcoin_ipc_mpgen_input:=.h) \
+ $(libbitcoin_ipc_mpgen_input:=.proxy-client.c++) \
+ $(libbitcoin_ipc_mpgen_input:=.proxy-server.c++) \
+ $(libbitcoin_ipc_mpgen_input:=.proxy-types.c++) \
+ $(libbitcoin_ipc_mpgen_input:=.proxy-types.h) \
+ $(libbitcoin_ipc_mpgen_input:=.proxy.h)
+nodist_libbitcoin_ipc_a_SOURCES = $(libbitcoin_ipc_mpgen_output)
+CLEANFILES += $(libbitcoin_ipc_mpgen_output)
+endif
+
if EMBEDDED_LEVELDB
include Makefile.crc32c.include
include Makefile.leveldb.include
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 399a8430ef..a1821cafe3 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -61,6 +61,7 @@ QT_MOC_CPP = \
qt/moc_optionsmodel.cpp \
qt/moc_overviewpage.cpp \
qt/moc_peertablemodel.cpp \
+ qt/moc_peertablesortproxy.cpp \
qt/moc_paymentserver.cpp \
qt/moc_psbtoperationsdialog.cpp \
qt/moc_qrimagewidget.cpp \
@@ -134,6 +135,7 @@ BITCOIN_QT_H = \
qt/overviewpage.h \
qt/paymentserver.h \
qt/peertablemodel.h \
+ qt/peertablesortproxy.h \
qt/platformstyle.h \
qt/psbtoperationsdialog.h \
qt/qrimagewidget.h \
@@ -232,6 +234,7 @@ BITCOIN_QT_BASE_CPP = \
qt/optionsdialog.cpp \
qt/optionsmodel.cpp \
qt/peertablemodel.cpp \
+ qt/peertablesortproxy.cpp \
qt/platformstyle.cpp \
qt/qvalidatedlineedit.cpp \
qt/qvaluecombobox.cpp \
@@ -285,7 +288,7 @@ RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png)
BITCOIN_RC = qt/res/bitcoin-qt-res.rc
-BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS
+BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS -DQT_USE_QSTRINGBUILDER
qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS)
@@ -355,19 +358,19 @@ $(srcdir)/qt/bitcoinstrings.cpp: FORCE
translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts $(srcdir)/qt/locale/bitcoin_en.ts
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/bitcoin_en.ts
+ @test -n $(LCONVERT) || echo "lconvert is required for updating translations"
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LCONVERT) -o $(srcdir)/qt/locale/bitcoin_en.xlf -i $(srcdir)/qt/locale/bitcoin_en.ts
$(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
@test -f $(RCC)
@cp -f $< $(@D)/temp_$(<F)
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale $(@D)/temp_$(<F) | \
- $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale --format-version 1 $(@D)/temp_$(<F) > $@
@rm $(@D)/temp_$(<F)
$(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' > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin --format-version 1 $< > $@
CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_bitcoin_locale.qrc
@@ -379,7 +382,8 @@ bitcoin_qt_clean: FORCE
bitcoin_qt : qt/bitcoin-qt$(EXEEXT)
APK_LIB_DIR = qt/android/libs/$(ANDROID_ARCH)
-QT_BASE_PATH = $(shell find ../depends/sources/ -maxdepth 1 -type f -regex ".*qtbase.*\.tar.xz")
+QT_BASE_VERSION = $(lastword $(shell $(MOC) --version))
+QT_BASE_PATH = $(shell find ../depends/sources/ -maxdepth 1 -type f -regex ".*qtbase.*$(QT_BASE_VERSION)\.tar.xz")
QT_BASE_TLD = $(shell tar tf $(QT_BASE_PATH) --exclude='*/*')
bitcoin_qt_apk: FORCE
@@ -398,12 +402,10 @@ ui_%.h: %.ui
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(UIC) -o $@ $< || (echo "Error creating $@"; false)
%.moc: %.cpp
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< | \
- $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< > $@
moc_%.cpp: %.h
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< | \
- $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< > $@
%.qm: %.ts
@test -f $(LRELEASE)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 570f011f7a..105d09f730 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -80,6 +80,7 @@ BITCOIN_TESTS =\
test/bswap_tests.cpp \
test/checkqueue_tests.cpp \
test/coins_tests.cpp \
+ test/coinstatsindex_tests.cpp \
test/compilerbug_tests.cpp \
test/compress_tests.cpp \
test/crypto_tests.cpp \
@@ -120,6 +121,7 @@ BITCOIN_TESTS =\
test/script_tests.cpp \
test/script_standard_tests.cpp \
test/scriptnum_tests.cpp \
+ test/serfloat_tests.cpp \
test/serialize_tests.cpp \
test/settings_tests.cpp \
test/sighash_tests.cpp \
@@ -256,7 +258,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/netaddress.cpp \
test/fuzz/netbase_dns_lookup.cpp \
test/fuzz/node_eviction.cpp \
- test/fuzz/p2p_transport_deserializer.cpp \
+ test/fuzz/p2p_transport_serialization.cpp \
test/fuzz/parse_hd_keypath.cpp \
test/fuzz/parse_iso8601.cpp \
test/fuzz/parse_numbers.cpp \
@@ -274,6 +276,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/random.cpp \
test/fuzz/rbf.cpp \
test/fuzz/rolling_bloom_filter.cpp \
+ test/fuzz/rpc.cpp \
test/fuzz/script.cpp \
test/fuzz/script_assets_test_minimizer.cpp \
test/fuzz/script_bitcoin_consensus.cpp \
@@ -301,6 +304,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/tx_out.cpp \
test/fuzz/tx_pool.cpp \
test/fuzz/txrequest.cpp \
+ test/fuzz/utxo_snapshot.cpp \
test/fuzz/validation_load_mempool.cpp \
test/fuzz/versionbits.cpp
endif # ENABLE_FUZZ_BINARY
@@ -348,11 +352,6 @@ if EMBEDDED_UNIVALUE
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
endif
-if ENABLE_FUZZ_LINK_ALL
-all-local: $(FUZZ_BINARY)
- bash ./test/fuzz/danger_link_all.sh
-endif
-
%.cpp.test: %.cpp
@echo Running tests: `cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $<
$(AM_V_at)$(TEST_BINARY) --catch_system_errors=no -l test_suite -t "`cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" -- DEBUG_LOG_OUT > $<.log 2>&1 || (cat $<.log && false)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 0922c1c432..c376aced10 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -43,7 +43,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
// open temp output file, and associate with CAutoFile
- fs::path pathTmp = GetDataDir() / tmpfn;
+ fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull()) {
@@ -135,7 +135,7 @@ bool CBanDB::Read(banmap_t& banSet)
CAddrDB::CAddrDB()
{
- pathAddr = GetDataDir() / "peers.dat";
+ pathAddr = gArgs.GetDataDirNet() / "peers.dat";
}
bool CAddrDB::Write(const CAddrMan& addr)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index f91121f156..14b412a038 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -7,9 +7,11 @@
#include <hash.h>
#include <logging.h>
+#include <netaddress.h>
#include <serialize.h>
#include <cmath>
+#include <optional>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -34,7 +36,7 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std:
int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
{
- uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash();
+ uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
@@ -481,7 +483,7 @@ int CAddrMan::Check_()
}
#endif
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct)
+void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
size_t nNodes = vRandom.size();
if (max_pct != 0) {
@@ -492,6 +494,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
}
// gather a list of random nodes, skipping those of low quality
+ const int64_t now{GetAdjustedTime()};
for (unsigned int n = 0; n < vRandom.size(); n++) {
if (vAddr.size() >= nNodes)
break;
@@ -501,8 +504,14 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
assert(mapInfo.count(vRandom[n]) == 1);
const CAddrInfo& ai = mapInfo[vRandom[n]];
- if (!ai.IsTerrible())
- vAddr.push_back(ai);
+
+ // Filter by network (optional)
+ if (network != std::nullopt && ai.GetNetClass() != network) continue;
+
+ // Filter for quality
+ if (ai.IsTerrible(now)) continue;
+
+ vAddr.push_back(ai);
}
}
@@ -643,7 +652,7 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
int length = ftell(filestr);
LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
fseek(filestr, 0, SEEK_SET);
- char cur_byte;
+ uint8_t cur_byte;
for (int i = 0; i < length; ++i) {
file >> cur_byte;
for (int bit = 0; bit < 8; ++bit) {
diff --git a/src/addrman.h b/src/addrman.h
index 92a5570953..41994288db 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -20,6 +20,7 @@
#include <hash.h>
#include <iostream>
#include <map>
+#include <optional>
#include <set>
#include <stdint.h>
#include <streams.h>
@@ -171,132 +172,6 @@ static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes
*/
class CAddrMan
{
-friend class CAddrManTest;
-protected:
- //! critical section to protect the inner data structures
- mutable RecursiveMutex cs;
-
-private:
- //! Serialization versions.
- enum Format : uint8_t {
- V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
- V1_DETERMINISTIC = 1, //!< for pre-asmap files
- V2_ASMAP = 2, //!< for files including asmap version
- V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
- };
-
- //! The maximum format this software knows it can unserialize. Also, we always serialize
- //! in this format.
- //! The format (first byte in the serialized stream) can be higher than this and
- //! still this software may be able to unserialize the file - if the second byte
- //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
- static constexpr Format FILE_FORMAT = Format::V3_BIP155;
-
- //! The initial value of a field that is incremented every time an incompatible format
- //! change is made (such that old software versions would not be able to parse and
- //! understand the new file format). This is 32 because we overtook the "key size"
- //! field which was 32 historically.
- //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
- static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
-
- //! last used nId
- int nIdCount GUARDED_BY(cs);
-
- //! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
-
- //! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
-
- //! randomly-ordered vector of all nIds
- std::vector<int> vRandom GUARDED_BY(cs);
-
- // number of "tried" entries
- int nTried GUARDED_BY(cs);
-
- //! list of "tried" buckets
- int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! number of (unique) "new" entries
- int nNew GUARDED_BY(cs);
-
- //! list of "new" buckets
- int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! last time Good was called (memory only)
- int64_t nLastGood GUARDED_BY(cs);
-
- //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
- std::set<int> m_tried_collisions;
-
-protected:
- //! secret key to randomize bucket select with
- uint256 nKey;
-
- //! Source of random numbers for randomization in inner loops
- FastRandomContext insecure_rand;
-
- //! Find an entry.
- CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! find an entry, creating it if necessary.
- //! nTime and nServices of the found node are updated, if necessary.
- CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Move an entry from the "new" table(s) to the "tried" table
- void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Delete an entry. It must not be in tried, and have refcount 0.
- void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
- void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Add an entry to the "new" table.
- bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry as attempted to connect.
- void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Return a random to-be-evicted tried table address.
- CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
-#ifdef DEBUG_ADDRMAN
- //! Perform consistency check. Returns an error code or zero.
- int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-#endif
-
- //! Select several addresses at once.
- void GetAddr_(std::vector<CAddress> &vAddr, size_t max_addresses, size_t max_pct) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- /** We have successfully connected to this peer. Calling this function
- * updates the CAddress's nTime, which is used in our IsTerrible()
- * decisions and gossiped to peers. Callers should be careful that updating
- * this information doesn't leak topology information to network spies.
- *
- * net_processing calls this function when it *disconnects* from a peer to
- * not leak information about currently connected peers.
- *
- * @param[in] addr The address of the peer we were connected to
- * @param[in] nTime The time that we were last connected to this peer
- */
- void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Update an entry's service bits.
- void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
public:
// Compressed IP->ASN mapping, loaded from a file when a node starts.
// Should be always empty if no file was provided.
@@ -317,7 +192,6 @@ public:
// Read asmap from provided binary file
static std::vector<bool> DecodeAsmap(fs::path path);
-
/**
* Serialized format.
* * format version byte (@see `Format`)
@@ -715,14 +589,20 @@ public:
return addrRet;
}
- //! Return a bunch of addresses, selected at random.
- std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct)
+ /**
+ * Return all or many randomly selected addresses, optionally by network.
+ *
+ * @param[in] max_addresses Maximum number of addresses to return (0 = all).
+ * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
+ * @param[in] network Select only addresses of this network (nullopt = all).
+ */
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
Check();
std::vector<CAddress> vAddr;
{
LOCK(cs);
- GetAddr_(vAddr, max_addresses, max_pct);
+ GetAddr_(vAddr, max_addresses, max_pct, network);
}
Check();
return vAddr;
@@ -745,6 +625,138 @@ public:
Check();
}
+protected:
+ //! secret key to randomize bucket select with
+ uint256 nKey;
+
+ //! Source of random numbers for randomization in inner loops
+ FastRandomContext insecure_rand;
+
+private:
+ //! critical section to protect the inner data structures
+ mutable RecursiveMutex cs;
+
+ //! Serialization versions.
+ enum Format : uint8_t {
+ V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
+ V1_DETERMINISTIC = 1, //!< for pre-asmap files
+ V2_ASMAP = 2, //!< for files including asmap version
+ V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
+ };
+
+ //! The maximum format this software knows it can unserialize. Also, we always serialize
+ //! in this format.
+ //! The format (first byte in the serialized stream) can be higher than this and
+ //! still this software may be able to unserialize the file - if the second byte
+ //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
+ static constexpr Format FILE_FORMAT = Format::V3_BIP155;
+
+ //! The initial value of a field that is incremented every time an incompatible format
+ //! change is made (such that old software versions would not be able to parse and
+ //! understand the new file format). This is 32 because we overtook the "key size"
+ //! field which was 32 historically.
+ //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
+ static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
+
+ //! last used nId
+ int nIdCount GUARDED_BY(cs);
+
+ //! table with information about all nIds
+ std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+
+ //! find an nId based on its network address
+ std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
+
+ //! randomly-ordered vector of all nIds
+ std::vector<int> vRandom GUARDED_BY(cs);
+
+ // number of "tried" entries
+ int nTried GUARDED_BY(cs);
+
+ //! list of "tried" buckets
+ int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! number of (unique) "new" entries
+ int nNew GUARDED_BY(cs);
+
+ //! list of "new" buckets
+ int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! last time Good was called (memory only)
+ int64_t nLastGood GUARDED_BY(cs);
+
+ //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
+ std::set<int> m_tried_collisions;
+
+ //! Find an entry.
+ CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! find an entry, creating it if necessary.
+ //! nTime and nServices of the found node are updated, if necessary.
+ CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Swap two elements in vRandom.
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Move an entry from the "new" table(s) to the "tried" table
+ void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Delete an entry. It must not be in tried, and have refcount 0.
+ void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
+ void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Mark an entry "good", possibly moving it from "new" to "tried".
+ void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Add an entry to the "new" table.
+ bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Mark an entry as attempted to connect.
+ void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
+ CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
+ void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Return a random to-be-evicted tried table address.
+ CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+#ifdef DEBUG_ADDRMAN
+ //! Perform consistency check. Returns an error code or zero.
+ int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+#endif
+
+ /**
+ * Return all or many randomly selected addresses, optionally by network.
+ *
+ * @param[out] vAddr Vector of randomly selected addresses from vRandom.
+ * @param[in] max_addresses Maximum number of addresses to return (0 = all).
+ * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
+ * @param[in] network Select only addresses of this network (nullopt = all).
+ */
+ void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** We have successfully connected to this peer. Calling this function
+ * updates the CAddress's nTime, which is used in our IsTerrible()
+ * decisions and gossiped to peers. Callers should be careful that updating
+ * this information doesn't leak topology information to network spies.
+ *
+ * net_processing calls this function when it *disconnects* from a peer to
+ * not leak information about currently connected peers.
+ *
+ * @param[in] addr The address of the peer we were connected to
+ * @param[in] nTime The time that we were last connected to this peer
+ */
+ void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Update an entry's service bits.
+ void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ friend class CAddrManTest;
};
#endif // BITCOIN_ADDRMAN_H
diff --git a/src/banman.cpp b/src/banman.cpp
index 3fe561ad01..bb97fc4809 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -15,7 +15,7 @@
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
{
- if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist...").translated);
+ if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
m_is_dirty = false;
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index ebdad5a4b8..b7bd8a3261 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -7,6 +7,7 @@
#include <random.h>
#include <util/time.h>
+#include <optional>
#include <vector>
/* A "source" is a source address from which we have received a bunch of other addresses. */
@@ -98,7 +99,7 @@ static void AddrManGetAddr(benchmark::Bench& bench)
FillAddrMan(addrman);
bench.run([&] {
- const auto& addresses = addrman.GetAddr(2500, 23);
+ const auto& addresses = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt);
assert(addresses.size() > 0);
});
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 67ab02a5b3..b4b33d115f 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -6,6 +6,7 @@
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <test/util/mining.h>
+#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/wallet.h>
#include <txmempool.h>
@@ -18,23 +19,17 @@ static void AssembleBlock(benchmark::Bench& bench)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
- const std::vector<unsigned char> op_true{OP_TRUE};
CScriptWitness witness;
- witness.stack.push_back(op_true);
-
- uint256 witness_program;
- CSHA256().Write(&op_true[0], op_true.size()).Finalize(witness_program.begin());
-
- const CScript SCRIPT_PUB{CScript(OP_0) << std::vector<unsigned char>{witness_program.begin(), witness_program.end()}};
+ witness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE);
// Collect some loose transactions that spend the coinbases of our mined blocks
constexpr size_t NUM_BLOCKS{200};
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, P2WSH_OP_TRUE));
tx.vin.back().scriptWitness = witness;
- tx.vout.emplace_back(1337, SCRIPT_PUB);
+ tx.vout.emplace_back(1337, P2WSH_OP_TRUE);
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
txs.at(b) = MakeTransactionRef(tx);
}
@@ -42,13 +37,13 @@ static void AssembleBlock(benchmark::Bench& bench)
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
for (const auto& txr : txs) {
- const MempoolAcceptResult res = ::AcceptToMemoryPool(::ChainstateActive(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
+ const MempoolAcceptResult res = ::AcceptToMemoryPool(test_setup->m_node.chainman->ActiveChainstate(), *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, P2WSH_OP_TRUE);
});
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 80acdec044..c279a9af2f 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -49,15 +49,14 @@ static void CoinSelection(benchmark::Bench& bench)
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
- const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34,
+ const CoinSelectionParams coin_selection_params(/* 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;
- bool bnb_used;
- bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
+ bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
@@ -101,12 +100,11 @@ static void BnBExhaustion(benchmark::Bench& bench)
std::vector<OutputGroup> utxo_pool;
CoinSet selection;
CAmount value_ret = 0;
- CAmount not_input_fees = 0;
bench.run([&] {
// Benchmark
CAmount target = make_hard_case(17, utxo_pool);
- SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret, not_input_fees); // Should exhaust
+ SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret); // Should exhaust
// Cleanup
utxo_pool.clear();
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 25d1a2b56c..4f6e1122b8 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -25,7 +25,8 @@ static void DuplicateInputs(benchmark::Bench& bench)
CMutableTransaction naughtyTx{};
LOCK(cs_main);
- CBlockIndex* pindexPrev = ::ChainActive().Tip();
+ assert(std::addressof(::ChainActive()) == std::addressof(testing_setup->m_node.chainman->ActiveChain()));
+ CBlockIndex* pindexPrev = testing_setup->m_node.chainman->ActiveChain().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
block.nNonce = 0;
diff --git a/src/bench/nanobench.h b/src/bench/nanobench.h
index c5379e7fd4..030d6ebf6a 100644
--- a/src/bench/nanobench.h
+++ b/src/bench/nanobench.h
@@ -7,7 +7,7 @@
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
-// Copyright (c) 2019-2020 Martin Ankerl <martin.ankerl@gmail.com>
+// Copyright (c) 2019-2021 Martin Ankerl <martin.ankerl@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -32,8 +32,8 @@
// see https://semver.org/
#define ANKERL_NANOBENCH_VERSION_MAJOR 4 // incompatible API changes
-#define ANKERL_NANOBENCH_VERSION_MINOR 0 // backwards-compatible changes
-#define ANKERL_NANOBENCH_VERSION_PATCH 0 // backwards-compatible bug fixes
+#define ANKERL_NANOBENCH_VERSION_MINOR 3 // backwards-compatible changes
+#define ANKERL_NANOBENCH_VERSION_PATCH 4 // backwards-compatible bug fixes
///////////////////////////////////////////////////////////////////////////////////////////////////
// public facing api - as minimal as possible
@@ -78,12 +78,20 @@
#if defined(ANKERL_NANOBENCH_LOG_ENABLED)
# include <iostream>
-# define ANKERL_NANOBENCH_LOG(x) std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl
+# define ANKERL_NANOBENCH_LOG(x) \
+ do { \
+ std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl; \
+ } while (0)
#else
-# define ANKERL_NANOBENCH_LOG(x)
+# define ANKERL_NANOBENCH_LOG(x) \
+ do { \
+ } while (0)
#endif
-#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
+#if defined(__linux__) && defined(PERF_EVENT_IOC_ID) && defined(PERF_COUNT_HW_REF_CPU_CYCLES) && defined(PERF_FLAG_FD_CLOEXEC) && \
+ !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
+// only enable perf counters on kernel 3.14 which seems to have all the necessary defines. The three PERF_... defines are not in
+// kernel 2.6.32 (all others are).
# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
#else
# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
@@ -173,7 +181,7 @@ class BigO;
* `contextswitches`, `instructions`, `branchinstructions`, and `branchmisses`. All the measuers (except `iterations`) are
* provided for a single iteration (so `elapsed` is the time a single iteration took). The following tags are available:
*
- * * `{{median(<name>>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.
+ * * `{{median(<name>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.
*
* * `{{average(<name>)}}` Average (mean) calculation.
*
@@ -181,10 +189,11 @@ class BigO;
* metric for the variation of measurements. It is more robust to outliers than the
* [Mean absolute percentage error (M-APE)](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error).
* @f[
- * \mathrm{medianAbsolutePercentError}(e) = \mathrm{median}\{| \frac{e_i - \mathrm{median}\{e\}}{e_i}| \}
+ * \mathrm{MdAPE}(e) = \mathrm{med}\{| \frac{e_i - \mathrm{med}\{e\}}{e_i}| \}
* @f]
- * E.g. for *elapsed*: First, @f$ \mathrm{median}\{elapsed\} @f$ is calculated. This is used to calculate the absolute percentage
- * error to this median for each measurement, as in @f$ | \frac{e_i - \mathrm{median}\{e\}}{e_i}| @f$. All these results
+ * E.g. for *elapsed*: First, @f$ \mathrm{med}\{e\} @f$ calculates the median by sorting and then taking the middle element
+ * of all *elapsed* measurements. This is used to calculate the absolute percentage
+ * error to this median for each measurement, as in @f$ | \frac{e_i - \mathrm{med}\{e\}}{e_i}| @f$. All these results
* are sorted, and the middle value is chosen as the median absolute percent error.
*
* This measurement is a bit hard to interpret, but it is very robust against outliers. E.g. a value of 5% means that half of the
@@ -207,7 +216,7 @@ class BigO;
*
* * `{{#measurement}}` To access individual measurement results, open the begin tag for measurements.
*
- * * `{{elapsed}}` Average elapsed time per iteration, in seconds.
+ * * `{{elapsed}}` Average elapsed wall clock time per iteration, in seconds.
*
* * `{{iterations}}` Number of iterations in the measurement. The number of iterations will fluctuate due
* to some applied randomness, to enhance accuracy.
@@ -261,6 +270,7 @@ class BigO;
* :cpp:func:`templates::csv() <ankerl::nanobench::templates::csv()>`
* :cpp:func:`templates::json() <ankerl::nanobench::templates::json()>`
* :cpp:func:`templates::htmlBoxplot() <ankerl::nanobench::templates::htmlBoxplot()>`
+ * :cpp:func:`templates::pyperf() <ankerl::nanobench::templates::pyperf()>`
@endverbatim
*
@@ -269,6 +279,7 @@ class BigO;
* @param out Output for the generated output.
*/
void render(char const* mustacheTemplate, Bench const& bench, std::ostream& out);
+void render(std::string const& mustacheTemplate, Bench const& bench, std::ostream& out);
/**
* Same as render(char const* mustacheTemplate, Bench const& bench, std::ostream& out), but for when
@@ -279,6 +290,7 @@ void render(char const* mustacheTemplate, Bench const& bench, std::ostream& out)
* @param out Output for the generated output.
*/
void render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
+void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
// Contains mustache-like templates
namespace templates {
@@ -297,7 +309,7 @@ char const* csv() noexcept;
/*!
@brief HTML output that uses plotly to generate an interactive boxplot chart. See the tutorial for an example output.
- The output uses only the elapsed time, and displays each epoch as a single dot.
+ The output uses only the elapsed wall clock time, and displays each epoch as a single dot.
@verbatim embed:rst
See the tutorial at :ref:`tutorial-template-html` for an example.
@endverbatim
@@ -307,6 +319,14 @@ char const* csv() noexcept;
char const* htmlBoxplot() noexcept;
/*!
+ @brief Output in pyperf compatible JSON format, which can be used for more analyzations.
+ @verbatim embed:rst
+ See the tutorial at :ref:`tutorial-template-pyperf` for an example how to further analyze the output.
+ @endverbatim
+ */
+char const* pyperf() noexcept;
+
+/*!
@brief Template to generate JSON data.
The generated JSON data contains *all* data that has been generated. All times are as double values, in seconds. The output can get
@@ -369,6 +389,8 @@ struct Config {
uint64_t mEpochIterations{0}; // If not 0, run *exactly* these number of iterations per epoch.
uint64_t mWarmup = 0;
std::ostream* mOut = nullptr;
+ std::chrono::duration<double> mTimeUnit = std::chrono::nanoseconds{1};
+ std::string mTimeUnitName = "ns";
bool mShowPerformanceCounters = true;
bool mIsRelative = false;
@@ -504,6 +526,7 @@ public:
*/
explicit Rng(uint64_t seed) noexcept;
Rng(uint64_t x, uint64_t y) noexcept;
+ Rng(std::vector<uint64_t> const& data);
/**
* Creates a copy of the Rng, thus the copy provides exactly the same random sequence as the original.
@@ -558,6 +581,14 @@ public:
template <typename Container>
void shuffle(Container& container) noexcept;
+ /**
+ * Extracts the full state of the generator, e.g. for serialization. For this RNG this is just 2 values, but to stay API compatible
+ * with future implementations that potentially use more state, we use a vector.
+ *
+ * @return Vector containing the full state:
+ */
+ std::vector<uint64_t> state() const;
+
private:
static constexpr uint64_t rotl(uint64_t x, unsigned k) noexcept;
@@ -667,6 +698,19 @@ public:
ANKERL_NANOBENCH(NODISCARD) std::string const& unit() const noexcept;
/**
+ * @brief Sets the time unit to be used for the default output.
+ *
+ * Nanobench defaults to using ns (nanoseconds) as output in the markdown. For some benchmarks this is too coarse, so it is
+ * possible to configure this. E.g. use `timeUnit(1ms, "ms")` to show `ms/op` instead of `ns/op`.
+ *
+ * @param tu Time unit to display the results in, default is 1ns.
+ * @param tuName Name for the time unit, default is "ns"
+ */
+ Bench& timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName);
+ ANKERL_NANOBENCH(NODISCARD) std::string const& timeUnitName() const noexcept;
+ ANKERL_NANOBENCH(NODISCARD) std::chrono::duration<double> const& timeUnit() const noexcept;
+
+ /**
* @brief Set the output stream where the resulting markdown table will be printed to.
*
* The default is `&std::cout`. You can disable all output by setting `nullptr`.
@@ -916,6 +960,7 @@ public:
@endverbatim
*/
Bench& render(char const* templateContent, std::ostream& os);
+ Bench& render(std::string const& templateContent, std::ostream& os);
Bench& config(Config const& benchmarkConfig);
ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;
@@ -945,23 +990,24 @@ void doNotOptimizeAway(T const& val);
#else
-// see folly's Benchmark.h
-template <typename T>
-constexpr bool doNotOptimizeNeedsIndirect() {
- using Decayed = typename std::decay<T>::type;
- return !ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(Decayed) || sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value;
-}
-
+// These assembly magic is directly from what Google Benchmark is doing. I have previously used what facebook's folly was doing, but
+// this seemd to have compilation problems in some cases. Google Benchmark seemed to be the most well tested anyways.
+// see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307
template <typename T>
-typename std::enable_if<!doNotOptimizeNeedsIndirect<T>()>::type doNotOptimizeAway(T const& val) {
+void doNotOptimizeAway(T const& val) {
// NOLINTNEXTLINE(hicpp-no-assembler)
- asm volatile("" ::"r"(val));
+ asm volatile("" : : "r,m"(val) : "memory");
}
template <typename T>
-typename std::enable_if<doNotOptimizeNeedsIndirect<T>()>::type doNotOptimizeAway(T const& val) {
+void doNotOptimizeAway(T& val) {
+# if defined(__clang__)
+ // NOLINTNEXTLINE(hicpp-no-assembler)
+ asm volatile("" : "+r,m"(val) : : "memory");
+# else
// NOLINTNEXTLINE(hicpp-no-assembler)
- asm volatile("" ::"m"(val) : "memory");
+ asm volatile("" : "+m,r"(val) : : "memory");
+# endif
}
#endif
@@ -1067,7 +1113,7 @@ constexpr uint64_t(Rng::max)() {
return (std::numeric_limits<uint64_t>::max)();
}
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint64_t Rng::operator()() noexcept {
auto x = mX;
@@ -1077,7 +1123,7 @@ uint64_t Rng::operator()() noexcept {
return x;
}
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint32_t Rng::bounded(uint32_t range) noexcept {
uint64_t r32 = static_cast<uint32_t>(operator()());
auto multiresult = r32 * range;
@@ -1103,6 +1149,7 @@ void Rng::shuffle(Container& container) noexcept {
}
}
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
constexpr uint64_t Rng::rotl(uint64_t x, unsigned k) noexcept {
return (x << k) | (x >> (64U - k));
}
@@ -1306,6 +1353,30 @@ char const* htmlBoxplot() noexcept {
</html>)DELIM";
}
+char const* pyperf() noexcept {
+ return R"DELIM({
+ "benchmarks": [
+ {
+ "runs": [
+ {
+ "values": [
+{{#measurement}} {{elapsed}}{{^-last}},
+{{/last}}{{/measurement}}
+ ]
+ }
+ ]
+ }
+ ],
+ "metadata": {
+ "loops": {{sum(iterations)}},
+ "inner_loops": {{batch}},
+ "name": "{{title}}",
+ "unit": "second"
+ },
+ "version": "1.0"
+})DELIM";
+}
+
char const* json() noexcept {
return R"DELIM({
"results": [
@@ -1410,6 +1481,7 @@ static std::vector<Node> parseMustacheTemplate(char const** tpl) {
}
static bool generateFirstLast(Node const& n, size_t idx, size_t size, std::ostream& out) {
+ ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
bool matchFirst = n == "-first";
bool matchLast = n == "-last";
if (!matchFirst && !matchLast) {
@@ -1632,6 +1704,7 @@ namespace detail {
char const* getEnv(char const* name);
bool isEndlessRunning(std::string const& name);
+bool isWarningsEnabled();
template <typename T>
T parseFile(std::string const& filename);
@@ -1770,25 +1843,49 @@ void render(char const* mustacheTemplate, std::vector<Result> const& results, st
for (size_t i = 0; i < nbResults; ++i) {
generateResult(n.children, i, results, out);
}
+ } else if (n == "measurement") {
+ if (results.size() != 1) {
+ throw std::runtime_error(
+ "render: can only use section 'measurement' here if there is a single result, but there are " +
+ detail::fmt::to_s(results.size()));
+ }
+ // when we only have a single result, we can immediately go into its measurement.
+ auto const& r = results.front();
+ for (size_t i = 0; i < r.size(); ++i) {
+ generateResultMeasurement(n.children, i, r, out);
+ }
} else {
- throw std::runtime_error("unknown section '" + std::string(n.begin, n.end) + "'");
+ throw std::runtime_error("render: unknown section '" + std::string(n.begin, n.end) + "'");
}
break;
case templates::Node::Type::tag:
- // This just uses the last result's config.
- if (!generateConfigTag(n, results.back().config(), out)) {
- throw std::runtime_error("unknown tag '" + std::string(n.begin, n.end) + "'");
+ if (results.size() == 1) {
+ // result & config are both supported there
+ generateResultTag(n, results.front(), out);
+ } else {
+ // This just uses the last result's config.
+ if (!generateConfigTag(n, results.back().config(), out)) {
+ throw std::runtime_error("unknown tag '" + std::string(n.begin, n.end) + "'");
+ }
}
break;
}
}
}
+void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {
+ render(mustacheTemplate.c_str(), results, out);
+}
+
void render(char const* mustacheTemplate, const Bench& bench, std::ostream& out) {
render(mustacheTemplate, bench.results(), out);
}
+void render(std::string const& mustacheTemplate, const Bench& bench, std::ostream& out) {
+ render(mustacheTemplate.c_str(), bench.results(), out);
+}
+
namespace detail {
PerformanceCounters& performanceCounters() {
@@ -1837,6 +1934,12 @@ bool isEndlessRunning(std::string const& name) {
return nullptr != endless && endless == name;
}
+// True when environment variable NANOBENCH_SUPPRESS_WARNINGS is either not set at all, or set to "0"
+bool isWarningsEnabled() {
+ auto suppression = getEnv("NANOBENCH_SUPPRESS_WARNINGS");
+ return nullptr == suppression || suppression == std::string("0");
+}
+
void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations) {
warnings.clear();
recommendations.clear();
@@ -1889,13 +1992,13 @@ void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<
recommendations.emplace_back("Make sure you compile for Release");
}
if (recommendPyPerf) {
- recommendations.emplace_back("Use 'pyperf system tune' before benchmarking. See https://github.com/vstinner/pyperf");
+ recommendations.emplace_back("Use 'pyperf system tune' before benchmarking. See https://github.com/psf/pyperf");
}
}
void printStabilityInformationOnce(std::ostream* outStream) {
static bool shouldPrint = true;
- if (shouldPrint && outStream) {
+ if (shouldPrint && outStream && isWarningsEnabled()) {
auto& os = *outStream;
shouldPrint = false;
std::vector<std::string> warnings;
@@ -1923,16 +2026,7 @@ uint64_t& singletonHeaderHash() noexcept {
return sHeaderHash;
}
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
-inline uint64_t fnv1a(std::string const& str) noexcept {
- auto val = UINT64_C(14695981039346656037);
- for (auto c : str) {
- val = (val ^ static_cast<uint8_t>(c)) * UINT64_C(1099511628211);
- }
- return val;
-}
-
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
inline uint64_t hash_combine(uint64_t seed, uint64_t val) {
return seed ^ (val + UINT64_C(0x9e3779b9) + (seed << 6U) + (seed >> 2U));
}
@@ -2010,7 +2104,7 @@ struct IterationLogic::Impl {
return static_cast<uint64_t>(doubleNewIters + 0.5);
}
- ANKERL_NANOBENCH_NO_SANITIZE("integer") void upscale(std::chrono::nanoseconds elapsed) {
+ ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined") void upscale(std::chrono::nanoseconds elapsed) {
if (elapsed * 10 < mTargetRuntimePerEpoch) {
// we are far below the target runtime. Multiply iterations by 10 (with overflow check)
if (mNumIters * 10 < mNumIters) {
@@ -2108,7 +2202,8 @@ struct IterationLogic::Impl {
columns.emplace_back(14, 0, "complexityN", "", mBench.complexityN());
}
- columns.emplace_back(22, 2, "ns/" + mBench.unit(), "", 1e9 * rMedian / mBench.batch());
+ columns.emplace_back(22, 2, mBench.timeUnitName() + "/" + mBench.unit(), "",
+ rMedian / (mBench.timeUnit().count() * mBench.batch()));
columns.emplace_back(22, 2, mBench.unit() + "/s", "", rMedian <= 0.0 ? 0.0 : mBench.batch() / rMedian);
double rErrorMedian = mResult.medianAbsolutePercentError(Result::Measure::elapsed);
@@ -2140,16 +2235,19 @@ struct IterationLogic::Impl {
}
}
- columns.emplace_back(12, 2, "total", "", mResult.sum(Result::Measure::elapsed));
+ columns.emplace_back(12, 2, "total", "", mResult.sumProduct(Result::Measure::iterations, Result::Measure::elapsed));
// write everything
auto& os = *mBench.output();
+ // combine all elements that are relevant for printing the header
uint64_t hash = 0;
- hash = hash_combine(fnv1a(mBench.unit()), hash);
- hash = hash_combine(fnv1a(mBench.title()), hash);
- hash = hash_combine(mBench.relative(), hash);
- hash = hash_combine(mBench.performanceCounters(), hash);
+ hash = hash_combine(std::hash<std::string>{}(mBench.unit()), hash);
+ hash = hash_combine(std::hash<std::string>{}(mBench.title()), hash);
+ hash = hash_combine(std::hash<std::string>{}(mBench.timeUnitName()), hash);
+ hash = hash_combine(std::hash<double>{}(mBench.timeUnit().count()), hash);
+ hash = hash_combine(std::hash<bool>{}(mBench.relative()), hash);
+ hash = hash_combine(std::hash<bool>{}(mBench.performanceCounters()), hash);
if (hash != singletonHeaderHash()) {
singletonHeaderHash() = hash;
@@ -2177,7 +2275,7 @@ struct IterationLogic::Impl {
os << col.value();
}
os << "| ";
- auto showUnstable = rErrorMedian >= 0.05;
+ auto showUnstable = isWarningsEnabled() && rErrorMedian >= 0.05;
if (showUnstable) {
os << ":wavy_dash: ";
}
@@ -2305,7 +2403,7 @@ public:
}
template <typename Op>
- ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void calibrate(Op&& op) {
// clear current calibration data,
for (auto& v : mCalibratedOverhead) {
@@ -2411,7 +2509,7 @@ bool LinuxPerformanceCounters::monitor(perf_hw_id hwId, LinuxPerformanceCounters
}
// overflow is ok, it's checked
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void LinuxPerformanceCounters::updateResults(uint64_t numIters) {
// clear old data
for (auto& id_value : mIdToTarget) {
@@ -2963,6 +3061,20 @@ std::string const& Bench::unit() const noexcept {
return mConfig.mUnit;
}
+Bench& Bench::timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName) {
+ mConfig.mTimeUnit = tu;
+ mConfig.mTimeUnitName = tuName;
+ return *this;
+}
+
+std::string const& Bench::timeUnitName() const noexcept {
+ return mConfig.mTimeUnitName;
+}
+
+std::chrono::duration<double> const& Bench::timeUnit() const noexcept {
+ return mConfig.mTimeUnit;
+}
+
// If benchmarkTitle differs from currently set title, the stored results will be cleared.
Bench& Bench::title(const char* benchmarkTitle) {
if (benchmarkTitle != mConfig.mBenchmarkTitle) {
@@ -3083,6 +3195,11 @@ Bench& Bench::render(char const* templateContent, std::ostream& os) {
return *this;
}
+Bench& Bench::render(std::string const& templateContent, std::ostream& os) {
+ ::ankerl::nanobench::render(templateContent, *this, os);
+ return *this;
+}
+
std::vector<BigO> Bench::complexityBigO() const {
std::vector<BigO> bigOs;
auto rangeMeasure = BigO::collectRangeMeasure(mResults);
@@ -3119,7 +3236,7 @@ Rng::Rng()
} while (mX == 0 && mY == 0);
}
-ANKERL_NANOBENCH_NO_SANITIZE("integer")
+ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint64_t splitMix64(uint64_t& state) noexcept {
uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15));
z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
@@ -3145,6 +3262,24 @@ Rng Rng::copy() const noexcept {
return Rng{mX, mY};
}
+Rng::Rng(std::vector<uint64_t> const& data)
+ : mX(0)
+ , mY(0) {
+ if (data.size() != 2) {
+ throw std::runtime_error("ankerl::nanobench::Rng::Rng: needed exactly 2 entries in data, but got " +
+ detail::fmt::to_s(data.size()));
+ }
+ mX = data[0];
+ mY = data[1];
+}
+
+std::vector<uint64_t> Rng::state() const {
+ std::vector<uint64_t> data(2);
+ data[0] = mX;
+ data[1] = mY;
+ return data;
+}
+
BigO::RangeMeasure BigO::collectRangeMeasure(std::vector<Result> const& results) {
BigO::RangeMeasure rangeMeasure;
for (auto const& result : results) {
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index e3f6b35a7d..39e74b9b2b 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -56,7 +56,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
txCredit.vout[0].scriptPubKey,
&txSpend.vin[0].scriptWitness,
flags,
- MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
+ MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL),
&err);
assert(err == SCRIPT_ERR_OK);
assert(success);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index d7cc167885..362b7c1e15 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -22,8 +22,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
- bool first_run;
- if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
+ if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
}
auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 4f3d7a4ffe..c5840b2098 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -42,6 +42,7 @@ static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
+static constexpr int8_t UNKNOWN_NETWORK{-1};
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std::string DEFAULT_NBLOCKS = "1";
@@ -59,6 +60,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -228,6 +230,60 @@ public:
virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
};
+/** Process addrinfo requests */
+class AddrinfoRequestHandler : public BaseRequestHandler
+{
+private:
+ static constexpr std::array m_networks{"ipv4", "ipv6", "torv2", "torv3", "i2p"};
+ int8_t NetworkStringToId(const std::string& str) const
+ {
+ for (size_t i = 0; i < m_networks.size(); ++i) {
+ if (str == m_networks.at(i)) return i;
+ }
+ return UNKNOWN_NETWORK;
+ }
+
+public:
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ if (!args.empty()) {
+ throw std::runtime_error("-addrinfo takes no arguments");
+ }
+ UniValue params{RPCConvertValues("getnodeaddresses", std::vector<std::string>{{"0"}})};
+ return JSONRPCRequestObj("getnodeaddresses", params, 1);
+ }
+
+ UniValue ProcessReply(const UniValue& reply) override
+ {
+ if (!reply["error"].isNull()) return reply;
+ const std::vector<UniValue>& nodes{reply["result"].getValues()};
+ if (!nodes.empty() && nodes.at(0)["network"].isNull()) {
+ throw std::runtime_error("-addrinfo requires bitcoind server to be running v22.0 and up");
+ }
+ // Count the number of peers we know by network, including torv2 versus torv3.
+ std::array<uint64_t, m_networks.size()> counts{{}};
+ for (const UniValue& node : nodes) {
+ std::string network_name{node["network"].get_str()};
+ if (network_name == "onion") {
+ network_name = node["address"].get_str().size() > 22 ? "torv3" : "torv2";
+ }
+ const int8_t network_id{NetworkStringToId(network_name)};
+ if (network_id == UNKNOWN_NETWORK) continue;
+ ++counts.at(network_id);
+ }
+ // Prepare result to return to user.
+ UniValue result{UniValue::VOBJ}, addresses{UniValue::VOBJ};
+ uint64_t total{0}; // Total address count
+ for (size_t i = 0; i < m_networks.size(); ++i) {
+ addresses.pushKV(m_networks.at(i), counts.at(i));
+ total += counts.at(i);
+ }
+ addresses.pushKV("total", total);
+ result.pushKV("addresses_known", addresses);
+ return JSONRPCReplyObj(result, NullUniValue, 1);
+ }
+};
+
/** Process getinfo requests */
class GetinfoRequestHandler: public BaseRequestHandler
{
@@ -299,16 +355,14 @@ public:
class NetinfoRequestHandler : public BaseRequestHandler
{
private:
- static constexpr int8_t UNKNOWN_NETWORK{-1};
- 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)
+ static constexpr std::array 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) {
+ for (size_t i = 0; i < m_networks.size(); ++i) {
if (str == m_networks.at(i)) return i;
}
return UNKNOWN_NETWORK;
@@ -364,7 +418,7 @@ private:
if (conn_type == "addr-fetch") return "addr";
return "";
}
- const int64_t m_time_now{GetSystemTimeInSeconds()};
+ const int64_t m_time_now{GetTimeSeconds()};
public:
static constexpr int ID_PEERINFO = 0;
@@ -405,10 +459,10 @@ public:
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
+ ++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 (conn_type == "block-relay-only") ++m_block_relay_peers_count;
if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
@@ -478,11 +532,11 @@ public:
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"}};
+ const std::array rows{"in", "out", "total"};
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
+ 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);
@@ -914,6 +968,8 @@ static int CommandLineRPC(int argc, char *argv[])
} else {
ParseError(error, strPrint, nRet);
}
+ } else if (gArgs.GetBoolArg("-addrinfo", false)) {
+ rh.reset(new AddrinfoRequestHandler());
} else {
rh.reset(new DefaultRequestHandler());
if (args.size() < 1) {
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 80ab69c131..cf9e4fad44 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -12,6 +12,7 @@
#include <compat.h>
#include <init.h>
#include <interfaces/chain.h>
+#include <interfaces/init.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
@@ -104,10 +105,8 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
#endif
-static bool AppInit(int argc, char* argv[])
+static bool AppInit(NodeContext& node, int argc, char* argv[])
{
- NodeContext node;
-
bool fRet = false;
util::ThreadSetInternalName("init");
@@ -224,7 +223,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitInterfaces(node) && AppInitMain(context, node);
+ fRet = AppInitInterfaces(node) && AppInitMain(node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
@@ -254,10 +253,18 @@ int main(int argc, char* argv[])
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
+
+ NodeContext node;
+ int exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
+ if (!init) {
+ return exit_status;
+ }
+
SetupEnvironment();
// Connect bitcoind signal handlers
noui_connect();
- return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
+ return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/src/chain.h b/src/chain.h
index 04a5db5a17..84a3a4e1e7 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -182,7 +182,7 @@ public:
//!
//! 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 NeedsRedownload
//! @sa ActivateSnapshot
uint32_t nStatus{0};
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 28b64ee7ff..fdaadeed4a 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -78,16 +78,18 @@ public:
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
- consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
+ consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
// Deployment of Taproot (BIPs 340-342)
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
- consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
- consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000001533efd8d716a517fe2c5008");
consensus.defaultAssumeValid = uint256S("0x0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"); // 654683
@@ -134,7 +136,7 @@ public:
bech32_hrp = "bc";
- vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_main), std::end(pnSeed6_main));
+ vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main));
fDefaultConsistencyChecks = false;
fRequireStandard = true;
@@ -198,13 +200,15 @@ public:
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
// Deployment of Taproot (BIPs 340-342)
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
- consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
- consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001db6ec4ac88cf2272c6");
consensus.defaultAssumeValid = uint256S("0x000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0"); // 1864000
@@ -239,7 +243,7 @@ public:
bech32_hrp = "tb";
- vFixedSeeds = std::vector<SeedSpec6>(std::begin(pnSeed6_test), std::end(pnSeed6_test));
+ vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test));
fDefaultConsistencyChecks = false;
fRequireStandard = false;
@@ -328,18 +332,20 @@ public:
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
- consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
+ consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.MinBIP9WarningHeight = 0;
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
- consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
// Activation of Taproot (BIPs 340-342)
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
// message start is defined as the first 4 bytes of the sha256d of the block script
CHashWriter h(SER_DISK, 0);
@@ -398,12 +404,16 @@ public:
consensus.fPowNoRetargeting = true;
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
+
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
consensus.nMinimumChainWork = uint256{};
consensus.defaultAssumeValid = uint256{};
@@ -413,7 +423,7 @@ public:
pchMessageStart[2] = 0xb5;
pchMessageStart[3] = 0xda;
nDefaultPort = 18444;
- nPruneAfterHeight = gArgs.GetBoolArg("-fastprune", false) ? 100 : 1000;
+ nPruneAfterHeight = args.GetBoolArg("-fastprune", false) ? 100 : 1000;
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
@@ -441,11 +451,11 @@ public:
m_assumeutxo_data = MapAssumeutxo{
{
110,
- {uint256S("0x76fd7334ac7c1baf57ddc0c626f073a655a35d98a4258cd1382c8cc2b8392e10"), 110},
+ {AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, 110},
},
{
- 210,
- {uint256S("0x9c5ed99ef98544b34f8920b6d1802f72ac28ae6e2bd2bd4c316ff10c230df3f2"), 210},
+ 200,
+ {AssumeutxoHash{uint256S("0x51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62")}, 200},
},
};
@@ -467,10 +477,11 @@ public:
/**
* Allows modifying the Version Bits regtest parameters.
*/
- void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+ void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int min_activation_height)
{
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
+ consensus.vDeployments[d].min_activation_height = min_activation_height;
}
void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
@@ -493,22 +504,26 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
- if (vDeploymentParams.size() != 3) {
- throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end");
+ if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
+ throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
}
int64_t nStartTime, nTimeout;
+ int min_activation_height = 0;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
}
+ if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
+ throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
+ }
bool found = false;
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
- UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
+ UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height);
found = true;
- LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
+ LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], nStartTime, nTimeout, min_activation_height);
break;
}
}
@@ -544,9 +559,3 @@ 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 6b43a44150..5c2351eea6 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -10,15 +10,11 @@
#include <consensus/params.h>
#include <primitives/block.h>
#include <protocol.h>
+#include <util/hash_type.h>
#include <memory>
#include <vector>
-struct SeedSpec6 {
- uint8_t addr[16];
- uint16_t port;
-};
-
typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
@@ -30,6 +26,10 @@ struct CCheckpointData {
}
};
+struct AssumeutxoHash : public BaseHash<uint256> {
+ explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {}
+};
+
/**
* Holds configuration for use during UTXO snapshot load and validation. The contents
* here are security critical, since they dictate which UTXO snapshots are recognized
@@ -37,7 +37,7 @@ struct CCheckpointData {
*/
struct AssumeutxoData {
//! The expected hash of the deserialized UTXO set.
- const uint256 hash_serialized;
+ const AssumeutxoHash hash_serialized;
//! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex().
//!
@@ -46,8 +46,6 @@ struct AssumeutxoData {
const unsigned int nChainTx;
};
-std::ostream& operator<<(std::ostream& o, const AssumeutxoData& aud);
-
using MapAssumeutxo = std::map<int, const AssumeutxoData>;
/**
@@ -105,7 +103,7 @@ public:
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP() const { return bech32_hrp; }
- const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
+ const std::vector<uint8_t>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
//! Get allowed assumeutxo configuration.
@@ -127,7 +125,7 @@ protected:
std::string bech32_hrp;
std::string strNetworkID;
CBlock genesis;
- std::vector<SeedSpec6> vFixedSeeds;
+ std::vector<uint8_t> vFixedSeeds;
bool fDefaultConsistencyChecks;
bool fRequireStandard;
bool m_is_test_chain;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 1631176477..e71b4bc859 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -22,7 +22,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 3dfbae33bc..08587e2b37 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -4,1183 +4,707 @@
* List of fixed seed nodes for the bitcoin network
* AUTOGENERATED by contrib/seeds/generate-seeds.py
*
- * Each line contains a 16-byte IPv6 address and a port.
- * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.
+ * Each line contains a BIP155 serialized (networkID, addr, port) tuple.
*/
-static SeedSpec6 pnSeed6_main[] = {
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x02,0x27,0xad,0x7e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x03,0x0e,0xa8,0xc9}, 48333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x04,0x24,0x70,0x2c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x08,0x12,0x1f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x0e,0xc8,0xa7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x38,0x14,0x02}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x66,0x92,0x63}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x67,0x89,0x92}, 9333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x80,0x57,0x7e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x85,0x41,0x52}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xbb,0x37,0xf2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xbc,0x3e,0x18}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xbc,0x3e,0x21}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xc7,0x85,0xc1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x08,0x26,0x59,0x98}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x0d,0xe7,0x14,0xf9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0x1b,0x4f,0x11}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x14,0xb8,0x0f,0x74}, 8433},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0x1c,0xcd,0x61}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0x6a,0xfc,0xe6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0xca}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0xd4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xf1,0xfa,0xfc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xf5,0x18,0x9a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0x56,0xb8,0x42}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0x74,0xf6,0x09}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0x8d,0x22,0xa6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0x9b,0xc4,0xf6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0x9d,0x82,0xde}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0xbc,0xb0,0xff}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x18,0xed,0x46,0x35}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1b,0x7c,0x04,0x43}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x11,0x46,0x50}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x15,0x08,0x20}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x2d,0x76,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x84,0x11,0x38}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x86,0x79,0xdf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x20,0xd6,0xb7,0x72}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0x89,0xec,0x20}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xb9,0x91,0x69}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xd1,0x33,0xd4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xf5,0xaf,0x4c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x74,0x5f,0x29}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x8f,0x09,0x6b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x8f,0x74,0x2b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xbf,0xf4,0x95}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xd3,0x4e,0xfd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xd1,0xde}, 24333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xe4,0x5c,0x6e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0xe1,0x3e,0x6b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0xe1,0x9d,0x98}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x24,0xb8,0x06}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x30,0xa8,0x10}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x55,0x55,0x08}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x55,0x55,0x09}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x81,0xb4,0xd6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x95,0x4e,0x80}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x97,0x7d,0xda}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x9a,0xff,0x2e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x9b,0x9d,0xef}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x1c,0x84,0x22}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x1c,0xcc,0x15}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x20,0x32,0x62}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x3b,0x0d,0x23}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x80,0x28,0xad}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x80,0x8c,0xc1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x92,0xf8,0x59}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xa6,0xa2,0x2d}, 20001},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xbc,0x0f,0x06}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xe5,0xa5,0x8e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xe5,0xee,0xbb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xf9,0x53,0x52}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xfe,0xd9,0xa9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x4a,0xbf,0x22}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x73,0x35,0xa3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0xbb,0x1a,0x87}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0xde,0x67,0xea}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0xfd,0x05,0x63}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x31,0xe8,0x52,0x4c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x31,0xf7,0xd7,0x2b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x02,0x0d,0xa6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x22,0x27,0x48}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x2d,0xe8,0xbd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x44,0x68,0x5c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0x44,0x24,0x39}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0x9a,0x3c,0x22}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xa9,0xee,0x42}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x36,0xc5,0x1e,0xdf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x36,0xe3,0x42,0x39}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3a,0x9e,0x00,0x56}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3a,0xab,0x87,0xf2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3a,0xe5,0xd0,0x9e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3c,0xf4,0x6d,0x13}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x26,0x4b,0xd0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x4a,0x8f,0x0b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x50,0xe3,0x31}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x98,0x3a,0x10}, 9421},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0xd2,0xa7,0xc7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0xea,0xbc,0xa0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0xfb,0x36,0xa3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0xe3,0x74,0xa2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x41,0x13,0x9b,0x52}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x41,0x5f,0x31,0x66}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0x12,0xac,0x15}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xf0,0xed,0x9b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x43,0xd2,0xe4,0xcb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x1e,0xd7,0x2a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x3b,0x12,0xce}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x40,0x21,0x47}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x77,0xc1,0x09}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0xd1,0x17,0x48}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x46,0x7b,0x7d,0xed}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x46,0xb9,0x38,0x88}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x47,0x26,0x5a,0xeb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0x0c,0x49,0x46}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0x35,0x86,0xb6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0xe1,0x07,0x50}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0xea,0xb6,0x27}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0xfa,0xb8,0x39}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x49,0x53,0x67,0x4f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0x76,0x89,0x77}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0x85,0x64,0x4a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0xd7,0xdb,0xd6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0xdc,0xff,0xbe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4b,0x9e,0x27,0xe7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x35,0x35,0xc4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x46,0x10,0xf5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x69,0x57,0x61}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x78,0x71,0x45}, 8433},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x78,0x7a,0x16}, 8433},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0xa6,0x53,0xa7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0xf7,0xb2,0x82}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x1b,0x8b,0x0d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x3f,0x1c,0x92}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x53,0x67,0x04}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x8d,0x7b,0x63}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x4d,0x21,0x83}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x4d,0x85,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x65,0x01,0x19}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x75,0xc0,0xe5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x85,0xe4,0x37}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4f,0x92,0x15,0xa3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0x59,0xcb,0xac}, 8001},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0x5d,0xd5,0xf6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xc0,0x62,0x6e}, 8334},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xe5,0x1c,0x3c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xe8,0xf7,0xd2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xf2,0x27,0x4c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xfd,0x5e,0xfc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0x00,0xc6,0x19}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0x07,0x0d,0x54}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0x75,0xe1,0xf5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0x87,0x89,0xe1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xab,0x16,0x8f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xbf,0xe9,0x86}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xe8,0x4e,0x4b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xf2,0x5b,0x17}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0x1d,0x3a,0x6d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0x88,0x63,0x16}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0x95,0x61,0x19}, 17567},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xa5,0x13,0x30}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xc2,0x99,0xe9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xc5,0xd7,0x7d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xc7,0x66,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xc8,0xcd,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xca,0x44,0xe7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xdd,0x80,0x1f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xe4,0x06,0x83}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0x55,0x8b,0x5e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0x63,0xf5,0x14}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0x89,0x29,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0xae,0xd1,0x57}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0xd9,0x08,0x1f}, 44420},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0x26,0x03,0xf9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0x26,0xb9,0x7a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0x5c,0x5c,0xf7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xc0,0x10,0xea}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xc2,0x9e,0x7c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xd4,0x91,0x18}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xd4,0xf4,0x5f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xd8,0x33,0x24}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xff,0xf9,0xa3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0x19,0xff,0x93}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0x46,0x9c,0xd1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0x91,0x8e,0x2e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xaa,0xe9,0x5f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xb8,0x8a,0x6c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xbe,0x00,0x05}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xbf,0xc8,0x33}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xc0,0xbf,0x06}, 18500},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xc2,0xee,0x83}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xc3,0x36,0x6e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd6,0xa1,0xfc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd6,0xb9,0x33}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xf1,0x6a,0xcb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xf6,0xa8,0xfc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x56,0x38,0xee,0xf7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x3d,0x5a,0xe6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x4f,0x44,0x56}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x4f,0x5e,0xdd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x78,0x08,0x05}, 20008},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0xf6,0x2e,0x84}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0xf7,0x6f,0xde}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x54,0xde,0xfc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x56,0xf3,0xf1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x57,0x5d,0x34}, 1691},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x77,0xc5,0xc8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x81,0xfd,0x5e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x93,0xf4,0xfa}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0xd0,0x03,0xc3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0xd4,0x2c,0x21}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0xd6,0x39,0x5f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x6a,0xc7,0x26}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x6c,0x7e,0xe4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x73,0x78,0x2b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x85,0x44,0x41}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0xbe,0x13,0xa2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0xf8,0xac,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5a,0x92,0x99,0x15}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5a,0xb6,0xa5,0x12}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0x6a,0xbc,0xe5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xc1,0xed,0x74}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xcc,0x63,0xb2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xcc,0x95,0x05}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xd6,0x46,0x3f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xe4,0x98,0xec}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5c,0x0c,0x9a,0x73}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5c,0xf9,0x8f,0x2c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x0c,0x42,0x62}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x2e,0x36,0x04}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x73,0x14,0x82}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x7b,0xb4,0xa4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0xbd,0x91,0xa9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0xf1,0xe4,0x66}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x13,0x07,0x37}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x13,0x80,0xcc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x34,0x70,0xe3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x9a,0x60,0x82}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x9c,0xae,0xc9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x9e,0xf6,0xb7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xab,0x49}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xc7,0xb2,0xe9}, 8100},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xed,0x7d,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xf7,0x86,0x4d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x30,0xe4,0x2d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x45,0xf9,0x3f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x52,0x92,0x46}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x53,0x49,0x1f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x54,0xa4,0x2b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x57,0xe2,0x38}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x6e,0xea,0x5d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xa3,0x47,0x7e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xa4,0x41,0xc2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xae,0x42,0xd3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd3,0xae,0x89}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0x0b,0x9c}, 8433},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x60,0x2f,0x72,0x6c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x61,0x54,0xe8,0x69}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x61,0x63,0xcd,0xf1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x62,0x19,0xc1,0x72}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x63,0x73,0x19,0x0d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x65,0x20,0x13,0xb8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x65,0x64,0xae,0xf0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x66,0x84,0xf5,0x10}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x0e,0xf4,0xbe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x4c,0x30,0x05}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x54,0x54,0xfa}, 8335},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x63,0xa8,0x96}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x6d,0x65,0xd8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x7a,0xf7,0x66}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x81,0x0d,0x2d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0xc6,0xc0,0x0e}, 20008},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0xe0,0x77,0x63}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0xe7,0xbf,0x07}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0xeb,0xe6,0xc4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xab,0xf2,0x9b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xee,0xdc,0xc7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6a,0xa3,0x9e,0x7f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x96,0x29,0xb3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x9f,0x5d,0x67}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6c,0xb7,0x4d,0x0c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0x09,0xaf,0x41}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0x63,0x3f,0x9f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0x6e,0x51,0x5a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0x7b,0xd5,0x82}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0x86,0xe8,0x51}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0xa9,0x14,0xa8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0xc7,0xf1,0x94}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0xe5,0xd2,0x06}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0xec,0x69,0x28}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6d,0xf8,0xce,0x0d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6f,0x2a,0x4a,0x41}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6f,0x5a,0x8c,0xb3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x70,0xd7,0xcd,0xec}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x71,0x34,0x87,0x7d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x72,0x17,0xf6,0x89}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x73,0x2f,0x8d,0xfa}, 8885},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x73,0x46,0x6e,0x04}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x74,0x22,0xbd,0x37}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x76,0x67,0x7e,0x8c}, 28333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x76,0xbd,0xbb,0xdb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x77,0x03,0xd0,0xec}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x77,0x08,0x2f,0xe1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x77,0x11,0x97,0x3d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x78,0x19,0x18,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x78,0xf1,0x22,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x79,0x62,0xcd,0x64}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7a,0x70,0x94,0x99}, 8339},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7a,0x74,0x2a,0x8c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7c,0xd9,0xeb,0xb4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7d,0xec,0xd7,0x85}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x81,0x0d,0xbd,0xd4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x82,0xb9,0x4d,0x69}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x83,0xbc,0x28,0xbf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x83,0xc1,0xdc,0x0f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x87,0x17,0x7c,0xef}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x88,0x21,0xb9,0x20}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x88,0x38,0xaa,0x60}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x89,0xe2,0x22,0x2e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8a,0xe5,0x1a,0x2a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x09,0xf9,0xea}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8d,0x65,0x08,0x24}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8f,0xb0,0xe0,0x68}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x90,0x02,0x45,0xe0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x90,0x22,0xa1,0x41}, 18333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x90,0x5b,0x74,0x2c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x90,0x89,0x1d,0xb5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x94,0x42,0x32,0x32}, 8335},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x94,0x48,0x96,0xe7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x94,0xaa,0xd4,0x2c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x95,0xa7,0x63,0xbe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x5c,0x10,0xbf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0xdd,0x1b,0x15}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9c,0x13,0x13,0x5a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9c,0xf1,0x05,0xbe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9d,0x0d,0x3d,0x4c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9d,0x0d,0x3d,0x50}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9d,0xe6,0xa6,0x62}, 14391},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9e,0x4b,0xcb,0x02}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9e,0xb5,0x7d,0x96}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9e,0xb5,0xe2,0x21}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x64,0xf2,0xfe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x64,0xf8,0xea}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x8a,0x57,0x12}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa0,0x10,0x00,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0x00,0xe3,0x36}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0x00,0xe3,0x38}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0x3e,0x12,0xe2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xd1,0x01,0xe9}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xf3,0xaf,0x56}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xf4,0x50,0xd0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xfa,0xbc,0x57}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xfa,0xbd,0x35}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x9e,0xca,0x70}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x9e,0xf3,0xe6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa5,0x49,0x3e,0x1f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa6,0x3e,0x52,0x67}, 32771},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa6,0x46,0x5e,0x6a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa7,0x56,0x5a,0xef}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa9,0x2c,0x22,0xcb}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x5d,0x65,0x49}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x69,0x07,0x2f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0x17,0x67,0x1e}, 8000},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0x35,0x4f,0x06}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0x46,0x0c,0x56}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0x59,0x1c,0x89}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0xb0,0xb8,0x36}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0xd0,0x80,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0xfe,0xcc,0x45}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0xff,0xcc,0x7c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xae,0x5e,0x9b,0xe0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xae,0x72,0x66,0x29}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xae,0x72,0x7c,0x0c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x0a,0xe3,0x3b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x1f,0xe0,0xd6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x4a,0x88,0xed}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x63,0x02,0xcf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x6a,0xbf,0x02}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0xa0,0xe4,0x09}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0xbf,0xb6,0x03}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0xd4,0xb9,0x99}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0xf1,0x89,0xb7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb1,0x26,0xd7,0x49}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x10,0xde,0x92}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x84,0x02,0xf6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x8f,0xbf,0xab}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x94,0xac,0xd1}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x94,0xe2,0xb4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x96,0x60,0x2e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xb6,0xe3,0x32}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xec,0x89,0x3f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xff,0x2a,0x7e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb4,0x96,0x34,0x25}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb5,0x27,0x20,0x63}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb5,0x30,0x4d,0x1a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb5,0x34,0xdf,0x34}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb5,0xee,0x33,0x98}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb7,0x58,0xdf,0xd0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb7,0x6e,0xdc,0xd2}, 30301},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb8,0x5f,0x3a,0xa6}, 8336},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb8,0xa4,0x93,0x52}, 41333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb8,0xab,0xd0,0x6d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0x27}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x19,0x30,0xb8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x74,0x0f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x50,0xdb,0x84}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x55,0x03,0x8c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x5f,0xdb,0x35}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x6c,0xf4,0x29}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x86,0xe9,0x79}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x91,0x80,0x15}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x94,0x03,0xe3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x99,0xc4,0xf0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9e,0x72,0xb8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0xc4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xb5,0xe6,0x4a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xb9,0x1a,0x8d}, 8111},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xba,0xd0,0xa2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xbd,0x84,0xb2}, 57780},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd3,0x3b,0x32}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xe9,0x94,0x92}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xee,0x81,0x71}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xf9,0xc7,0x6a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xfb,0xa1,0x36}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbb,0xbd,0x99,0x88}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x25,0x18,0xbe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x2a,0x28,0xea}, 18333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x3d,0x2e,0x24}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x44,0x2d,0x8f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x7f,0xe5,0x69}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x86,0x06,0x54}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x86,0x08,0x24}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xd6,0x81,0x41}, 20012},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe6,0xa8,0x72}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbd,0x22,0x0e,0x5d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbd,0xcf,0x2e,0x20}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbe,0xd3,0xcc,0x44}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbf,0xd1,0x15,0xbc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x03,0x0b,0x14}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x03,0xb9,0xd2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x41,0xaa,0x0f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x41,0xaa,0x32}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x92,0x89,0x12}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x9d,0xca,0xb2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xe3,0x50,0x53}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x0a,0xcb,0x17}, 8334},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x19,0x06,0xce}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x2a,0x6e,0x1e}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x3a,0xc4,0xd4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x6a,0x1c,0x08}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xbd,0xbe,0x7b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xc2,0xa3,0x23}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xc2,0xa3,0x35}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x0e,0xf6,0xcd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x24,0x5b,0xfd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x7e,0x71,0x87}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x87,0x87,0x45}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x38,0x3f,0x04}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x38,0x3f,0x05}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x43,0x8b,0x36}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x87,0xc2,0x08}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0xca,0xa9,0x95}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0xce,0x69,0x2a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0xd1,0xf9,0xa4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x01,0xe7,0x06}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0xc8,0x2b,0xd7}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc7,0xb6,0xb8,0xcc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc7,0xf7,0x07,0xd0}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc7,0xf7,0xf9,0xbc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc8,0x07,0xfc,0x76}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc8,0x14,0xba,0xfe}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc8,0x53,0xa6,0x88}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x37,0x57,0x2d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x4f,0xa7,0x41}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x6c,0xd3,0x87}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0xa9,0x66,0x49}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcb,0x82,0x30,0x75}, 8885},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcb,0x84,0x5f,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcb,0x97,0xa6,0x7b}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x5d,0x71,0x6c}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x6f,0xf1,0xc3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xce,0x7c,0x95,0x42}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcf,0x73,0x66,0x62}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcf,0xe5,0x2e,0x96}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd0,0x4c,0xfc,0xc6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd0,0x64,0x0d,0x38}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd0,0x64,0xb2,0xaf}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd0,0x6e,0x63,0x69}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0x06,0xd2,0xb3}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0x85,0xdc,0x4a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0x8d,0x39,0x39}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd3,0x1b,0x93,0x43}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x22,0xe1,0x76}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x59,0xad,0xd8}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x63,0xe2,0x24}, 9020},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0xed,0x60,0x62}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x59,0x83,0x35}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0x26,0x81,0xa4}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0x86,0xa5,0x37}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0x92,0xfb,0x08}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xbd,0xbe,0x5f}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xe2,0x80,0xbd}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xec,0xa4,0x52}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x13,0xd8,0xd2}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x1a,0x20,0x0a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x40,0x2f,0x8a}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x40,0x85,0xdc}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x5c,0x37,0xf6}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xda,0x1f,0x71,0xf5}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xda,0xff,0xf2,0x72}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xdc,0x85,0x27,0x3d}, 8333},
- {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xdf,0x10,0x1e,0xaf}, 8333},
- {{0x20,0x01,0x19,0xf0,0x60,0x01,0x30,0x6f,0x0e,0xc4,0x7a,0xff,0xfe,0x8f,0x66,0xec}, 8333},
- {{0x20,0x01,0x1b,0xc0,0x00,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x01}, 8333},
- {{0x20,0x01,0x1c,0x02,0x2f,0x18,0x0d,0x00,0xb6,0x2e,0x99,0xff,0xfe,0x49,0xd4,0x92}, 8333},
- {{0x20,0x01,0x41,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93}, 8333},
- {{0x20,0x01,0x41,0x00,0x00,0x00,0x00,0x64,0xdc,0xaf,0xaf,0xff,0xfe,0x00,0x67,0x07}, 8333},
- {{0x20,0x01,0x04,0x70,0x00,0x0a,0x0c,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x20,0x01,0x48,0x01,0x78,0x19,0x00,0x74,0xb7,0x45,0xb9,0xd5,0xff,0x10,0xa6,0x1a}, 8333},
- {{0x20,0x01,0x4b,0xa0,0xff,0xfa,0x00,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93}, 8333},
- {{0x20,0x01,0x06,0x10,0x19,0x08,0xff,0x01,0xf8,0x16,0x3e,0xff,0xfe,0x33,0x2e,0x32}, 8333},
- {{0x20,0x01,0x06,0x38,0xa0,0x00,0x41,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0x01,0x91}, 8333},
- {{0x20,0x01,0x06,0x48,0x28,0x00,0x01,0x31,0x4b,0x1f,0xf6,0xfc,0x20,0xf7,0xf9,0x9f}, 8333},
- {{0x20,0x01,0x06,0x78,0x07,0xdc,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x20,0x01,0x06,0x78,0x0c,0xc8,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x10,0x00,0x88}, 20008},
- {{0x20,0x01,0x06,0x7c,0x12,0x20,0x08,0x0c,0x00,0x00,0x00,0x00,0x93,0xe5,0x0d,0xd2}, 8333},
- {{0x20,0x01,0x06,0x7c,0x12,0x20,0x08,0x0c,0xe5,0xdc,0xad,0x0c,0x92,0x89,0xc2,0x8f}, 8333},
- {{0x20,0x01,0x06,0x7c,0x16,0xdc,0x12,0x01,0x50,0x54,0x00,0xff,0xfe,0x17,0x4d,0xac}, 8333},
- {{0x20,0x01,0x06,0x7c,0x23,0x54,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22}, 8333},
- {{0x20,0x01,0x06,0x7c,0x26,0xb4,0x00,0x12,0x7a,0xe3,0xb5,0xff,0xfe,0x04,0x6f,0x9c}, 8333},
- {{0x20,0x01,0x06,0x7c,0x02,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xfa}, 8333},
- {{0x20,0x01,0x07,0x18,0x08,0x01,0x03,0x11,0x50,0x54,0x00,0xff,0xfe,0x19,0xc4,0x83}, 8333},
- {{0x20,0x01,0x08,0xd8,0x08,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x99,0x03,0xc1}, 8333},
- {{0x20,0x01,0x08,0xf1,0x14,0x04,0x37,0x00,0x8e,0x49,0x71,0x5a,0x2e,0x09,0xb6,0x34}, 9444},
- {{0x20,0x01,0x0b,0x07,0x5d,0x29,0x99,0xa5,0x19,0x4b,0x38,0x74,0xd6,0x5e,0xa9,0x0d}, 8333},
- {{0x20,0x01,0x0b,0xa8,0x01,0xf1,0xf0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x20,0x01,0x0b,0xc8,0x12,0x00,0x00,0x00,0xda,0xc4,0x97,0xff,0xfe,0x2a,0x35,0x54}, 20008},
- {{0x20,0x01,0x0d,0xa8,0x10,0x0d,0x00,0x22,0x10,0xfa,0xd8,0x5f,0x10,0xf2,0x21,0xfd}, 8333},
- {{0x20,0x01,0x0d,0xa8,0x80,0x01,0x7a,0x39,0xf0,0x35,0x00,0x7d,0xb9,0x9f,0xeb,0x79}, 8333},
- {{0x20,0x01,0x0e,0x42,0x01,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30}, 8333},
- {{0x24,0x00,0x24,0x12,0x01,0x03,0xc9,0x00,0x08,0x25,0x8f,0x20,0xea,0xff,0x65,0xc2}, 8333},
- {{0x24,0x00,0x40,0x52,0x0e,0x20,0x4f,0x00,0x69,0xfe,0xbb,0x33,0x7b,0x1c,0xa1,0xca}, 8333},
- {{0x24,0x01,0x18,0x00,0x78,0x00,0x01,0x05,0xbe,0x76,0x4e,0xff,0xfe,0x1c,0x0b,0x35}, 8333},
- {{0x24,0x01,0x39,0x00,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x01,0x50}, 8333},
- {{0x24,0x01,0xd0,0x02,0x44,0x02,0x00,0x00,0x8f,0x28,0x59,0x1a,0x6e,0xa0,0xc6,0x83}, 8333},
- {{0x24,0x03,0x62,0x00,0x88,0x21,0x3d,0x68,0x19,0x5b,0x87,0xe9,0x68,0x19,0xd5,0xc8}, 8333},
- {{0x24,0x05,0x65,0x80,0x21,0x40,0x3a,0x00,0xc2,0x8c,0x09,0x83,0x36,0x4b,0x5d,0x70}, 8333},
- {{0x24,0x05,0x98,0x00,0xb9,0x11,0xa1,0x8a,0x58,0xeb,0xcd,0x3c,0x9d,0x82,0xea,0x4a}, 8333},
- {{0x24,0x05,0xaa,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40}, 8333},
- {{0x24,0x09,0x00,0x10,0xca,0x20,0x1d,0xf0,0x02,0x24,0xe8,0xff,0xfe,0x1f,0x60,0xd9}, 8333},
- {{0x24,0x09,0x8a,0x1e,0xa9,0xaf,0x36,0x60,0x1c,0x5a,0x5b,0x6b,0x8a,0x2d,0x98,0x48}, 8333},
- {{0x24,0x09,0x8a,0x1e,0xa9,0xaf,0x36,0x60,0x04,0x04,0x39,0xba,0x88,0xf2,0xe8,0xdf}, 8333},
- {{0x24,0x0b,0x00,0x10,0x91,0x41,0x04,0x00,0x49,0xb4,0x3a,0x2e,0x01,0xe5,0x08,0x4c}, 8333},
- {{0x24,0x0d,0x00,0x1a,0x07,0x59,0x60,0x00,0xa7,0xb1,0x45,0x1a,0x88,0x74,0xe1,0xac}, 8333},
- {{0x24,0x0d,0x00,0x1a,0x07,0x59,0x60,0x00,0xdd,0xab,0x31,0x41,0x4d,0xa0,0x88,0x78}, 8333},
- {{0x26,0x00,0x88,0x05,0x24,0x00,0x01,0x4e,0x12,0xdd,0xb1,0xff,0xfe,0xf2,0x30,0x13}, 8333},
- {{0x26,0x01,0x06,0x02,0x8d,0x80,0x0b,0x63,0xdc,0x3e,0x24,0xff,0xfe,0x92,0x05,0xeb}, 8333},
- {{0x26,0x02,0xff,0xb6,0x00,0x04,0x27,0x98,0xf8,0x16,0x3e,0xff,0xfe,0x2f,0x54,0x41}, 8333},
- {{0x26,0x02,0xff,0xb6,0x00,0x04,0x73,0x9e,0xf8,0x16,0x3e,0xff,0xfe,0x00,0xc2,0xb3}, 8333},
- {{0x26,0x02,0xff,0xb8,0x00,0x00,0x00,0x00,0x02,0x08,0x00,0x72,0x00,0x57,0x02,0x00}, 8333},
- {{0x26,0x04,0x13,0x80,0x41,0x11,0x93,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x26,0x04,0x43,0x00,0x00,0x0a,0x00,0x2e,0x02,0x1b,0x21,0xff,0xfe,0x11,0x03,0x92}, 8333},
- {{0x26,0x04,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2e,0x06}, 8112},
- {{0x26,0x04,0x55,0x00,0x70,0x6a,0x40,0x00,0xfc,0x79,0xb9,0xbb,0x01,0xd7,0xc3,0x25}, 8333},
- {{0x26,0x04,0x55,0x00,0xc1,0x34,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc}, 32797},
- {{0x26,0x04,0x68,0x00,0x5e,0x11,0x01,0x62,0x5c,0x8f,0xd2,0xff,0xfe,0x26,0x14,0x6f}, 8333},
- {{0x26,0x05,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50}, 8333},
- {{0x26,0x05,0x64,0x00,0x00,0x20,0x13,0xbf,0xdf,0x1d,0x18,0x1c,0x83,0xbb,0x22,0xe8}, 8333},
- {{0x26,0x05,0xae,0x00,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x03}, 8333},
- {{0x26,0x05,0xc0,0x00,0x2a,0x0a,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02}, 8333},
- {{0x26,0x07,0xf2,0xc0,0xf0,0x0e,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54}, 8333},
- {{0x26,0x07,0xf2,0xf8,0xad,0x40,0x0b,0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x26,0x07,0xf4,0x70,0x00,0x08,0x10,0x48,0xae,0x1f,0x6b,0xff,0xfe,0x70,0x72,0x40}, 8333},
- {{0x26,0x07,0xff,0x28,0x80,0x0f,0x00,0x97,0x02,0x25,0x90,0xff,0xfe,0x75,0x11,0x10}, 8333},
- {{0x26,0x20,0x01,0x1c,0x50,0x01,0x11,0x18,0xd2,0x67,0xe5,0xff,0xfe,0xe9,0xe6,0x73}, 8333},
- {{0x26,0x20,0x00,0x6e,0xa0,0x00,0x20,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06}, 8333},
- {{0x28,0x04,0x01,0x4d,0x4c,0x93,0x98,0x09,0x97,0x69,0xda,0x80,0x18,0x32,0x34,0x80}, 8333},
- {{0x2a,0x00,0x13,0x28,0xe1,0x01,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x63}, 8333},
- {{0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x02,0x15,0x5d,0xff,0xfe,0xd6,0x10,0x33}, 8333},
- {{0x2a,0x00,0x13,0xa0,0x30,0x15,0x00,0x01,0x00,0x85,0x00,0x14,0x00,0x79,0x00,0x26}, 8333},
- {{0x2a,0x00,0x16,0x30,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01}, 8333},
- {{0x2a,0x00,0x17,0x68,0x20,0x01,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x6a}, 8333},
- {{0x2a,0x00,0x18,0x28,0xa0,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66}, 8333},
- {{0x2a,0x00,0x18,0x38,0x00,0x36,0x00,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0xcb}, 8333},
- {{0x2a,0x00,0x18,0x38,0x00,0x36,0x00,0x7d,0x00,0x00,0x00,0x00,0x00,0x00,0xd3,0xc6}, 8333},
- {{0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x58,0xf7,0xe0,0xff,0xfe,0x24,0xa0,0xba}, 22220},
- {{0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x17}, 22220},
- {{0x2a,0x00,0x1f,0x40,0x50,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31}, 8333},
- {{0x2a,0x00,0x60,0x20,0x13,0x95,0x14,0x00,0xba,0xf7,0x2d,0x43,0x60,0xb3,0x19,0x8b}, 8333},
- {{0x2a,0x00,0x7c,0x80,0x00,0x00,0x01,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xaf}, 8333},
- {{0x2a,0x00,0x8a,0x60,0xe0,0x12,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21}, 8333},
- {{0x2a,0x00,0xab,0x00,0x06,0x03,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03}, 8333},
- {{0x2a,0x00,0xbb,0xe0,0x00,0xcc,0x00,0x00,0x62,0xa4,0x4c,0xff,0xfe,0x23,0x75,0x10}, 8333},
- {{0x2a,0x00,0x0c,0xa8,0x0a,0x1f,0x30,0x25,0xf9,0x49,0xe4,0x42,0xc9,0x40,0x13,0xe8}, 8333},
- {{0x2a,0x00,0xd2,0xa0,0x00,0x0a,0x3d,0x00,0x1c,0xdf,0x38,0xbb,0xa7,0xd6,0xc2,0x51}, 8333},
- {{0x2a,0x00,0xd8,0x80,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0e}, 8333},
- {{0x2a,0x00,0x0e,0xc0,0x72,0x07,0x91,0x00,0x5f,0x8f,0x25,0xdd,0x25,0x74,0x39,0x82}, 8333},
- {{0x2a,0x00,0xf8,0x20,0x04,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36}, 8333},
- {{0x2a,0x01,0x01,0x38,0xa0,0x17,0xb0,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42}, 8333},
- {{0x2a,0x01,0x04,0x30,0x00,0x17,0x00,0x01,0x00,0x00,0x00,0x00,0xff,0xff,0x11,0x53}, 8333},
- {{0x2a,0x01,0x04,0x90,0x00,0x16,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x2a,0x01,0x4b,0x00,0x80,0x7c,0x1b,0x00,0xcd,0xa1,0x0c,0x6a,0x2b,0xad,0x24,0x18}, 8333},
- {{0x2a,0x01,0x4b,0x00,0x80,0xe7,0x54,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2a,0x01,0x04,0xf8,0x01,0x92,0x42,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8433},
- {{0x2a,0x01,0x07,0xa0,0x00,0x02,0x13,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03}, 8333},
- {{0x2a,0x01,0x07,0xa7,0x00,0x02,0x14,0x67,0x0e,0xc4,0x7a,0xff,0xfe,0xe2,0x56,0x90}, 8333},
- {{0x2a,0x01,0x07,0xc8,0xd0,0x02,0x01,0x0f,0x50,0x54,0x00,0xff,0xfe,0x5c,0xda,0xc7}, 8333},
- {{0x2a,0x01,0x07,0xc8,0xd0,0x02,0x03,0x18,0x50,0x54,0x00,0xff,0xfe,0xbe,0xcb,0xb1}, 8333},
- {{0x2a,0x01,0x87,0x40,0x00,0x01,0xff,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x8c,0x6a}, 8333},
- {{0x2a,0x01,0xcb,0x00,0x0f,0x98,0xca,0x00,0x50,0x54,0x00,0xff,0xfe,0xd4,0x76,0x3d}, 8333},
- {{0x2a,0x01,0xcb,0x14,0x0c,0xf6,0xbc,0x00,0x21,0xe5,0xf1,0x2e,0x32,0xc8,0x01,0x45}, 8333},
- {{0x2a,0x01,0x00,0xd0,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x45}, 8333},
- {{0x2a,0x01,0x00,0xd0,0xbe,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12}, 8333},
- {{0x2a,0x01,0x0e,0x35,0x2e,0x40,0x68,0x30,0x02,0x11,0x32,0xff,0xfe,0xa6,0xde,0x3d}, 8333},
- {{0x2a,0x02,0x12,0x05,0xc6,0xaa,0x60,0xc0,0x70,0xd8,0xaa,0xee,0xa8,0x2d,0x99,0x3c}, 8333},
- {{0x2a,0x02,0x01,0x69,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14}, 8333},
- {{0x2a,0x02,0x01,0x80,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x5b,0x8f,0x53,0x8c}, 8333},
- {{0x2a,0x02,0x03,0x48,0x00,0x62,0x5e,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2a,0x02,0x03,0x90,0x90,0x00,0x00,0x00,0x02,0x18,0x7d,0xff,0xfe,0x10,0xbe,0x33}, 8333},
- {{0x2a,0x02,0x7a,0xa0,0x16,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xdc,0x8d,0xe0}, 8333},
- {{0x2a,0x02,0x7b,0x40,0xb0,0xdf,0x89,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2a,0x02,0x7b,0x40,0xb9,0x05,0x37,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2a,0x02,0x81,0x0d,0x8c,0xbf,0xf3,0xa8,0x96,0xc6,0x91,0xff,0xfe,0x17,0xae,0x1d}, 8333},
- {{0x2a,0x02,0x83,0x89,0x01,0xc0,0x96,0x80,0x02,0x01,0x2e,0xff,0xfe,0x82,0xb3,0xcc}, 8333},
- {{0x2a,0x02,0xa4,0x54,0xa5,0x16,0x00,0x01,0x05,0x17,0x09,0x28,0x7e,0x0d,0x95,0x7c}, 8333},
- {{0x2a,0x02,0x0a,0xf8,0xfa,0xb0,0x08,0x04,0x01,0x51,0x02,0x36,0x00,0x34,0x01,0x61}, 8333},
- {{0x2a,0x02,0x0a,0xf8,0xfa,0xb0,0x08,0x08,0x00,0x85,0x02,0x34,0x01,0x45,0x01,0x32}, 8333},
- {{0x2a,0x02,0x0e,0x00,0xff,0xf0,0x01,0xe2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a}, 8333},
- {{0x2a,0x03,0x22,0x60,0x30,0x06,0x00,0x0d,0xd3,0x07,0x5d,0x1d,0x32,0xca,0x1f,0xe8}, 8333},
- {{0x2a,0x03,0x60,0x00,0x08,0x70,0x00,0x00,0x00,0x46,0x00,0x23,0x00,0x87,0x02,0x18}, 8333},
- {{0x2a,0x03,0x9d,0xa0,0x00,0xf6,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x2a,0x03,0xc9,0x80,0x00,0xdb,0x00,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 8333},
- {{0x2a,0x03,0xe2,0xc0,0x01,0xce,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x2a,0x04,0x35,0x44,0x10,0x00,0x15,0x10,0x70,0x6c,0xab,0xff,0xfe,0x6c,0x50,0x1c}, 8333},
- {{0x2a,0x04,0x52,0xc0,0x01,0x01,0x03,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0x87}, 8333},
- {{0x2a,0x04,0x52,0xc0,0x01,0x01,0x03,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0x4c,0x27}, 8333},
- {{0x2a,0x04,0xee,0x41,0x00,0x83,0x50,0xdf,0xd9,0x08,0xf7,0x1d,0x2a,0x86,0xb3,0x37}, 8333},
- {{0x2a,0x05,0x6d,0x40,0xb9,0x4e,0xd1,0x00,0x02,0x25,0x90,0xff,0xfe,0x0d,0xcf,0xc2}, 8333},
- {{0x2a,0x05,0xe5,0xc0,0x00,0x00,0x01,0x00,0x02,0x50,0x56,0xff,0xfe,0xb9,0xd6,0xcb}, 8333},
- {{0x2a,0x05,0xfc,0x87,0x00,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 8333},
- {{0x2a,0x05,0xfc,0x87,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08}, 8333},
- {{0x2a,0x07,0x57,0x41,0x00,0x00,0x11,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2a,0x07,0xa8,0x80,0x46,0x01,0x10,0x62,0xb4,0xb4,0xbd,0x2a,0x39,0xd4,0x7a,0xcf}, 51401},
- {{0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x09,0x46}, 8333},
- {{0x2a,0x07,0xb4,0x00,0x00,0x01,0x03,0x4c,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x02}, 8333},
- {{0x2a,0x0a,0x8c,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb4}, 8333},
- {{0x2a,0x0a,0xc8,0x01,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x83}, 8333},
- {{0x2a,0x0b,0xae,0x40,0x00,0x03,0x4a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15}, 8333},
- {{0x2a,0x0f,0xdf,0x00,0x00,0x00,0x02,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46}, 8333},
- {{0x2c,0x0f,0xf5,0x98,0x00,0x05,0x00,0x01,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0x2c,0x0f,0xfc,0xe8,0x00,0x00,0x04,0x00,0x0b,0x7c,0x00,0x00,0x00,0x00,0x00,0x01}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd6,0xbc,0x4a,0x3c,0x6d,0x03,0xa9,0x4e,0x1f,0x55}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd6,0x8f,0xf0,0xf8,0xbb,0x10,0x00,0x18,0x42,0x54}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd6,0xec,0x32,0xc1,0x59,0x9c,0xd8,0x46,0xd5,0x48}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd6,0xf0,0x8d,0x96,0x37,0xc3,0x27,0x61,0x9a,0x24}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd0,0x12,0xe6,0xed,0x8e,0xc1,0x78,0x8d,0x1c,0x21}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd0,0x4a,0xc5,0xbd,0x5d,0xe9,0xca,0x57,0xaf,0xc4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd0,0x94,0xc0,0x97,0xd2,0x32,0xed,0x81,0x92,0x67}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd1,0xd5,0x49,0x23,0xa6,0x10,0x01,0x49,0xda,0x05}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd2,0x2a,0x76,0x2c,0x37,0x09,0x9a,0xa1,0x61,0x4b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd3,0x19,0x77,0x50,0xf5,0xf3,0x48,0x17,0x59,0x50}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd4,0x24,0xda,0xf8,0x97,0x6d,0x28,0x80,0x47,0xf9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd4,0x28,0x30,0x9d,0x6d,0xac,0x1e,0xb4,0x6e,0x59}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd5,0x13,0x71,0x95,0xd5,0x2e,0x12,0xf6,0x0e,0x6e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd5,0xc6,0x62,0x50,0xb1,0x22,0xb6,0x4a,0x31,0x56}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd5,0xc7,0x99,0x46,0x87,0x91,0x13,0xc9,0xc9,0x16}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdf,0x22,0x06,0xea,0xce,0x87,0x08,0x09,0x32,0x52}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdf,0xa1,0xf5,0x1c,0xe4,0x4e,0x97,0x71,0xee,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdf,0xf7,0x64,0x8e,0x4f,0xa3,0xbb,0xaa,0x4f,0x30}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdf,0xfc,0xa5,0x92,0x7d,0xbc,0x03,0x13,0x69,0x35}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdf,0xd5,0x61,0xfc,0xb7,0x73,0xff,0xef,0x2f,0xaa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd9,0x3b,0x3f,0x9e,0x1c,0x02,0xe7,0xd9,0xba,0xb7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd9,0x83,0x73,0x90,0x25,0x3b,0xa9,0x4b,0x18,0x5b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xd9,0xcc,0x14,0xe4,0x9a,0x68,0x6d,0x8a,0x12,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xda,0x29,0x4a,0xc4,0x7a,0xb0,0x0e,0x0d,0x0a,0xee}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xda,0x67,0x7a,0x24,0x60,0x45,0x8f,0xe4,0x2e,0x74}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xda,0xfa,0x48,0x68,0x74,0xfb,0x2b,0x21,0x27,0x80}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdb,0x5c,0x56,0x99,0xb0,0x5c,0x08,0x43,0xb7,0xee}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdc,0x79,0xc1,0x8f,0x29,0x44,0xf2,0xdc,0x00,0xf6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xdd,0x66,0x1a,0x59,0x93,0x73,0x7f,0x58,0x76,0x19}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe7,0x9c,0x7c,0xce,0x79,0xe3,0xc8,0xa4,0x73,0x66}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe7,0xca,0xbd,0xa2,0xab,0xe5,0x7b,0xe4,0xca,0x71}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe7,0xd1,0xe8,0x45,0x7a,0x42,0x60,0x2b,0x2c,0xde}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe7,0xe9,0x47,0x9b,0x22,0x6c,0x6c,0x03,0xba,0x6e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe0,0xba,0x25,0x23,0x7f,0x25,0x5c,0x51,0xcb,0xc3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe1,0x21,0xbf,0x26,0x37,0xfd,0xe9,0x89,0x95,0xe2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe1,0x2c,0xa1,0xde,0xa2,0x37,0x7e,0x01,0xc5,0xa8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe1,0x57,0x53,0x20,0x2d,0x66,0x9a,0xb1,0xed,0xa0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe2,0x00,0xe6,0xcf,0x0c,0xe7,0xd0,0xc0,0x58,0x9c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe2,0x6f,0x9d,0xfd,0xce,0xa7,0x40,0x6f,0xfb,0x62}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe3,0x1a,0xaa,0xa7,0xc7,0x07,0xf6,0x48,0x34,0x2a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe3,0x5b,0x4c,0x5d,0x9d,0x57,0x66,0xbc,0x26,0x1b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe3,0x73,0xac,0x1b,0x82,0x6b,0xa6,0x4d,0x91,0x3f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe3,0xdd,0x9b,0x1f,0xdd,0xf7,0x30,0x6c,0x8c,0x6a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe4,0x0c,0x50,0xf7,0xd1,0xab,0xc2,0xc2,0x4a,0xff}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe4,0x64,0x0b,0xeb,0x73,0x04,0x33,0x66,0x21,0x89}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe5,0x3a,0x9e,0x83,0x1e,0x88,0x24,0xeb,0x4f,0x8c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe5,0x5d,0x1a,0xcd,0xd8,0x21,0x8f,0xcc,0x86,0xb1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xee,0xa5,0xc4,0xf5,0xeb,0x1d,0x96,0xfc,0x9e,0x76}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe8,0x02,0xf4,0x22,0x05,0xa9,0x14,0xe2,0x26,0x2e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe8,0x30,0x3c,0xde,0xfe,0x4e,0x1d,0x9d,0xb4,0x99}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe8,0xaf,0x91,0xca,0x33,0x72,0xba,0x33,0x3b,0x80}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe9,0x07,0x44,0x29,0xf4,0x1a,0x09,0xb4,0xe2,0x25}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe9,0x1e,0x40,0x15,0x4c,0xc0,0x38,0x5a,0xf4,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe9,0x71,0x75,0xe6,0x68,0x16,0xe7,0xe6,0xba,0x79}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe9,0xc7,0xe2,0x60,0x96,0xee,0x02,0xd8,0x78,0xc1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xea,0x70,0x5c,0x9e,0xca,0x90,0x7d,0x48,0xc5,0xfa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xeb,0x5c,0xe8,0x18,0x53,0xef,0xbe,0x83,0x77,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xeb,0x64,0x56,0x71,0xb0,0x86,0x72,0xf8,0xa6,0x2f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xeb,0xa1,0x29,0xde,0x4f,0xc9,0xd6,0x64,0x90,0xbe}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xeb,0xf3,0x96,0x0f,0x93,0x2b,0x9b,0x18,0x64,0x3a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xec,0x84,0xa6,0x5f,0x98,0xa0,0x82,0xd7,0xf1,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xed,0x09,0xfb,0x3a,0x39,0x0b,0x7c,0x77,0x37,0x64}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xed,0xae,0x7b,0xea,0x6e,0xcb,0xdd,0x52,0xfb,0x3b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xed,0xd5,0xbc,0x51,0xbb,0xf1,0x37,0xa2,0x6f,0x88}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xee,0x4c,0x79,0xe8,0xdf,0xa8,0xa4,0x07,0xa3,0xdd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf6,0x86,0x21,0xbe,0xa3,0x72,0xcb,0x95,0x0f,0x2b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf6,0xc2,0xa7,0x69,0x87,0x45,0xda,0xdd,0x07,0xe3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf7,0xce,0x9a,0x96,0xbe,0xb2,0x05,0x30,0x2d,0x9d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf1,0xbc,0xa7,0x71,0x4b,0x51,0x7a,0x09,0xac,0x68}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf2,0xbb,0x98,0x90,0x97,0xb7,0x04,0x01,0xdd,0x1d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf2,0x8b,0xd0,0x60,0xeb,0x79,0x1b,0x8b,0x18,0x12}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf3,0x00,0x83,0x5d,0x35,0x11,0x27,0xc7,0xa2,0x64}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf4,0x49,0x29,0x57,0x83,0xab,0xd6,0x1e,0xa0,0xe7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf4,0x52,0x4b,0xf8,0xd8,0xa0,0x28,0x8d,0x8b,0xa4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf4,0x94,0x66,0x97,0x9b,0x7b,0xce,0x3a,0xa6,0x80}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf4,0xa7,0x70,0x38,0x74,0xb2,0x24,0x6e,0xca,0x07}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf5,0xe1,0x8e,0x4e,0x5e,0x0b,0xbd,0x4e,0x8c,0xcc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf8,0x2a,0xd5,0xec,0x70,0x79,0xa9,0xad,0xa6,0xa0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf9,0x4b,0xcb,0x2b,0x5e,0xf3,0x5d,0xad,0xce,0xed}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xf9,0xd0,0xf0,0xd3,0x25,0x18,0xb1,0x98,0x29,0x46}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xfc,0x92,0xc5,0xe6,0x33,0x3a,0x56,0xf2,0xe0,0x6a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xfc,0xe9,0x3d,0xe6,0x7a,0x02,0xad,0x16,0x5b,0xd7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xfd,0x0f,0x24,0xe5,0x3e,0x6d,0xf6,0x32,0xb6,0xf3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xfd,0x99,0xcb,0x49,0xdb,0xb5,0x41,0x3b,0xb4,0x33}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xfe,0x14,0xcc,0xd3,0x01,0xb0,0xf4,0xf9,0xe4,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x06,0xbe,0x1a,0x9d,0x0d,0x07,0x31,0xad,0xa6,0xee}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x07,0x77,0x59,0x8d,0x9f,0xa2,0x09,0x3e,0xd4,0x6b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x07,0x7d,0xdf,0xea,0xe9,0xa3,0x8a,0xd9,0xe8,0x6f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x00,0x5f,0xae,0xa9,0xa8,0x28,0xe4,0xd1,0x6a,0x35}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x01,0x0b,0x7f,0xd0,0x39,0x78,0x17,0xf1,0x2c,0x0a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x02,0x3b,0x1d,0x0d,0x0e,0xcb,0x89,0xf8,0xc4,0x79}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x02,0x1f,0x47,0xbc,0xe8,0x9e,0xc8,0xd3,0x19,0xe9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x02,0xcc,0xf4,0xa7,0x06,0x1e,0xcd,0x36,0xb1,0xef}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x02,0xd0,0x7a,0x03,0xf1,0x3e,0x05,0xce,0xe8,0xf1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x03,0x36,0x6c,0x60,0xb8,0x6d,0xf3,0x6c,0x5c,0xf7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x03,0x54,0xec,0xe4,0xa7,0x5e,0xa3,0xba,0x0b,0xd4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x04,0xf7,0x3b,0x25,0x61,0x98,0xb4,0xb8,0x36,0x1d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x05,0x60,0xe0,0xaf,0xfa,0x7b,0x05,0xee,0x0f,0x08}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x05,0x98,0x3c,0xe8,0xb2,0xd8,0x7a,0x7e,0xd2,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x06,0x31,0x67,0xa3,0x1f,0xf8,0x69,0x31,0xa6,0x29}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0e,0x91,0xb7,0xa7,0xe2,0xd7,0x05,0x57,0xc6,0x5f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0f,0x10,0xb2,0x07,0x17,0x15,0x3c,0xd9,0xcd,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0f,0x2b,0x55,0x06,0x08,0x78,0x98,0xab,0x3f,0x95}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x08,0xc6,0x58,0x5d,0xf2,0xea,0x02,0x3d,0x96,0x76}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x09,0x3a,0x13,0x09,0xee,0xe3,0x9d,0x4b,0xf6,0x18}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x09,0x96,0x0e,0x33,0xd9,0x24,0xeb,0x3a,0xfd,0x72}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x09,0xf7,0xa3,0x66,0xdb,0x6e,0x04,0xac,0xc2,0x93}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x09,0xdd,0xc5,0x38,0x6f,0x21,0xdb,0xfb,0xc7,0x77}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0a,0x26,0x27,0x21,0xbc,0x8a,0xca,0x0e,0x5a,0x17}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0a,0x2d,0xf9,0x79,0x25,0xf4,0x74,0xc2,0xec,0x54}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0a,0xbf,0x87,0xf8,0x8f,0x6b,0x04,0xb5,0xc3,0xfa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0a,0xc4,0xa9,0xc4,0xd5,0x27,0x6a,0x49,0xa6,0x4a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0a,0xec,0x17,0xfc,0xc5,0x19,0x4a,0x39,0x5f,0x86}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0b,0x6e,0xdf,0x42,0x02,0xef,0x4d,0x56,0xf5,0xcf}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0b,0xfe,0xed,0x69,0x75,0x12,0x41,0x62,0x2e,0xb5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0c,0x21,0x88,0x50,0x46,0x4f,0x26,0x23,0xb7,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x0d,0x47,0x96,0x52,0x62,0x81,0x7e,0x6c,0xe5,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x16,0xfd,0x96,0x10,0xc9,0x52,0x1a,0x59,0xb2,0x65}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x17,0x0a,0xdf,0x68,0xcd,0x5c,0xd6,0x68,0xbe,0x75}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x10,0x00,0x45,0xf7,0x04,0x1d,0x50,0xe7,0x43,0x2a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x10,0x21,0xde,0x00,0x2b,0x28,0x62,0xda,0x30,0x63}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x11,0x22,0xd8,0xb2,0x2a,0xee,0x5c,0xcc,0xbb,0x2d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x11,0xe2,0x8f,0x22,0x66,0x48,0x00,0x67,0x17,0x93}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x13,0x45,0x64,0x2b,0x73,0x68,0xf4,0x44,0xb3,0xb9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x15,0x30,0x98,0x3b,0x28,0x23,0x04,0xcb,0x02,0xeb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x15,0xff,0x00,0x68,0xcf,0x86,0x1f,0xf7,0xac,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x16,0x5f,0xfb,0x18,0x14,0x97,0x0d,0x54,0x3b,0xfa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1e,0x8a,0xde,0xf2,0x25,0xc2,0x46,0x06,0x99,0x1c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1e,0xa4,0xae,0x76,0x9e,0x10,0x3d,0xcc,0x12,0x07}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1e,0xc0,0xeb,0x31,0xa6,0xaa,0xa7,0x2c,0xa0,0x04}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1f,0x51,0x4e,0x01,0x19,0xde,0x34,0xa3,0x08,0xc9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1f,0xb2,0x1b,0x6a,0x57,0x6d,0xcc,0x9e,0xca,0xbb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x18,0x7b,0x11,0xf4,0x9c,0xf4,0xfe,0xc3,0x21,0xa8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x18,0x91,0xa3,0x51,0x6e,0x8a,0xf9,0xcc,0x27,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x18,0xdf,0x33,0xe9,0x96,0x9e,0xe3,0x2a,0xb9,0xc6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x19,0x63,0x6c,0x83,0xe5,0x11,0x04,0xa6,0xb5,0x92}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1a,0x6c,0x74,0x95,0x3c,0x89,0xf6,0xec,0xef,0x09}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1a,0x95,0xd6,0x31,0xe4,0xea,0x66,0x97,0x0d,0x5d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1b,0x93,0xbc,0x99,0x92,0x0e,0x69,0x16,0x40,0xcf}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1b,0xc4,0x4e,0x17,0x71,0x14,0x06,0x3c,0x86,0xfd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1c,0x6c,0xed,0xd5,0xb7,0x11,0xfa,0xec,0x94,0x2e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1d,0x10,0xa5,0x20,0x77,0x43,0xf6,0xbc,0x12,0xed}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1d,0x20,0x35,0xa1,0xf3,0x16,0xb4,0x8f,0x1c,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1d,0x30,0xfe,0x09,0xc7,0xe8,0xfe,0xd3,0xee,0x83}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1d,0x33,0xd9,0xd9,0xdb,0xcf,0xc5,0xde,0xae,0xe9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1d,0x69,0xe1,0xac,0x11,0xf1,0x32,0x2f,0x5c,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1e,0x3d,0x98,0x4b,0x9e,0xc0,0x96,0x40,0x63,0x0f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x1e,0x75,0x81,0xb1,0x3b,0xc4,0x22,0x26,0x72,0x3f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x26,0x83,0xa0,0x76,0x54,0xa8,0xc1,0x6c,0xde,0x83}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x26,0xf6,0x7e,0xfd,0x3a,0x25,0x94,0xa8,0x49,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x27,0x54,0x94,0x03,0x1f,0x7e,0x53,0xd8,0x3f,0x35}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x27,0xd0,0xa7,0x73,0x43,0xd5,0xb2,0x26,0x57,0x1c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x20,0x3c,0x17,0x1f,0x8a,0x74,0xe1,0xdf,0x5a,0x5d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x21,0x47,0x7f,0x18,0x5c,0x97,0x49,0x9c,0x40,0x86}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x21,0x62,0xfa,0x51,0x02,0xf5,0x14,0x4c,0x40,0x52}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x21,0xa3,0x41,0x6c,0x28,0xda,0x27,0x1a,0x78,0xd0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x24,0x45,0xe9,0xa6,0x5a,0xa0,0xb0,0x01,0xaf,0x5b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x25,0x09,0xa6,0xf6,0x4a,0xec,0xd5,0x33,0x74,0x35}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x26,0x55,0x1f,0xca,0x70,0xe5,0xbe,0xe3,0xa6,0x33}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2e,0xdb,0x8c,0x24,0x20,0xf2,0x9f,0x7c,0xb4,0xea}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x28,0x21,0xfd,0xd5,0x3c,0x78,0xa5,0xfd,0xcc,0xf4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x28,0xeb,0x35,0xa7,0x6f,0x90,0x83,0x7a,0x1f,0xfd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x29,0x86,0xfb,0xba,0xbc,0x6e,0x6f,0x53,0x89,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2a,0x25,0x08,0x7a,0xb9,0x56,0xd9,0xe9,0xeb,0x5d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2a,0x8c,0xfd,0xc2,0xc4,0x30,0x05,0x11,0xe8,0x29}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2b,0xb7,0x31,0x96,0xd7,0xd7,0xe6,0x05,0x42,0x1d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2c,0x15,0x79,0x88,0xf6,0xc3,0xd1,0x27,0xa9,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2c,0x28,0xda,0x1d,0x76,0xa8,0xff,0x18,0x78,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2c,0x6d,0x3e,0xb2,0x42,0x7e,0x0e,0x8a,0x59,0xe4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2c,0xc1,0xc3,0x15,0x28,0xa5,0x7c,0x5d,0x2c,0x9a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2d,0x1d,0x8d,0x21,0xf4,0x84,0x61,0x62,0x74,0x45}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x2e,0x7c,0xd9,0x21,0x3e,0x4a,0x31,0x4b,0x2e,0x42}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x36,0xea,0xb6,0x80,0x00,0x71,0xbb,0x23,0x51,0x1d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x37,0x38,0x8f,0x26,0xd2,0xa4,0xd5,0x66,0x49,0xf9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x37,0x7b,0x3f,0x74,0x7d,0x12,0x92,0x8b,0x89,0xb6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0x12,0x3f,0x13,0x11,0x5e,0xa1,0x65,0x15,0x86}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0x57,0x42,0x6c,0xf1,0xee,0xdf,0xc3,0x46,0xff}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0x5f,0x17,0x76,0x79,0x1d,0x11,0x42,0x97,0x95}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0xb8,0xbd,0xce,0x0b,0xde,0xa0,0x72,0x99,0x88}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0x9a,0xb7,0x46,0xb3,0x7e,0x05,0x40,0x24,0x5e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x30,0xe4,0x80,0xe9,0xaa,0xd1,0x08,0xe4,0x0c,0xc2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x31,0x3a,0x66,0x7c,0x5e,0xb7,0xf0,0x03,0xbf,0x3f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x31,0x0c,0x29,0x90,0x84,0x7f,0x05,0x62,0xcd,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x31,0x5d,0x88,0x82,0x83,0x35,0x7b,0x04,0x8d,0x54}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x31,0x9e,0x1a,0x61,0xec,0xb9,0x91,0xaf,0x2c,0x5e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x31,0xe0,0x8a,0xe0,0x9f,0x11,0x44,0xa4,0x49,0xb3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x32,0x22,0x05,0x5d,0xcc,0x69,0x3a,0x50,0xe3,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x32,0xf3,0xd3,0x15,0x5b,0xdc,0xe9,0x43,0x75,0xa4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x33,0xd6,0x09,0xdd,0xd8,0x37,0x5b,0x75,0xf6,0x29}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x34,0x50,0xf5,0xf6,0xe9,0xb6,0x34,0x31,0x47,0xc2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x34,0xce,0x7c,0xad,0x90,0x12,0x35,0xa6,0xde,0x34}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x34,0xdd,0xa1,0xfb,0x92,0xb3,0xa4,0x56,0x2b,0xc2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x35,0x00,0x24,0x34,0x98,0xee,0x98,0x61,0x05,0xfa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x35,0x95,0x33,0x45,0x93,0xb2,0xbc,0xda,0xf6,0x42}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x35,0x9d,0x76,0xb9,0x43,0x15,0x85,0xf3,0xe3,0x8f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3e,0xf7,0xb2,0xf2,0x0d,0xb1,0x3e,0xc8,0xe1,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3f,0x81,0xbd,0x37,0x81,0x58,0x6d,0x6c,0x37,0x83}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0x0b,0x69,0xc4,0x2e,0x74,0xb2,0xe2,0x30,0x2c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0x6c,0x73,0x48,0x3b,0x21,0x10,0xd6,0xc7,0xd3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0xab,0xe2,0xba,0xe7,0xeb,0x15,0xf2,0x9c,0x3d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0xfc,0x75,0x4c,0x4b,0xf5,0x80,0xcc,0xaf,0x2c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0xc1,0xe6,0x48,0x1c,0xaf,0x23,0x3f,0xfc,0xd7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x38,0xcc,0xdb,0xaa,0x90,0x90,0xfd,0x64,0xda,0xd7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x39,0x8f,0xb0,0x65,0xbb,0x21,0x24,0x31,0xd4,0x46}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x39,0xf1,0x7a,0x78,0x36,0x52,0x48,0x52,0x25,0xd9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3a,0x32,0xdf,0x45,0x8e,0x2c,0x8d,0xba,0x3d,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3a,0x61,0x7b,0xcb,0x1a,0x74,0x88,0xc2,0xd4,0x95}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3a,0x82,0xff,0xb0,0x26,0xb7,0x94,0xb5,0xcb,0x92}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3a,0xdc,0x9a,0x59,0x16,0x0a,0x9c,0x9e,0x28,0x79}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3a,0xf3,0x79,0x26,0x3f,0x70,0x77,0x0c,0xe6,0x10}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3b,0x51,0x4c,0xbd,0x64,0xc9,0x03,0x83,0xd7,0xe0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3b,0x9a,0x32,0x59,0x49,0xe4,0xb9,0x11,0x8a,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3c,0x17,0xd6,0xd7,0xd5,0x38,0x88,0x81,0xec,0x2d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3c,0x9e,0x97,0x7d,0x90,0x8c,0x49,0xd3,0x62,0xf1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3d,0x3d,0xc9,0x69,0x83,0x8e,0xef,0xfc,0x5d,0x40}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3d,0x6d,0x58,0x6a,0x56,0x54,0x2d,0xb8,0x57,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3d,0x9d,0xa0,0xa3,0x0d,0x1c,0x63,0x57,0xaf,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x3e,0x6e,0x9d,0x8e,0x67,0xde,0x35,0x79,0xf3,0xae}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x46,0xe8,0x5b,0xd2,0xdb,0x9f,0xc5,0x72,0x8d,0xf0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x40,0x36,0xdd,0xc3,0xb4,0xe7,0x4d,0x57,0xdf,0xe0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x40,0x8a,0x69,0x6c,0xa2,0x98,0x94,0x3e,0x60,0x8e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x40,0x9f,0x9f,0x4c,0xf0,0xa8,0xd2,0x2b,0x2e,0xa1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x41,0x77,0xac,0xbb,0xb4,0xe3,0x0e,0x3a,0x34,0xa3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x41,0xb8,0xb3,0x52,0x0b,0xf5,0x6e,0xa0,0xb1,0x91}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x41,0xce,0x28,0xfc,0xa7,0x16,0x60,0x30,0x0b,0x98}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x42,0x59,0xa9,0xe2,0xee,0x0f,0xea,0xaa,0x83,0x39}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x43,0xf6,0xfa,0x52,0x06,0x3d,0x18,0x5c,0xf6,0xd6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x44,0x36,0xa2,0x4f,0xfa,0x2e,0xf1,0xa2,0xc5,0xe6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x44,0x00,0x69,0xf4,0x4e,0xe0,0xe7,0xf3,0xf8,0xe5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x45,0x0d,0x6e,0x69,0x07,0xf1,0xdf,0x18,0x47,0x5e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x45,0x4b,0xff,0xf2,0xbc,0x9f,0xd5,0xed,0xa3,0xc3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x45,0x4a,0x01,0x0c,0xbf,0x12,0x0d,0xac,0xeb,0x1a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x45,0x65,0x71,0xd9,0x54,0xeb,0x8d,0xac,0xa7,0x8b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x45,0xec,0x68,0x9c,0x0a,0x5d,0x69,0xc3,0x79,0xdf}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x46,0x7b,0xe6,0x39,0xde,0x62,0x9f,0xb3,0x7e,0xee}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4e,0x84,0xfe,0xb2,0x96,0xea,0x76,0xba,0x30,0x57}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4e,0x97,0x15,0x46,0xd4,0x32,0xc7,0x62,0x5a,0xd2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4e,0xa1,0x36,0x28,0x7a,0x18,0x02,0xb9,0x4b,0x3c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4f,0x49,0xac,0x50,0x0d,0xef,0xeb,0xa3,0xf4,0x8b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4f,0x52,0x58,0x8e,0x67,0x84,0xfa,0x6d,0x76,0xf9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4f,0x56,0xad,0x52,0xba,0x0c,0x9e,0x58,0x5c,0xaa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x48,0x1b,0x5a,0xe6,0x4c,0xc8,0xa4,0x9d,0x95,0x0b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x49,0x1a,0xdd,0x4d,0x98,0x5e,0xef,0x70,0x45,0x90}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x49,0x5c,0x4e,0x97,0x52,0x16,0x5c,0x92,0xbf,0x7a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x49,0x84,0x64,0x79,0x5a,0x7d,0xdc,0xe4,0x76,0x1b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x49,0xc0,0xd0,0x6b,0x92,0xd8,0xf2,0xa4,0x4f,0x2f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4a,0x26,0x6a,0x2c,0x3a,0xe3,0x2a,0x58,0x44,0x66}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4a,0x69,0x5b,0x05,0x25,0xca,0xd2,0xc6,0xfe,0x7b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4a,0xc4,0x57,0x30,0xd1,0xed,0xca,0x4b,0x81,0x05}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4a,0xc8,0x79,0x7b,0x01,0x0e,0xbd,0x05,0xb5,0xa0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4a,0xd3,0x9c,0xf2,0x6c,0x0c,0x23,0x78,0x6e,0x1d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4b,0x85,0xc7,0x40,0x44,0x20,0xd4,0x6f,0xfe,0xa5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4c,0x7b,0x8f,0x35,0x34,0x08,0x83,0x5f,0x1b,0x7f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4c,0x5c,0x07,0x4b,0xcb,0x07,0x2a,0x82,0x1d,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4c,0x98,0xf3,0x99,0x40,0xc7,0xd0,0x83,0x85,0x51}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4c,0xd5,0x26,0xb9,0x54,0x90,0x72,0xc9,0x7e,0xcb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4d,0x3a,0x3a,0x3b,0x71,0xf3,0xfc,0x34,0x65,0xa2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4d,0xbd,0x9c,0x32,0xe2,0x69,0x02,0x03,0xd2,0x89}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4d,0xc0,0xba,0x9c,0xbf,0xb7,0xec,0x4a,0xc3,0x36}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4e,0x32,0x72,0x6d,0x06,0xe7,0x10,0x25,0x62,0x41}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x4e,0x49,0xea,0x29,0xbc,0x40,0xe2,0x7e,0x70,0x8e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x57,0x0c,0xb7,0x4d,0x77,0x6b,0x27,0x30,0xf8,0x53}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x57,0xe9,0x8d,0xa2,0xcc,0xa9,0xa9,0x9c,0x18,0x7a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x52,0xf5,0xb7,0x14,0x06,0xdd,0x14,0x1f,0x1e,0xeb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x53,0x95,0x3d,0x42,0x3e,0x1f,0x1e,0xcc,0x07,0x43}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x53,0xc0,0xba,0x6c,0xfd,0xc0,0xd4,0xe0,0x22,0xb2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x54,0x46,0xf0,0x8e,0xb3,0x85,0xba,0x2e,0xac,0x84}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x54,0x51,0x6f,0x2b,0x29,0xc8,0x23,0x93,0x07,0x66}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x54,0x5f,0xa9,0x9c,0x4c,0xb4,0x5f,0x27,0x50,0x9e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x55,0x71,0x51,0xd9,0x36,0x98,0x09,0xd6,0x3b,0xff}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x56,0x23,0x78,0xa3,0xb1,0x0c,0x7c,0x87,0xd2,0x32}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x56,0x76,0xeb,0x9b,0xff,0xe7,0x47,0x79,0xfb,0x50}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5e,0xed,0xd2,0x89,0x48,0xd5,0x83,0x17,0x6a,0x01}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5f,0x38,0x14,0x4a,0x97,0x39,0xff,0x12,0x07,0xb0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5f,0x7d,0xd3,0x77,0x5b,0x23,0x12,0x40,0xd2,0x49}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5f,0xad,0xd5,0x0c,0x88,0x35,0xa4,0x66,0x97,0xb3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5f,0xc1,0xc2,0x32,0x38,0x2d,0xd4,0x93,0x31,0x81}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5f,0xe4,0xb7,0x48,0x49,0x84,0x02,0x82,0x8a,0x56}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x58,0x00,0x54,0xc2,0xb3,0x71,0xbe,0x34,0x95,0x7a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x58,0x0f,0xef,0xf9,0x57,0x09,0x82,0x6b,0x6e,0x9a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x58,0x61,0xa0,0x7d,0xed,0x7b,0x2a,0x8b,0x6a,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x58,0xb9,0x66,0xbe,0x0b,0xd7,0xeb,0x86,0x23,0x7d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x58,0xdc,0x52,0x84,0xaf,0x56,0xd3,0xe1,0x7f,0x1f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x59,0x0e,0xf6,0x19,0x6a,0x45,0x5c,0x18,0x6a,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x59,0x89,0x67,0xa7,0x3f,0x41,0x3e,0x30,0x42,0x11}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x59,0x95,0x50,0xd6,0x2e,0xf7,0xd2,0xe6,0x3a,0x56}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5a,0x2d,0xdc,0xf1,0xa6,0x40,0xbc,0x1f,0xd5,0xb5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5a,0x65,0xf3,0x5a,0x2c,0x66,0x41,0xe8,0x78,0xc0}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5b,0x2a,0x0b,0xec,0x9e,0x05,0x81,0x7a,0x9e,0x08}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5c,0x73,0xff,0x8e,0xc5,0xfe,0x21,0xc1,0x19,0xb3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5d,0x41,0xde,0x3d,0xa1,0x86,0x9b,0x26,0x27,0x11}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5d,0xc5,0xaa,0x3c,0xf7,0xc6,0x2e,0x55,0x9d,0xa5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5e,0x13,0x80,0x8e,0x3c,0x3b,0x13,0xb0,0xc0,0x01}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x5e,0x75,0x95,0xb5,0x98,0xc3,0x6d,0x33,0x58,0xba}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x67,0x8e,0x26,0xbd,0x0a,0x43,0x30,0x7d,0xff,0x0f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x60,0xfd,0xbe,0xb9,0x89,0x6c,0x4c,0x72,0x10,0x7b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x60,0xc3,0xb7,0x51,0xf6,0x2f,0x0b,0xa8,0x61,0x21}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x61,0x3c,0x3e,0x12,0x57,0xfb,0x8e,0x36,0xdd,0xa4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x61,0x04,0x55,0x21,0x5d,0x12,0x39,0xfb,0x09,0x49}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x61,0x63,0x52,0x55,0xbf,0xb7,0xa3,0x69,0x3f,0x91}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x62,0x19,0x4a,0x4d,0x64,0xb7,0x65,0x19,0x8e,0x8a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x63,0x71,0x25,0x6d,0x19,0xbd,0x62,0x0d,0x9e,0x95}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x64,0x29,0xe3,0x42,0x71,0x3b,0x3d,0x7c,0xda,0xc7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x65,0xa8,0x2f,0x55,0xcc,0xe3,0x4c,0x84,0xcc,0x3b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x65,0xc7,0x38,0xa4,0xe4,0xd6,0x0b,0x2b,0xed,0xe6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6f,0x10,0x12,0x4f,0x8f,0x44,0x85,0x5d,0x69,0xa9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6f,0x87,0xcf,0x54,0x39,0xbf,0x36,0x12,0x55,0x61}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6f,0xa7,0xe5,0x14,0xd9,0x5d,0x5d,0x9b,0x9c,0xac}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6f,0xe3,0x17,0x08,0xf6,0x24,0x4b,0xa8,0x5f,0x24}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x68,0xa4,0x34,0x41,0x8d,0xb9,0xda,0xd4,0x86,0x59}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x27,0x7b,0x6d,0x0b,0x29,0x5a,0x67,0xd1,0x95}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x57,0x2a,0xd0,0x28,0x58,0xc8,0x75,0xd2,0xd1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x64,0xb2,0xc9,0x15,0xc6,0x0e,0x8b,0x86,0x4f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x8b,0xd2,0x78,0x3f,0x7a,0xf8,0x92,0x8f,0x80}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x9e,0xf9,0x07,0x73,0xd8,0xe8,0x24,0x93,0xcc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0xcb,0x6c,0x41,0x52,0x61,0x20,0x4e,0x77,0x39}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0xf0,0x96,0x3c,0x4c,0x78,0x33,0xd0,0xf0,0x00}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6b,0x59,0x5f,0xe7,0xdd,0x57,0xba,0xc1,0x12,0x51}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6c,0x62,0x5b,0x0d,0x91,0x66,0xd0,0xca,0x10,0x2d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6c,0x62,0xc5,0x19,0x94,0x5b,0xcd,0x20,0xd9,0x73}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6d,0xb8,0x7f,0xac,0x82,0x55,0x27,0xf2,0x01,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6d,0x95,0x8d,0xd8,0x7b,0x41,0xdc,0x81,0xd4,0x3d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6e,0x38,0xa5,0x11,0x8c,0x64,0x2b,0xc5,0xbe,0x6c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x76,0xbb,0x65,0x0a,0xdf,0x23,0xa2,0x6d,0x4d,0xc8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x76,0x8d,0x46,0x54,0x2a,0xb7,0x9e,0xce,0x74,0x45}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x77,0x30,0x99,0x1c,0x76,0x58,0x64,0x7c,0x2e,0x16}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x71,0x6f,0xc8,0x1a,0xde,0x5b,0xde,0xda,0xcc,0xd5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x72,0x89,0x34,0x3d,0x7c,0x33,0x47,0x01,0x02,0x92}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x74,0x3b,0x0e,0x42,0x30,0x42,0x63,0xa5,0x3e,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x74,0x2d,0xb6,0x15,0xc8,0x70,0x60,0x25,0x2e,0xe7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x74,0x65,0x8d,0x57,0xdb,0x20,0xa2,0xc1,0xa7,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x74,0xf9,0x3c,0xb3,0x2d,0xc2,0x18,0xc5,0xcb,0x2a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x75,0x95,0xe1,0x69,0x25,0x99,0xec,0xac,0x00,0xe4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x76,0x3f,0x29,0x6c,0xec,0xd3,0x95,0x7e,0x4e,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7e,0x9e,0x2f,0x58,0x20,0x23,0xea,0x34,0x78,0x44}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7e,0xaf,0xae,0x18,0x67,0x04,0x98,0x61,0x2f,0xa9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7f,0x84,0xea,0x51,0x31,0xd3,0x46,0x75,0xae,0xbb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x78,0x3e,0x3b,0x74,0x2b,0x6f,0x57,0x06,0x53,0xbb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x78,0x24,0xc1,0x1e,0x6e,0x73,0x93,0xa5,0x08,0xe3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x78,0xc0,0xf5,0x28,0xea,0xf3,0xc2,0x2c,0x6a,0x69}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x79,0x0f,0xd0,0x25,0xd4,0xa5,0xbc,0xcb,0x72,0x51}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7a,0xa9,0x41,0x75,0xf6,0x5f,0x6f,0x83,0x58,0xf1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7c,0x39,0x64,0xaf,0xf5,0x37,0xe7,0x22,0xe0,0x42}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7c,0xc3,0x68,0x1e,0x92,0x7c,0xbb,0x04,0x12,0x0b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7c,0xec,0xf0,0xdb,0x09,0xea,0xdb,0x82,0x5b,0x45}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7d,0x3f,0x6d,0xa4,0xb8,0x8e,0x5f,0xf9,0x5e,0x48}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x7d,0xb0,0xb0,0xe2,0xa5,0xa0,0xbd,0xa3,0x9e,0xb7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x86,0x8a,0x76,0xb7,0x13,0xe8,0x74,0x0c,0x54,0x44}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x86,0xd1,0xb0,0x3e,0x88,0x73,0x42,0x0c,0xb0,0xa4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x80,0xfc,0x51,0x3e,0x9b,0x7d,0x42,0x5d,0x63,0x77}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x81,0x49,0x6a,0xef,0x1f,0x06,0xdf,0xc4,0x6c,0x23}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x81,0xf1,0x31,0xce,0x65,0x59,0xc2,0x2e,0x46,0x47}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x82,0x9b,0xbe,0xc4,0x3b,0xbe,0x8d,0x70,0xda,0x1c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x82,0xea,0xb2,0x5e,0x5f,0x7d,0x80,0x2d,0x17,0x81}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x83,0x8c,0x28,0x22,0x33,0xa4,0xc1,0xe8,0xae,0xe6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x84,0x73,0x02,0xdd,0x47,0x8b,0x29,0xda,0xf6,0x2e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x84,0xb0,0x90,0x4a,0x1c,0xf0,0x75,0x2c,0x23,0x12}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x85,0x29,0xc0,0xeb,0x29,0x0b,0x63,0xaa,0x13,0x98}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x85,0x30,0x22,0xa7,0x56,0x23,0x73,0xe0,0x97,0x03}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x85,0x47,0x8d,0x89,0x8e,0x13,0x57,0x5e,0xd7,0xe2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x85,0x6c,0x77,0xc3,0x06,0x03,0x75,0x75,0x63,0xa7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x86,0x29,0x3b,0x0b,0x5e,0xa2,0xd7,0x44,0x80,0xa1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8f,0x3b,0x03,0x68,0x7e,0x45,0x8a,0x33,0xc2,0xcb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8f,0x2f,0x41,0xc7,0xd4,0xe4,0x7a,0xdc,0x18,0x1c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8f,0x80,0xf0,0x76,0x52,0xa2,0x6e,0x1b,0x0f,0x7c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8f,0xb3,0xa3,0x0a,0x54,0xdf,0xd5,0xb3,0x00,0x07}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x88,0x62,0x93,0x14,0x42,0x07,0xab,0xd0,0xff,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x88,0x90,0x5b,0xa0,0x20,0xb4,0x27,0xe8,0xdf,0x39}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x88,0xdd,0xbb,0x8a,0x6a,0xde,0x55,0x94,0xd5,0x6d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x88,0xea,0xb2,0x3f,0x1e,0x31,0xcc,0xf0,0x3f,0x2e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x89,0x05,0x2d,0x83,0x5f,0x11,0xeb,0xa5,0x9b,0xdd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x89,0x58,0x14,0x63,0xb5,0xcc,0xea,0xdf,0x1f,0x0d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8a,0xd1,0xd5,0x85,0x24,0xe2,0xbf,0xf4,0x37,0x36}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8b,0xa1,0x66,0xb0,0x8f,0x12,0x79,0xdd,0xd4,0xa7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8b,0xc2,0xdb,0xf7,0x90,0x6a,0x11,0x58,0xb0,0xfb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8c,0xab,0x57,0x1a,0x03,0x5a,0x12,0xff,0xfc,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x8d,0x99,0xd1,0xf0,0xe6,0xd9,0xc5,0xff,0xa8,0x73}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x96,0xf0,0x45,0xaa,0xa2,0xe9,0x7b,0x72,0x62,0x56}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x97,0xa3,0x7e,0xe8,0xe8,0x9b,0x1e,0xfe,0x2c,0xc4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x90,0x3c,0xcd,0xc6,0xb8,0x12,0x1e,0x62,0x31,0x58}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x90,0x2a,0x40,0x90,0x92,0x62,0x91,0x56,0x14,0x2e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x90,0x70,0x98,0xf5,0xaf,0x56,0x98,0xb6,0x16,0xdf}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x91,0x23,0x64,0xf3,0x49,0x61,0x3b,0x73,0x9d,0x96}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x91,0xb9,0x56,0x50,0x35,0xd8,0xd3,0x1c,0xd6,0x87}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x91,0xe4,0x65,0x49,0x74,0xcf,0x92,0xa3,0x3f,0xc6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x92,0x71,0x96,0x5a,0xd4,0xf0,0xd0,0x84,0x4f,0x71}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x92,0xb6,0x46,0xee,0x24,0xa0,0xcd,0xb9,0x0c,0xdd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x92,0x9c,0x82,0xbf,0x8e,0x4f,0xd7,0xc7,0x4a,0x9d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x92,0xc9,0xa1,0x01,0xeb,0x52,0xdb,0xbd,0x93,0xf8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x93,0x06,0x3f,0xc3,0xe6,0x73,0x40,0x91,0xb1,0x30}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x93,0xd8,0x7a,0x5d,0x21,0xd0,0x87,0xf5,0x92,0x8d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x94,0x54,0x6c,0x57,0xa4,0x1b,0x74,0xf0,0x7d,0x0b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x94,0x96,0xd4,0xa4,0xed,0x65,0x96,0xbc,0x4a,0xbc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x95,0x3d,0xec,0x1a,0x20,0x97,0xa2,0xa1,0xcd,0xab}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x95,0x1a,0x3a,0xb0,0x29,0x8c,0xcc,0x32,0x80,0xf7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x95,0x47,0xee,0xab,0xa9,0x78,0x17,0xa7,0xed,0x73}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x95,0x68,0x0e,0x9d,0x10,0x5d,0x2d,0xf7,0x6a,0x56}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x95,0xe0,0x9a,0x05,0x94,0x67,0x22,0xc2,0x99,0xf4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9e,0xb9,0xda,0xa3,0xfc,0xd4,0xd1,0xb9,0xb5,0x40}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9f,0x0a,0x17,0x56,0xa6,0xcb,0xda,0x86,0x0f,0x4f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9f,0x17,0xcb,0x57,0x64,0x8a,0x8e,0xf1,0x93,0x4f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9f,0x60,0x23,0xd8,0x31,0xf5,0x3b,0x5d,0x00,0xca}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9f,0xd2,0xb0,0x27,0xc6,0x36,0x2f,0xf9,0x76,0xb8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x98,0x3d,0x24,0x92,0x18,0x0e,0xbe,0x5e,0x37,0x80}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x98,0x2b,0xfa,0x4d,0xf6,0xe3,0xcb,0x8f,0xa7,0xca}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x98,0x2e,0x6e,0xe7,0x52,0xb9,0x59,0xd1,0x70,0x7e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0x15,0x6a,0xb4,0x2e,0x18,0x73,0x15,0xd0,0xb2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xb9,0x4b,0x45,0x2c,0x9c,0x74,0x95,0x85,0x38}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xf8,0x24,0xd4,0xa5,0x4c,0xed,0xea,0xb9,0x94}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xfc,0x5b,0xe1,0x93,0xb3,0x4a,0x82,0xc0,0x94}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xe6,0x23,0x9d,0x7a,0xed,0x35,0xe6,0x99,0x70}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9a,0x10,0x03,0xfc,0x52,0xa3,0x94,0xb1,0x55,0x1e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9a,0xbd,0xbb,0xf4,0xaa,0xde,0xf7,0xfc,0xee,0x83}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9a,0x8c,0xe7,0x4c,0x13,0xf0,0xa0,0xdf,0xd7,0x18}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9b,0x53,0xdf,0x76,0xd6,0x86,0x7b,0x67,0xa6,0xb2}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9c,0xbd,0x0b,0xef,0xec,0x63,0xe9,0xe6,0xa7,0xb8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9c,0xd2,0x89,0x56,0xf8,0x19,0x83,0x37,0xf7,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9d,0x9b,0xde,0x57,0xf1,0x06,0xae,0x93,0x0f,0xbd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9d,0xc8,0xce,0xb0,0x94,0x36,0xb8,0x6d,0x13,0x23}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9e,0x11,0x46,0xb7,0x7e,0x5b,0x0a,0x28,0x75,0x71}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9e,0x2b,0xdf,0x5e,0x5e,0x37,0x9a,0x3c,0xc2,0x97}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa7,0x3e,0x5d,0x9e,0xf6,0x87,0xbb,0x23,0x4b,0x8e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa7,0x23,0xf2,0xb4,0xee,0x5c,0x47,0x6b,0x2d,0xa8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa7,0x7b,0xe7,0x14,0x3b,0x66,0x01,0x10,0x16,0xcd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa7,0x61,0xb3,0x07,0x3c,0x83,0xf3,0xcb,0x55,0x71}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa0,0x14,0xbc,0x6f,0x03,0x89,0x2b,0x57,0xde,0xc8}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa1,0xbc,0x70,0x3d,0x1c,0x84,0xc8,0xac,0x8b,0xf5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa2,0x3b,0xdd,0xc1,0xd3,0x1f,0xa2,0xe6,0xee,0x25}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa2,0x23,0xf2,0xee,0xcb,0x9b,0x94,0x0f,0x04,0x21}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa2,0xa2,0x94,0x9e,0xce,0x1a,0xf9,0xcb,0x31,0xc5}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa2,0xfa,0x66,0x69,0x17,0xc7,0xd5,0x01,0x96,0xc6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa3,0x46,0x3f,0xc6,0x49,0xe3,0xc8,0xdd,0xd9,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa3,0xa3,0x67,0xe4,0xa4,0x3c,0xf0,0xa8,0x9b,0x9b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa3,0xab,0x27,0xeb,0x0b,0x9b,0x40,0xe4,0xc3,0xcb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa4,0x81,0x96,0x0c,0x52,0xde,0x9b,0x8d,0x70,0x78}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa4,0x81,0x99,0x7c,0xcb,0x67,0xcc,0x4c,0x5d,0x4b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa4,0xa5,0xa5,0x10,0x66,0xfc,0x15,0x63,0x0e,0x3d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa4,0xcd,0x88,0xd6,0xdf,0xed,0xab,0xa6,0xe1,0x88}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa6,0x6c,0x01,0x32,0x5f,0x56,0x32,0x72,0x1c,0x2b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xae,0x94,0x31,0x12,0x75,0x92,0xd8,0x32,0x8a,0xd1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xaf,0x56,0x76,0xe7,0x35,0xf3,0x5a,0x62,0x9b,0xa3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa8,0xb9,0xc3,0x07,0x95,0x23,0xde,0xe0,0xc6,0x7b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa9,0x6d,0x83,0xa6,0x9c,0xdd,0xae,0x7c,0xd6,0x97}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa9,0xa8,0x9a,0x15,0x5d,0xda,0xe1,0x87,0x2d,0x0e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa9,0xc8,0x44,0xc2,0x1a,0xaf,0x46,0xa0,0xf2,0xf1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xaa,0x7d,0xc2,0x0c,0x95,0xe2,0x5b,0x02,0x8e,0x41}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xab,0x00,0x8e,0xd1,0x06,0x26,0x63,0xa5,0x1d,0x49}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xab,0x29,0x85,0x0f,0xf2,0xb8,0x58,0x8f,0xdb,0xbf}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xab,0x98,0x40,0x0a,0x73,0x43,0x6f,0xb6,0x3d,0x8b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xab,0xdd,0x6d,0x5d,0xc5,0x36,0xcb,0x6c,0xc8,0x70}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xac,0x81,0x65,0xa3,0x8b,0xea,0x0b,0x71,0xe4,0x16}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xad,0x1b,0x40,0xc1,0x45,0x64,0xbf,0x24,0x15,0xca}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xad,0x1c,0xc0,0xb4,0x95,0xb5,0x17,0xc0,0xc2,0x41}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xad,0xc4,0xfa,0x8d,0xa6,0xf7,0x40,0x42,0xe7,0xd3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xae,0x2e,0xe4,0x64,0x79,0x05,0x5f,0xb7,0x04,0x14}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb0,0x48,0xe6,0xe8,0x48,0xfa,0xca,0x87,0x78,0x18}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb0,0x6c,0x4a,0x92,0xde,0xd3,0x0d,0x28,0xc4,0x79}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb1,0x41,0x81,0xac,0xde,0xce,0x0b,0x94,0x8a,0x9d}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb1,0x73,0xdf,0x4b,0xab,0xc3,0x7a,0x3c,0x48,0x99}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb1,0xb6,0xc8,0x72,0x86,0xc6,0x34,0x6b,0xef,0x41}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb1,0xd5,0x8e,0xf0,0x22,0x9a,0x8b,0xa6,0xf1,0xfb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb1,0xd8,0x90,0x36,0x0e,0xc6,0x51,0x9c,0x8b,0x93}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb2,0xce,0xea,0x6a,0xd7,0x34,0x30,0x8d,0xdf,0x65}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb2,0xea,0xa2,0xc5,0xeb,0x2a,0x10,0xec,0xeb,0x4e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbe,0xda,0x60,0xee,0xa0,0xf8,0xdd,0x5a,0x11,0xb6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbf,0x7f,0x7f,0x68,0x2c,0x63,0x70,0xba,0xbb,0xf1}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb9,0xaa,0xce,0xfd,0x87,0x35,0x7b,0xee,0x0d,0x40}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb9,0xe5,0xb3,0x2c,0xb6,0x6d,0x91,0x46,0x22,0xad}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xba,0x49,0xd2,0xda,0xb8,0x28,0xe8,0x4d,0x53,0xca}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xba,0xcd,0x40,0x9b,0x0b,0xc6,0x82,0xba,0xc8,0xdd}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbb,0x57,0x4d,0xce,0xa0,0x53,0x4d,0x8f,0xcd,0x4f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbb,0xba,0xc0,0x45,0x0b,0x3d,0x30,0xef,0x86,0x93}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbc,0x80,0x0b,0xa0,0xe3,0xc1,0x9b,0x6b,0xc5,0x17}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbd,0x3a,0xc5,0xd0,0xc3,0x93,0x32,0x55,0x57,0x27}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbd,0x63,0x78,0x09,0xf3,0x85,0x50,0x42,0x0c,0x3a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xbd,0xb2,0x78,0xc7,0x06,0x2c,0xe1,0xb8,0x72,0xdc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc7,0x25,0x66,0x48,0x17,0x18,0x9d,0x2d,0x05,0xb4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc7,0x66,0xbe,0x2e,0x08,0xdf,0xba,0xf7,0xae,0x83}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc7,0x6d,0x92,0x43,0x00,0x24,0xe5,0xd6,0x83,0xd3}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc7,0xf7,0x05,0x69,0x99,0x52,0x54,0x77,0x2b,0x1f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc7,0xdd,0x9d,0xe0,0x6d,0xaa,0x03,0xcb,0x9c,0x21}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc0,0x0f,0xf8,0x18,0xb0,0x84,0x66,0x47,0x08,0xe4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc0,0x41,0xc0,0xc5,0x9d,0xef,0x46,0x46,0xae,0x7f}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc1,0x89,0x05,0x1b,0x88,0x6b,0xd7,0x20,0x08,0x9b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc2,0x4e,0xd2,0xd3,0xfd,0x58,0x32,0x14,0x6f,0x87}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc2,0x6d,0xf5,0x40,0x0f,0xbd,0xfb,0x53,0x19,0xc9}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc3,0x3e,0x86,0xb1,0xd5,0x0c,0x5a,0x0e,0x18,0x4e}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0x3a,0x2a,0x49,0xb4,0x72,0xa4,0x2c,0x7b,0x99}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0x44,0x04,0x3a,0x11,0x84,0x47,0x67,0x2a,0x13}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0x45,0x1f,0xbc,0xc9,0xa0,0x32,0x01,0xeb,0xbc}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0x55,0x2a,0xb9,0xbb,0x9b,0x2a,0xe7,0x1c,0x75}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0xdf,0x49,0x72,0xb7,0xed,0xbe,0x9f,0x59,0xfa}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc4,0xe0,0x24,0x19,0x5a,0x39,0xc6,0xbe,0x74,0xee}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc5,0xdc,0x95,0xee,0xec,0x4d,0x25,0xb1,0xa1,0x5a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc6,0x47,0x01,0xca,0x17,0xe1,0x47,0x46,0x9b,0xd6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xce,0xf6,0xda,0x2a,0x7f,0x69,0x90,0xad,0x89,0xe4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xce,0xf1,0x60,0x80,0x76,0xe7,0x9a,0x36,0xdc,0xc7}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcf,0x98,0x18,0x43,0xeb,0x5d,0xd7,0x16,0xf1,0x50}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc8,0x76,0xb8,0x89,0x52,0x6f,0x23,0x93,0xe5,0x24}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc9,0x3e,0xe1,0xbf,0xef,0xc8,0x22,0x97,0xae,0x51}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc9,0x82,0xc3,0xcc,0x29,0x07,0x0b,0x8d,0x6f,0xfb}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xc9,0xfe,0x7a,0x81,0x62,0x35,0x52,0xf7,0x02,0x0c}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xca,0x33,0x3a,0xdc,0x87,0x62,0x7a,0xc2,0x1d,0xe6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xca,0x50,0x8d,0xe0,0x82,0x1c,0x59,0x0f,0xef,0x1b}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xca,0xa3,0x66,0x19,0x34,0xac,0xb2,0x0f,0x60,0x9a}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcb,0xb3,0xa0,0x39,0xf6,0x46,0xec,0x5a,0x42,0xc6}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcc,0xc6,0x22,0xb4,0xfc,0xf7,0xff,0xb0,0xa2,0xb4}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcd,0x2e,0x71,0x78,0x7b,0x6d,0x9e,0x61,0x70,0x05}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcd,0x31,0x38,0x94,0x95,0xca,0x44,0xf4,0x65,0x68}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcd,0x61,0xe1,0xbe,0x7b,0x46,0x9c,0x51,0xbf,0x66}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xcd,0xac,0xcf,0x18,0x1f,0xa6,0x8f,0x02,0x6a,0x43}, 8333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xce,0x20,0x1e,0x2c,0x8d,0x2c,0x9e,0xd9,0xa7,0xac}, 8333}
+static const uint8_t chainparams_seed_main[] = {
+ 0x01,0x04,0x02,0x27,0xad,0x7e,0x20,0x8d,
+ 0x01,0x04,0x03,0x0e,0xa8,0xc9,0xbc,0xcd,
+ 0x01,0x04,0x04,0x24,0x70,0x2c,0x20,0x8d,
+ 0x01,0x04,0x05,0x08,0x12,0x1f,0x20,0x8d,
+ 0x01,0x04,0x05,0x0e,0xc8,0xa7,0x20,0x8d,
+ 0x01,0x04,0x05,0x38,0x14,0x02,0x20,0x8d,
+ 0x01,0x04,0x05,0x66,0x92,0x63,0x20,0x8d,
+ 0x01,0x04,0x05,0x67,0x89,0x92,0x24,0x75,
+ 0x01,0x04,0x05,0x80,0x57,0x7e,0x20,0x8d,
+ 0x01,0x04,0x05,0x85,0x41,0x52,0x20,0x8d,
+ 0x01,0x04,0x05,0xbb,0x37,0xf2,0x20,0x8d,
+ 0x01,0x04,0x05,0xbc,0x3e,0x18,0x20,0x8d,
+ 0x01,0x04,0x05,0xbc,0x3e,0x21,0x20,0x8d,
+ 0x01,0x04,0x05,0xc7,0x85,0xc1,0x20,0x8d,
+ 0x01,0x04,0x08,0x26,0x59,0x98,0x20,0x8d,
+ 0x01,0x04,0x0d,0xe7,0x14,0xf9,0x20,0x8d,
+ 0x01,0x04,0x12,0x1b,0x4f,0x11,0x20,0x8d,
+ 0x01,0x04,0x14,0xb8,0x0f,0x74,0x20,0xf1,
+ 0x01,0x04,0x17,0x1c,0xcd,0x61,0x20,0x8d,
+ 0x01,0x04,0x17,0x6a,0xfc,0xe6,0x20,0x8d,
+ 0x01,0x04,0x17,0xaf,0x00,0xca,0x20,0x8d,
+ 0x01,0x04,0x17,0xaf,0x00,0xd4,0x20,0x8d,
+ 0x01,0x04,0x17,0xf1,0xfa,0xfc,0x20,0x8d,
+ 0x01,0x04,0x17,0xf5,0x18,0x9a,0x20,0x8d,
+ 0x01,0x04,0x18,0x56,0xb8,0x42,0x20,0x8d,
+ 0x01,0x04,0x18,0x74,0xf6,0x09,0x20,0x8d,
+ 0x01,0x04,0x18,0x8d,0x22,0xa6,0x20,0x8d,
+ 0x01,0x04,0x18,0x9b,0xc4,0xf6,0x20,0x8d,
+ 0x01,0x04,0x18,0x9d,0x82,0xde,0x20,0x8d,
+ 0x01,0x04,0x18,0xbc,0xb0,0xff,0x20,0x8d,
+ 0x01,0x04,0x18,0xed,0x46,0x35,0x20,0x8d,
+ 0x01,0x04,0x1b,0x7c,0x04,0x43,0x20,0x8d,
+ 0x01,0x04,0x1f,0x11,0x46,0x50,0x20,0x8d,
+ 0x01,0x04,0x1f,0x15,0x08,0x20,0x20,0x8d,
+ 0x01,0x04,0x1f,0x2d,0x76,0x0a,0x20,0x8d,
+ 0x01,0x04,0x1f,0x84,0x11,0x38,0x20,0x8d,
+ 0x01,0x04,0x1f,0x86,0x79,0xdf,0x20,0x8d,
+ 0x01,0x04,0x20,0xd6,0xb7,0x72,0x20,0x8d,
+ 0x01,0x04,0x23,0x89,0xec,0x20,0x20,0x8d,
+ 0x01,0x04,0x23,0xb9,0x91,0x69,0x20,0x8d,
+ 0x01,0x04,0x23,0xd1,0x33,0xd4,0x20,0x8d,
+ 0x01,0x04,0x23,0xf5,0xaf,0x4c,0x20,0x8d,
+ 0x01,0x04,0x25,0x74,0x5f,0x29,0x20,0x8d,
+ 0x01,0x04,0x25,0x8f,0x09,0x6b,0x20,0x8d,
+ 0x01,0x04,0x25,0x8f,0x74,0x2b,0x20,0x8d,
+ 0x01,0x04,0x25,0xbf,0xf4,0x95,0x20,0x8d,
+ 0x01,0x04,0x25,0xd3,0x4e,0xfd,0x20,0x8d,
+ 0x01,0x04,0x25,0xdd,0xd1,0xde,0x5f,0x0d,
+ 0x01,0x04,0x25,0xe4,0x5c,0x6e,0x20,0x8d,
+ 0x01,0x04,0x2b,0xe1,0x3e,0x6b,0x20,0x8d,
+ 0x01,0x04,0x2b,0xe1,0x9d,0x98,0x20,0x8d,
+ 0x01,0x04,0x2d,0x24,0xb8,0x06,0x20,0x8d,
+ 0x01,0x04,0x2d,0x30,0xa8,0x10,0x20,0x8d,
+ 0x01,0x04,0x2d,0x55,0x55,0x08,0x20,0x8d,
+ 0x01,0x04,0x2d,0x55,0x55,0x09,0x20,0x8d,
+ 0x01,0x04,0x2d,0x81,0xb4,0xd6,0x20,0x8d,
+ 0x01,0x04,0x2d,0x95,0x4e,0x80,0x20,0x8d,
+ 0x01,0x04,0x2d,0x97,0x7d,0xda,0x20,0x8d,
+ 0x01,0x04,0x2d,0x9a,0xff,0x2e,0x20,0x8d,
+ 0x01,0x04,0x2d,0x9b,0x9d,0xef,0x20,0x8d,
+ 0x01,0x04,0x2e,0x1c,0x84,0x22,0x20,0x8d,
+ 0x01,0x04,0x2e,0x1c,0xcc,0x15,0x20,0x8d,
+ 0x01,0x04,0x2e,0x20,0x32,0x62,0x20,0x8d,
+ 0x01,0x04,0x2e,0x3b,0x0d,0x23,0x20,0x8d,
+ 0x01,0x04,0x2e,0x80,0x28,0xad,0x20,0x8d,
+ 0x01,0x04,0x2e,0x80,0x8c,0xc1,0x20,0x8d,
+ 0x01,0x04,0x2e,0x92,0xf8,0x59,0x20,0x8d,
+ 0x01,0x04,0x2e,0xa6,0xa2,0x2d,0x4e,0x21,
+ 0x01,0x04,0x2e,0xbc,0x0f,0x06,0x20,0x8d,
+ 0x01,0x04,0x2e,0xe5,0xa5,0x8e,0x20,0x8d,
+ 0x01,0x04,0x2e,0xe5,0xee,0xbb,0x20,0x8d,
+ 0x01,0x04,0x2e,0xf9,0x53,0x52,0x20,0x8d,
+ 0x01,0x04,0x2e,0xfe,0xd9,0xa9,0x20,0x8d,
+ 0x01,0x04,0x2f,0x4a,0xbf,0x22,0x20,0x8d,
+ 0x01,0x04,0x2f,0x73,0x35,0xa3,0x20,0x8d,
+ 0x01,0x04,0x2f,0xbb,0x1a,0x87,0x20,0x8d,
+ 0x01,0x04,0x2f,0xde,0x67,0xea,0x20,0x8d,
+ 0x01,0x04,0x2f,0xfd,0x05,0x63,0x20,0x8d,
+ 0x01,0x04,0x31,0xe8,0x52,0x4c,0x20,0x8d,
+ 0x01,0x04,0x31,0xf7,0xd7,0x2b,0x20,0x8d,
+ 0x01,0x04,0x32,0x02,0x0d,0xa6,0x20,0x8d,
+ 0x01,0x04,0x32,0x22,0x27,0x48,0x20,0x8d,
+ 0x01,0x04,0x32,0x2d,0xe8,0xbd,0x20,0x8d,
+ 0x01,0x04,0x32,0x44,0x68,0x5c,0x20,0x8d,
+ 0x01,0x04,0x33,0x44,0x24,0x39,0x20,0x8d,
+ 0x01,0x04,0x33,0x9a,0x3c,0x22,0x20,0x8d,
+ 0x01,0x04,0x34,0xa9,0xee,0x42,0x20,0x8d,
+ 0x01,0x04,0x36,0xc5,0x1e,0xdf,0x20,0x8d,
+ 0x01,0x04,0x36,0xe3,0x42,0x39,0x20,0x8d,
+ 0x01,0x04,0x3a,0x9e,0x00,0x56,0x20,0x8d,
+ 0x01,0x04,0x3a,0xab,0x87,0xf2,0x20,0x8d,
+ 0x01,0x04,0x3a,0xe5,0xd0,0x9e,0x20,0x8d,
+ 0x01,0x04,0x3c,0xf4,0x6d,0x13,0x20,0x8d,
+ 0x01,0x04,0x3e,0x26,0x4b,0xd0,0x20,0x8d,
+ 0x01,0x04,0x3e,0x4a,0x8f,0x0b,0x20,0x8d,
+ 0x01,0x04,0x3e,0x50,0xe3,0x31,0x20,0x8d,
+ 0x01,0x04,0x3e,0x98,0x3a,0x10,0x24,0xcd,
+ 0x01,0x04,0x3e,0xd2,0xa7,0xc7,0x20,0x8d,
+ 0x01,0x04,0x3e,0xea,0xbc,0xa0,0x20,0x8d,
+ 0x01,0x04,0x3e,0xfb,0x36,0xa3,0x20,0x8d,
+ 0x01,0x04,0x3f,0xe3,0x74,0xa2,0x20,0x8d,
+ 0x01,0x04,0x41,0x13,0x9b,0x52,0x20,0x8d,
+ 0x01,0x04,0x41,0x5f,0x31,0x66,0x20,0x8d,
+ 0x01,0x04,0x42,0x12,0xac,0x15,0x20,0x8d,
+ 0x01,0x04,0x42,0xf0,0xed,0x9b,0x20,0x8d,
+ 0x01,0x04,0x43,0xd2,0xe4,0xcb,0x20,0x8d,
+ 0x01,0x04,0x45,0x1e,0xd7,0x2a,0x20,0x8d,
+ 0x01,0x04,0x45,0x3b,0x12,0xce,0x20,0x8d,
+ 0x01,0x04,0x45,0x40,0x21,0x47,0x20,0x8d,
+ 0x01,0x04,0x45,0x77,0xc1,0x09,0x20,0x8d,
+ 0x01,0x04,0x45,0xd1,0x17,0x48,0x20,0x8d,
+ 0x01,0x04,0x46,0x7b,0x7d,0xed,0x20,0x8d,
+ 0x01,0x04,0x46,0xb9,0x38,0x88,0x20,0x8d,
+ 0x01,0x04,0x47,0x26,0x5a,0xeb,0x20,0x8d,
+ 0x01,0x04,0x48,0x0c,0x49,0x46,0x20,0x8d,
+ 0x01,0x04,0x48,0x35,0x86,0xb6,0x20,0x8d,
+ 0x01,0x04,0x48,0xe1,0x07,0x50,0x20,0x8d,
+ 0x01,0x04,0x48,0xea,0xb6,0x27,0x20,0x8d,
+ 0x01,0x04,0x48,0xfa,0xb8,0x39,0x20,0x8d,
+ 0x01,0x04,0x49,0x53,0x67,0x4f,0x20,0x8d,
+ 0x01,0x04,0x4a,0x76,0x89,0x77,0x20,0x8d,
+ 0x01,0x04,0x4a,0x85,0x64,0x4a,0x20,0x8d,
+ 0x01,0x04,0x4a,0xd7,0xdb,0xd6,0x20,0x8d,
+ 0x01,0x04,0x4a,0xdc,0xff,0xbe,0x20,0x8d,
+ 0x01,0x04,0x4b,0x9e,0x27,0xe7,0x20,0x8d,
+ 0x01,0x04,0x4d,0x35,0x35,0xc4,0x20,0x8d,
+ 0x01,0x04,0x4d,0x46,0x10,0xf5,0x20,0x8d,
+ 0x01,0x04,0x4d,0x69,0x57,0x61,0x20,0x8d,
+ 0x01,0x04,0x4d,0x78,0x71,0x45,0x20,0xf1,
+ 0x01,0x04,0x4d,0x78,0x7a,0x16,0x20,0xf1,
+ 0x01,0x04,0x4d,0xa6,0x53,0xa7,0x20,0x8d,
+ 0x01,0x04,0x4d,0xf7,0xb2,0x82,0x20,0x8d,
+ 0x01,0x04,0x4e,0x1b,0x8b,0x0d,0x20,0x8d,
+ 0x01,0x04,0x4e,0x3f,0x1c,0x92,0x20,0x8d,
+ 0x01,0x04,0x4e,0x53,0x67,0x04,0x20,0x8d,
+ 0x01,0x04,0x4e,0x8d,0x7b,0x63,0x20,0x8d,
+ 0x01,0x04,0x4f,0x4d,0x21,0x83,0x20,0x8d,
+ 0x01,0x04,0x4f,0x4d,0x85,0x1e,0x20,0x8d,
+ 0x01,0x04,0x4f,0x65,0x01,0x19,0x20,0x8d,
+ 0x01,0x04,0x4f,0x75,0xc0,0xe5,0x20,0x8d,
+ 0x01,0x04,0x4f,0x85,0xe4,0x37,0x20,0x8d,
+ 0x01,0x04,0x4f,0x92,0x15,0xa3,0x20,0x8d,
+ 0x01,0x04,0x50,0x59,0xcb,0xac,0x1f,0x41,
+ 0x01,0x04,0x50,0x5d,0xd5,0xf6,0x20,0x8d,
+ 0x01,0x04,0x50,0xc0,0x62,0x6e,0x20,0x8e,
+ 0x01,0x04,0x50,0xe5,0x1c,0x3c,0x20,0x8d,
+ 0x01,0x04,0x50,0xe8,0xf7,0xd2,0x20,0x8d,
+ 0x01,0x04,0x50,0xf2,0x27,0x4c,0x20,0x8d,
+ 0x01,0x04,0x50,0xfd,0x5e,0xfc,0x20,0x8d,
+ 0x01,0x04,0x51,0x00,0xc6,0x19,0x20,0x8d,
+ 0x01,0x04,0x51,0x07,0x0d,0x54,0x20,0x8d,
+ 0x01,0x04,0x51,0x75,0xe1,0xf5,0x20,0x8d,
+ 0x01,0x04,0x51,0x87,0x89,0xe1,0x20,0x8d,
+ 0x01,0x04,0x51,0xab,0x16,0x8f,0x20,0x8d,
+ 0x01,0x04,0x51,0xbf,0xe9,0x86,0x20,0x8d,
+ 0x01,0x04,0x51,0xe8,0x4e,0x4b,0x20,0x8d,
+ 0x01,0x04,0x51,0xf2,0x5b,0x17,0x20,0x8d,
+ 0x01,0x04,0x52,0x1d,0x3a,0x6d,0x20,0x8d,
+ 0x01,0x04,0x52,0x88,0x63,0x16,0x20,0x8d,
+ 0x01,0x04,0x52,0x95,0x61,0x19,0x44,0x9f,
+ 0x01,0x04,0x52,0xa5,0x13,0x30,0x20,0x8d,
+ 0x01,0x04,0x52,0xc2,0x99,0xe9,0x20,0x8d,
+ 0x01,0x04,0x52,0xc5,0xd7,0x7d,0x20,0x8d,
+ 0x01,0x04,0x52,0xc7,0x66,0x0a,0x20,0x8d,
+ 0x01,0x04,0x52,0xc8,0xcd,0x1e,0x20,0x8d,
+ 0x01,0x04,0x52,0xca,0x44,0xe7,0x20,0x8d,
+ 0x01,0x04,0x52,0xdd,0x80,0x1f,0x20,0x8d,
+ 0x01,0x04,0x52,0xe4,0x06,0x83,0x20,0x8d,
+ 0x01,0x04,0x53,0x55,0x8b,0x5e,0x20,0x8d,
+ 0x01,0x04,0x53,0x63,0xf5,0x14,0x20,0x8d,
+ 0x01,0x04,0x53,0x89,0x29,0x0a,0x20,0x8d,
+ 0x01,0x04,0x53,0xae,0xd1,0x57,0x20,0x8d,
+ 0x01,0x04,0x53,0xd9,0x08,0x1f,0xad,0x84,
+ 0x01,0x04,0x54,0x26,0x03,0xf9,0x20,0x8d,
+ 0x01,0x04,0x54,0x26,0xb9,0x7a,0x20,0x8d,
+ 0x01,0x04,0x54,0x5c,0x5c,0xf7,0x20,0x8d,
+ 0x01,0x04,0x54,0xc0,0x10,0xea,0x20,0x8d,
+ 0x01,0x04,0x54,0xc2,0x9e,0x7c,0x20,0x8d,
+ 0x01,0x04,0x54,0xd4,0x91,0x18,0x20,0x8d,
+ 0x01,0x04,0x54,0xd4,0xf4,0x5f,0x20,0x8d,
+ 0x01,0x04,0x54,0xd8,0x33,0x24,0x20,0x8d,
+ 0x01,0x04,0x54,0xff,0xf9,0xa3,0x20,0x8d,
+ 0x01,0x04,0x55,0x19,0xff,0x93,0x20,0x8d,
+ 0x01,0x04,0x55,0x46,0x9c,0xd1,0x20,0x8d,
+ 0x01,0x04,0x55,0x91,0x8e,0x2e,0x20,0x8d,
+ 0x01,0x04,0x55,0xaa,0xe9,0x5f,0x20,0x8d,
+ 0x01,0x04,0x55,0xb8,0x8a,0x6c,0x20,0x8d,
+ 0x01,0x04,0x55,0xbe,0x00,0x05,0x20,0x8d,
+ 0x01,0x04,0x55,0xbf,0xc8,0x33,0x20,0x8d,
+ 0x01,0x04,0x55,0xc0,0xbf,0x06,0x48,0x44,
+ 0x01,0x04,0x55,0xc2,0xee,0x83,0x20,0x8d,
+ 0x01,0x04,0x55,0xc3,0x36,0x6e,0x20,0x8d,
+ 0x01,0x04,0x55,0xd6,0xa1,0xfc,0x20,0x8d,
+ 0x01,0x04,0x55,0xd6,0xb9,0x33,0x20,0x8d,
+ 0x01,0x04,0x55,0xf1,0x6a,0xcb,0x20,0x8d,
+ 0x01,0x04,0x55,0xf6,0xa8,0xfc,0x20,0x8d,
+ 0x01,0x04,0x56,0x38,0xee,0xf7,0x20,0x8d,
+ 0x01,0x04,0x57,0x3d,0x5a,0xe6,0x20,0x8d,
+ 0x01,0x04,0x57,0x4f,0x44,0x56,0x20,0x8d,
+ 0x01,0x04,0x57,0x4f,0x5e,0xdd,0x20,0x8d,
+ 0x01,0x04,0x57,0x78,0x08,0x05,0x4e,0x28,
+ 0x01,0x04,0x57,0xf6,0x2e,0x84,0x20,0x8d,
+ 0x01,0x04,0x57,0xf7,0x6f,0xde,0x20,0x8d,
+ 0x01,0x04,0x58,0x54,0xde,0xfc,0x20,0x8d,
+ 0x01,0x04,0x58,0x56,0xf3,0xf1,0x20,0x8d,
+ 0x01,0x04,0x58,0x57,0x5d,0x34,0x06,0x9b,
+ 0x01,0x04,0x58,0x77,0xc5,0xc8,0x20,0x8d,
+ 0x01,0x04,0x58,0x81,0xfd,0x5e,0x20,0x8d,
+ 0x01,0x04,0x58,0x93,0xf4,0xfa,0x20,0x8d,
+ 0x01,0x04,0x58,0xd0,0x03,0xc3,0x20,0x8d,
+ 0x01,0x04,0x58,0xd4,0x2c,0x21,0x20,0x8d,
+ 0x01,0x04,0x58,0xd6,0x39,0x5f,0x20,0x8d,
+ 0x01,0x04,0x59,0x6a,0xc7,0x26,0x20,0x8d,
+ 0x01,0x04,0x59,0x6c,0x7e,0xe4,0x20,0x8d,
+ 0x01,0x04,0x59,0x73,0x78,0x2b,0x20,0x8d,
+ 0x01,0x04,0x59,0x85,0x44,0x41,0x20,0x8d,
+ 0x01,0x04,0x59,0xbe,0x13,0xa2,0x20,0x8d,
+ 0x01,0x04,0x59,0xf8,0xac,0x0a,0x20,0x8d,
+ 0x01,0x04,0x5a,0x92,0x99,0x15,0x20,0x8d,
+ 0x01,0x04,0x5a,0xb6,0xa5,0x12,0x20,0x8d,
+ 0x01,0x04,0x5b,0x6a,0xbc,0xe5,0x20,0x8d,
+ 0x01,0x04,0x5b,0xc1,0xed,0x74,0x20,0x8d,
+ 0x01,0x04,0x5b,0xcc,0x63,0xb2,0x20,0x8d,
+ 0x01,0x04,0x5b,0xcc,0x95,0x05,0x20,0x8d,
+ 0x01,0x04,0x5b,0xd6,0x46,0x3f,0x20,0x8d,
+ 0x01,0x04,0x5b,0xe4,0x98,0xec,0x20,0x8d,
+ 0x01,0x04,0x5c,0x0c,0x9a,0x73,0x20,0x8d,
+ 0x01,0x04,0x5c,0xf9,0x8f,0x2c,0x20,0x8d,
+ 0x01,0x04,0x5d,0x0c,0x42,0x62,0x20,0x8d,
+ 0x01,0x04,0x5d,0x2e,0x36,0x04,0x20,0x8d,
+ 0x01,0x04,0x5d,0x73,0x14,0x82,0x20,0x8d,
+ 0x01,0x04,0x5d,0x7b,0xb4,0xa4,0x20,0x8d,
+ 0x01,0x04,0x5d,0xbd,0x91,0xa9,0x20,0x8d,
+ 0x01,0x04,0x5d,0xf1,0xe4,0x66,0x20,0x8d,
+ 0x01,0x04,0x5e,0x13,0x07,0x37,0x20,0x8d,
+ 0x01,0x04,0x5e,0x13,0x80,0xcc,0x20,0x8d,
+ 0x01,0x04,0x5e,0x34,0x70,0xe3,0x20,0x8d,
+ 0x01,0x04,0x5e,0x9a,0x60,0x82,0x20,0x8d,
+ 0x01,0x04,0x5e,0x9c,0xae,0xc9,0x20,0x8d,
+ 0x01,0x04,0x5e,0x9e,0xf6,0xb7,0x20,0x8d,
+ 0x01,0x04,0x5e,0xb1,0xab,0x49,0x20,0x8d,
+ 0x01,0x04,0x5e,0xc7,0xb2,0xe9,0x1f,0xa4,
+ 0x01,0x04,0x5e,0xed,0x7d,0x1e,0x20,0x8d,
+ 0x01,0x04,0x5e,0xf7,0x86,0x4d,0x20,0x8d,
+ 0x01,0x04,0x5f,0x30,0xe4,0x2d,0x20,0x8d,
+ 0x01,0x04,0x5f,0x45,0xf9,0x3f,0x20,0x8d,
+ 0x01,0x04,0x5f,0x52,0x92,0x46,0x20,0x8d,
+ 0x01,0x04,0x5f,0x53,0x49,0x1f,0x20,0x8d,
+ 0x01,0x04,0x5f,0x54,0xa4,0x2b,0x20,0x8d,
+ 0x01,0x04,0x5f,0x57,0xe2,0x38,0x20,0x8d,
+ 0x01,0x04,0x5f,0x6e,0xea,0x5d,0x20,0x8d,
+ 0x01,0x04,0x5f,0xa3,0x47,0x7e,0x20,0x8d,
+ 0x01,0x04,0x5f,0xa4,0x41,0xc2,0x20,0x8d,
+ 0x01,0x04,0x5f,0xae,0x42,0xd3,0x20,0x8d,
+ 0x01,0x04,0x5f,0xd3,0xae,0x89,0x20,0x8d,
+ 0x01,0x04,0x5f,0xd8,0x0b,0x9c,0x20,0xf1,
+ 0x01,0x04,0x60,0x2f,0x72,0x6c,0x20,0x8d,
+ 0x01,0x04,0x61,0x54,0xe8,0x69,0x20,0x8d,
+ 0x01,0x04,0x61,0x63,0xcd,0xf1,0x20,0x8d,
+ 0x01,0x04,0x62,0x19,0xc1,0x72,0x20,0x8d,
+ 0x01,0x04,0x63,0x73,0x19,0x0d,0x20,0x8d,
+ 0x01,0x04,0x65,0x20,0x13,0xb8,0x20,0x8d,
+ 0x01,0x04,0x65,0x64,0xae,0xf0,0x20,0x8d,
+ 0x01,0x04,0x66,0x84,0xf5,0x10,0x20,0x8d,
+ 0x01,0x04,0x67,0x0e,0xf4,0xbe,0x20,0x8d,
+ 0x01,0x04,0x67,0x4c,0x30,0x05,0x20,0x8d,
+ 0x01,0x04,0x67,0x54,0x54,0xfa,0x20,0x8f,
+ 0x01,0x04,0x67,0x63,0xa8,0x96,0x20,0x8d,
+ 0x01,0x04,0x67,0x6d,0x65,0xd8,0x20,0x8d,
+ 0x01,0x04,0x67,0x7a,0xf7,0x66,0x20,0x8d,
+ 0x01,0x04,0x67,0x81,0x0d,0x2d,0x20,0x8d,
+ 0x01,0x04,0x67,0xc6,0xc0,0x0e,0x4e,0x28,
+ 0x01,0x04,0x67,0xe0,0x77,0x63,0x20,0x8d,
+ 0x01,0x04,0x67,0xe7,0xbf,0x07,0x20,0x8d,
+ 0x01,0x04,0x67,0xeb,0xe6,0xc4,0x20,0x8d,
+ 0x01,0x04,0x68,0xab,0xf2,0x9b,0x20,0x8d,
+ 0x01,0x04,0x68,0xee,0xdc,0xc7,0x20,0x8d,
+ 0x01,0x04,0x6a,0xa3,0x9e,0x7f,0x20,0x8d,
+ 0x01,0x04,0x6b,0x96,0x29,0xb3,0x20,0x8d,
+ 0x01,0x04,0x6b,0x9f,0x5d,0x67,0x20,0x8d,
+ 0x01,0x04,0x6c,0xb7,0x4d,0x0c,0x20,0x8d,
+ 0x01,0x04,0x6d,0x09,0xaf,0x41,0x20,0x8d,
+ 0x01,0x04,0x6d,0x63,0x3f,0x9f,0x20,0x8d,
+ 0x01,0x04,0x6d,0x6e,0x51,0x5a,0x20,0x8d,
+ 0x01,0x04,0x6d,0x7b,0xd5,0x82,0x20,0x8d,
+ 0x01,0x04,0x6d,0x86,0xe8,0x51,0x20,0x8d,
+ 0x01,0x04,0x6d,0xa9,0x14,0xa8,0x20,0x8d,
+ 0x01,0x04,0x6d,0xc7,0xf1,0x94,0x20,0x8d,
+ 0x01,0x04,0x6d,0xe5,0xd2,0x06,0x20,0x8d,
+ 0x01,0x04,0x6d,0xec,0x69,0x28,0x20,0x8d,
+ 0x01,0x04,0x6d,0xf8,0xce,0x0d,0x20,0x8d,
+ 0x01,0x04,0x6f,0x2a,0x4a,0x41,0x20,0x8d,
+ 0x01,0x04,0x6f,0x5a,0x8c,0xb3,0x20,0x8d,
+ 0x01,0x04,0x70,0xd7,0xcd,0xec,0x20,0x8d,
+ 0x01,0x04,0x71,0x34,0x87,0x7d,0x20,0x8d,
+ 0x01,0x04,0x72,0x17,0xf6,0x89,0x20,0x8d,
+ 0x01,0x04,0x73,0x2f,0x8d,0xfa,0x22,0xb5,
+ 0x01,0x04,0x73,0x46,0x6e,0x04,0x20,0x8d,
+ 0x01,0x04,0x74,0x22,0xbd,0x37,0x20,0x8d,
+ 0x01,0x04,0x76,0x67,0x7e,0x8c,0x6e,0xad,
+ 0x01,0x04,0x76,0xbd,0xbb,0xdb,0x20,0x8d,
+ 0x01,0x04,0x77,0x03,0xd0,0xec,0x20,0x8d,
+ 0x01,0x04,0x77,0x08,0x2f,0xe1,0x20,0x8d,
+ 0x01,0x04,0x77,0x11,0x97,0x3d,0x20,0x8d,
+ 0x01,0x04,0x78,0x19,0x18,0x1e,0x20,0x8d,
+ 0x01,0x04,0x78,0xf1,0x22,0x0a,0x20,0x8d,
+ 0x01,0x04,0x79,0x62,0xcd,0x64,0x20,0x8d,
+ 0x01,0x04,0x7a,0x70,0x94,0x99,0x20,0x93,
+ 0x01,0x04,0x7a,0x74,0x2a,0x8c,0x20,0x8d,
+ 0x01,0x04,0x7c,0xd9,0xeb,0xb4,0x20,0x8d,
+ 0x01,0x04,0x7d,0xec,0xd7,0x85,0x20,0x8d,
+ 0x01,0x04,0x81,0x0d,0xbd,0xd4,0x20,0x8d,
+ 0x01,0x04,0x82,0xb9,0x4d,0x69,0x20,0x8d,
+ 0x01,0x04,0x83,0xbc,0x28,0xbf,0x20,0x8d,
+ 0x01,0x04,0x83,0xc1,0xdc,0x0f,0x20,0x8d,
+ 0x01,0x04,0x87,0x17,0x7c,0xef,0x20,0x8d,
+ 0x01,0x04,0x88,0x21,0xb9,0x20,0x20,0x8d,
+ 0x01,0x04,0x88,0x38,0xaa,0x60,0x20,0x8d,
+ 0x01,0x04,0x89,0xe2,0x22,0x2e,0x20,0x8d,
+ 0x01,0x04,0x8a,0xe5,0x1a,0x2a,0x20,0x8d,
+ 0x01,0x04,0x8b,0x09,0xf9,0xea,0x20,0x8d,
+ 0x01,0x04,0x8d,0x65,0x08,0x24,0x20,0x8d,
+ 0x01,0x04,0x8f,0xb0,0xe0,0x68,0x20,0x8d,
+ 0x01,0x04,0x90,0x02,0x45,0xe0,0x20,0x8d,
+ 0x01,0x04,0x90,0x22,0xa1,0x41,0x47,0x9d,
+ 0x01,0x04,0x90,0x5b,0x74,0x2c,0x20,0x8d,
+ 0x01,0x04,0x90,0x89,0x1d,0xb5,0x20,0x8d,
+ 0x01,0x04,0x94,0x42,0x32,0x32,0x20,0x8f,
+ 0x01,0x04,0x94,0x48,0x96,0xe7,0x20,0x8d,
+ 0x01,0x04,0x94,0xaa,0xd4,0x2c,0x20,0x8d,
+ 0x01,0x04,0x95,0xa7,0x63,0xbe,0x20,0x8d,
+ 0x01,0x04,0x9a,0x5c,0x10,0xbf,0x20,0x8d,
+ 0x01,0x04,0x9a,0xdd,0x1b,0x15,0x20,0x8d,
+ 0x01,0x04,0x9c,0x13,0x13,0x5a,0x20,0x8d,
+ 0x01,0x04,0x9c,0xf1,0x05,0xbe,0x20,0x8d,
+ 0x01,0x04,0x9d,0x0d,0x3d,0x4c,0x20,0x8d,
+ 0x01,0x04,0x9d,0x0d,0x3d,0x50,0x20,0x8d,
+ 0x01,0x04,0x9d,0xe6,0xa6,0x62,0x38,0x37,
+ 0x01,0x04,0x9e,0x4b,0xcb,0x02,0x20,0x8d,
+ 0x01,0x04,0x9e,0xb5,0x7d,0x96,0x20,0x8d,
+ 0x01,0x04,0x9e,0xb5,0xe2,0x21,0x20,0x8d,
+ 0x01,0x04,0x9f,0x64,0xf2,0xfe,0x20,0x8d,
+ 0x01,0x04,0x9f,0x64,0xf8,0xea,0x20,0x8d,
+ 0x01,0x04,0x9f,0x8a,0x57,0x12,0x20,0x8d,
+ 0x01,0x04,0xa0,0x10,0x00,0x1e,0x20,0x8d,
+ 0x01,0x04,0xa2,0x00,0xe3,0x36,0x20,0x8d,
+ 0x01,0x04,0xa2,0x00,0xe3,0x38,0x20,0x8d,
+ 0x01,0x04,0xa2,0x3e,0x12,0xe2,0x20,0x8d,
+ 0x01,0x04,0xa2,0xd1,0x01,0xe9,0x20,0x8d,
+ 0x01,0x04,0xa2,0xf3,0xaf,0x56,0x20,0x8d,
+ 0x01,0x04,0xa2,0xf4,0x50,0xd0,0x20,0x8d,
+ 0x01,0x04,0xa2,0xfa,0xbc,0x57,0x20,0x8d,
+ 0x01,0x04,0xa2,0xfa,0xbd,0x35,0x20,0x8d,
+ 0x01,0x04,0xa3,0x9e,0xca,0x70,0x20,0x8d,
+ 0x01,0x04,0xa3,0x9e,0xf3,0xe6,0x20,0x8d,
+ 0x01,0x04,0xa5,0x49,0x3e,0x1f,0x20,0x8d,
+ 0x01,0x04,0xa6,0x3e,0x52,0x67,0x80,0x03,
+ 0x01,0x04,0xa6,0x46,0x5e,0x6a,0x20,0x8d,
+ 0x01,0x04,0xa7,0x56,0x5a,0xef,0x20,0x8d,
+ 0x01,0x04,0xa9,0x2c,0x22,0xcb,0x20,0x8d,
+ 0x01,0x04,0xac,0x5d,0x65,0x49,0x20,0x8d,
+ 0x01,0x04,0xac,0x69,0x07,0x2f,0x20,0x8d,
+ 0x01,0x04,0xad,0x17,0x67,0x1e,0x1f,0x40,
+ 0x01,0x04,0xad,0x35,0x4f,0x06,0x20,0x8d,
+ 0x01,0x04,0xad,0x46,0x0c,0x56,0x20,0x8d,
+ 0x01,0x04,0xad,0x59,0x1c,0x89,0x20,0x8d,
+ 0x01,0x04,0xad,0xb0,0xb8,0x36,0x20,0x8d,
+ 0x01,0x04,0xad,0xd0,0x80,0x0a,0x20,0x8d,
+ 0x01,0x04,0xad,0xfe,0xcc,0x45,0x20,0x8d,
+ 0x01,0x04,0xad,0xff,0xcc,0x7c,0x20,0x8d,
+ 0x01,0x04,0xae,0x5e,0x9b,0xe0,0x20,0x8d,
+ 0x01,0x04,0xae,0x72,0x66,0x29,0x20,0x8d,
+ 0x01,0x04,0xae,0x72,0x7c,0x0c,0x20,0x8d,
+ 0x01,0x04,0xb0,0x0a,0xe3,0x3b,0x20,0x8d,
+ 0x01,0x04,0xb0,0x1f,0xe0,0xd6,0x20,0x8d,
+ 0x01,0x04,0xb0,0x4a,0x88,0xed,0x20,0x8d,
+ 0x01,0x04,0xb0,0x63,0x02,0xcf,0x20,0x8d,
+ 0x01,0x04,0xb0,0x6a,0xbf,0x02,0x20,0x8d,
+ 0x01,0x04,0xb0,0xa0,0xe4,0x09,0x20,0x8d,
+ 0x01,0x04,0xb0,0xbf,0xb6,0x03,0x20,0x8d,
+ 0x01,0x04,0xb0,0xd4,0xb9,0x99,0x20,0x8d,
+ 0x01,0x04,0xb0,0xf1,0x89,0xb7,0x20,0x8d,
+ 0x01,0x04,0xb1,0x26,0xd7,0x49,0x20,0x8d,
+ 0x01,0x04,0xb2,0x10,0xde,0x92,0x20,0x8d,
+ 0x01,0x04,0xb2,0x84,0x02,0xf6,0x20,0x8d,
+ 0x01,0x04,0xb2,0x8f,0xbf,0xab,0x20,0x8d,
+ 0x01,0x04,0xb2,0x94,0xac,0xd1,0x20,0x8d,
+ 0x01,0x04,0xb2,0x94,0xe2,0xb4,0x20,0x8d,
+ 0x01,0x04,0xb2,0x96,0x60,0x2e,0x20,0x8d,
+ 0x01,0x04,0xb2,0xb6,0xe3,0x32,0x20,0x8d,
+ 0x01,0x04,0xb2,0xec,0x89,0x3f,0x20,0x8d,
+ 0x01,0x04,0xb2,0xff,0x2a,0x7e,0x20,0x8d,
+ 0x01,0x04,0xb4,0x96,0x34,0x25,0x20,0x8d,
+ 0x01,0x04,0xb5,0x27,0x20,0x63,0x20,0x8d,
+ 0x01,0x04,0xb5,0x30,0x4d,0x1a,0x20,0x8d,
+ 0x01,0x04,0xb5,0x34,0xdf,0x34,0x20,0x8d,
+ 0x01,0x04,0xb5,0xee,0x33,0x98,0x20,0x8d,
+ 0x01,0x04,0xb7,0x58,0xdf,0xd0,0x20,0x8d,
+ 0x01,0x04,0xb7,0x6e,0xdc,0xd2,0x76,0x5d,
+ 0x01,0x04,0xb8,0x5f,0x3a,0xa6,0x20,0x90,
+ 0x01,0x04,0xb8,0xa4,0x93,0x52,0xa1,0x75,
+ 0x01,0x04,0xb8,0xab,0xd0,0x6d,0x20,0x8d,
+ 0x01,0x04,0xb9,0x19,0x30,0x27,0x20,0x8d,
+ 0x01,0x04,0xb9,0x19,0x30,0xb8,0x20,0x8d,
+ 0x01,0x04,0xb9,0x40,0x74,0x0f,0x20,0x8d,
+ 0x01,0x04,0xb9,0x50,0xdb,0x84,0x20,0x8d,
+ 0x01,0x04,0xb9,0x55,0x03,0x8c,0x20,0x8d,
+ 0x01,0x04,0xb9,0x5f,0xdb,0x35,0x20,0x8d,
+ 0x01,0x04,0xb9,0x6c,0xf4,0x29,0x20,0x8d,
+ 0x01,0x04,0xb9,0x86,0xe9,0x79,0x20,0x8d,
+ 0x01,0x04,0xb9,0x91,0x80,0x15,0x20,0x8d,
+ 0x01,0x04,0xb9,0x94,0x03,0xe3,0x20,0x8d,
+ 0x01,0x04,0xb9,0x99,0xc4,0xf0,0x20,0x8d,
+ 0x01,0x04,0xb9,0x9e,0x72,0xb8,0x20,0x8d,
+ 0x01,0x04,0xb9,0xa5,0xa8,0xc4,0x20,0x8d,
+ 0x01,0x04,0xb9,0xb5,0xe6,0x4a,0x20,0x8d,
+ 0x01,0x04,0xb9,0xb9,0x1a,0x8d,0x1f,0xaf,
+ 0x01,0x04,0xb9,0xba,0xd0,0xa2,0x20,0x8d,
+ 0x01,0x04,0xb9,0xbd,0x84,0xb2,0xe1,0xb4,
+ 0x01,0x04,0xb9,0xd3,0x3b,0x32,0x20,0x8d,
+ 0x01,0x04,0xb9,0xe9,0x94,0x92,0x20,0x8d,
+ 0x01,0x04,0xb9,0xee,0x81,0x71,0x20,0x8d,
+ 0x01,0x04,0xb9,0xf9,0xc7,0x6a,0x20,0x8d,
+ 0x01,0x04,0xb9,0xfb,0xa1,0x36,0x20,0x8d,
+ 0x01,0x04,0xbb,0xbd,0x99,0x88,0x20,0x8d,
+ 0x01,0x04,0xbc,0x25,0x18,0xbe,0x20,0x8d,
+ 0x01,0x04,0xbc,0x2a,0x28,0xea,0x47,0x9d,
+ 0x01,0x04,0xbc,0x3d,0x2e,0x24,0x20,0x8d,
+ 0x01,0x04,0xbc,0x44,0x2d,0x8f,0x20,0x8d,
+ 0x01,0x04,0xbc,0x7f,0xe5,0x69,0x20,0x8d,
+ 0x01,0x04,0xbc,0x86,0x06,0x54,0x20,0x8d,
+ 0x01,0x04,0xbc,0x86,0x08,0x24,0x20,0x8d,
+ 0x01,0x04,0xbc,0xd6,0x81,0x41,0x4e,0x2c,
+ 0x01,0x04,0xbc,0xe6,0xa8,0x72,0x20,0x8d,
+ 0x01,0x04,0xbd,0x22,0x0e,0x5d,0x20,0x8d,
+ 0x01,0x04,0xbd,0xcf,0x2e,0x20,0x20,0x8d,
+ 0x01,0x04,0xbe,0xd3,0xcc,0x44,0x20,0x8d,
+ 0x01,0x04,0xbf,0xd1,0x15,0xbc,0x20,0x8d,
+ 0x01,0x04,0xc0,0x03,0x0b,0x14,0x20,0x8d,
+ 0x01,0x04,0xc0,0x03,0xb9,0xd2,0x20,0x8d,
+ 0x01,0x04,0xc0,0x41,0xaa,0x0f,0x20,0x8d,
+ 0x01,0x04,0xc0,0x41,0xaa,0x32,0x20,0x8d,
+ 0x01,0x04,0xc0,0x92,0x89,0x12,0x20,0x8d,
+ 0x01,0x04,0xc0,0x9d,0xca,0xb2,0x20,0x8d,
+ 0x01,0x04,0xc0,0xe3,0x50,0x53,0x20,0x8d,
+ 0x01,0x04,0xc1,0x0a,0xcb,0x17,0x20,0x8e,
+ 0x01,0x04,0xc1,0x19,0x06,0xce,0x20,0x8d,
+ 0x01,0x04,0xc1,0x2a,0x6e,0x1e,0x20,0x8d,
+ 0x01,0x04,0xc1,0x3a,0xc4,0xd4,0x20,0x8d,
+ 0x01,0x04,0xc1,0x6a,0x1c,0x08,0x20,0x8d,
+ 0x01,0x04,0xc1,0xbd,0xbe,0x7b,0x20,0x8d,
+ 0x01,0x04,0xc1,0xc2,0xa3,0x23,0x20,0x8d,
+ 0x01,0x04,0xc1,0xc2,0xa3,0x35,0x20,0x8d,
+ 0x01,0x04,0xc2,0x0e,0xf6,0xcd,0x20,0x8d,
+ 0x01,0x04,0xc2,0x24,0x5b,0xfd,0x20,0x8d,
+ 0x01,0x04,0xc2,0x7e,0x71,0x87,0x20,0x8d,
+ 0x01,0x04,0xc2,0x87,0x87,0x45,0x20,0x8d,
+ 0x01,0x04,0xc3,0x38,0x3f,0x04,0x20,0x8d,
+ 0x01,0x04,0xc3,0x38,0x3f,0x05,0x20,0x8d,
+ 0x01,0x04,0xc3,0x43,0x8b,0x36,0x20,0x8d,
+ 0x01,0x04,0xc3,0x87,0xc2,0x08,0x20,0x8d,
+ 0x01,0x04,0xc3,0xca,0xa9,0x95,0x20,0x8d,
+ 0x01,0x04,0xc3,0xce,0x69,0x2a,0x20,0x8d,
+ 0x01,0x04,0xc3,0xd1,0xf9,0xa4,0x20,0x8d,
+ 0x01,0x04,0xc6,0x01,0xe7,0x06,0x20,0x8d,
+ 0x01,0x04,0xc6,0xc8,0x2b,0xd7,0x20,0x8d,
+ 0x01,0x04,0xc7,0xb6,0xb8,0xcc,0x20,0x8d,
+ 0x01,0x04,0xc7,0xf7,0x07,0xd0,0x20,0x8d,
+ 0x01,0x04,0xc7,0xf7,0xf9,0xbc,0x20,0x8d,
+ 0x01,0x04,0xc8,0x07,0xfc,0x76,0x20,0x8d,
+ 0x01,0x04,0xc8,0x14,0xba,0xfe,0x20,0x8d,
+ 0x01,0x04,0xc8,0x53,0xa6,0x88,0x20,0x8d,
+ 0x01,0x04,0xca,0x37,0x57,0x2d,0x20,0x8d,
+ 0x01,0x04,0xca,0x4f,0xa7,0x41,0x20,0x8d,
+ 0x01,0x04,0xca,0x6c,0xd3,0x87,0x20,0x8d,
+ 0x01,0x04,0xca,0xa9,0x66,0x49,0x20,0x8d,
+ 0x01,0x04,0xcb,0x82,0x30,0x75,0x22,0xb5,
+ 0x01,0x04,0xcb,0x84,0x5f,0x0a,0x20,0x8d,
+ 0x01,0x04,0xcb,0x97,0xa6,0x7b,0x20,0x8d,
+ 0x01,0x04,0xcc,0x5d,0x71,0x6c,0x20,0x8d,
+ 0x01,0x04,0xcc,0x6f,0xf1,0xc3,0x20,0x8d,
+ 0x01,0x04,0xce,0x7c,0x95,0x42,0x20,0x8d,
+ 0x01,0x04,0xcf,0x73,0x66,0x62,0x20,0x8d,
+ 0x01,0x04,0xcf,0xe5,0x2e,0x96,0x20,0x8d,
+ 0x01,0x04,0xd0,0x4c,0xfc,0xc6,0x20,0x8d,
+ 0x01,0x04,0xd0,0x64,0x0d,0x38,0x20,0x8d,
+ 0x01,0x04,0xd0,0x64,0xb2,0xaf,0x20,0x8d,
+ 0x01,0x04,0xd0,0x6e,0x63,0x69,0x20,0x8d,
+ 0x01,0x04,0xd1,0x06,0xd2,0xb3,0x20,0x8d,
+ 0x01,0x04,0xd1,0x85,0xdc,0x4a,0x20,0x8d,
+ 0x01,0x04,0xd1,0x8d,0x39,0x39,0x20,0x8d,
+ 0x01,0x04,0xd3,0x1b,0x93,0x43,0x20,0x8d,
+ 0x01,0x04,0xd4,0x22,0xe1,0x76,0x20,0x8d,
+ 0x01,0x04,0xd4,0x59,0xad,0xd8,0x20,0x8d,
+ 0x01,0x04,0xd4,0x63,0xe2,0x24,0x23,0x3c,
+ 0x01,0x04,0xd4,0xed,0x60,0x62,0x20,0x8d,
+ 0x01,0x04,0xd5,0x59,0x83,0x35,0x20,0x8d,
+ 0x01,0x04,0xd8,0x26,0x81,0xa4,0x20,0x8d,
+ 0x01,0x04,0xd8,0x86,0xa5,0x37,0x20,0x8d,
+ 0x01,0x04,0xd8,0x92,0xfb,0x08,0x20,0x8d,
+ 0x01,0x04,0xd8,0xbd,0xbe,0x5f,0x20,0x8d,
+ 0x01,0x04,0xd8,0xe2,0x80,0xbd,0x20,0x8d,
+ 0x01,0x04,0xd8,0xec,0xa4,0x52,0x20,0x8d,
+ 0x01,0x04,0xd9,0x13,0xd8,0xd2,0x20,0x8d,
+ 0x01,0x04,0xd9,0x1a,0x20,0x0a,0x20,0x8d,
+ 0x01,0x04,0xd9,0x40,0x2f,0x8a,0x20,0x8d,
+ 0x01,0x04,0xd9,0x40,0x85,0xdc,0x20,0x8d,
+ 0x01,0x04,0xd9,0x5c,0x37,0xf6,0x20,0x8d,
+ 0x01,0x04,0xda,0x1f,0x71,0xf5,0x20,0x8d,
+ 0x01,0x04,0xda,0xff,0xf2,0x72,0x20,0x8d,
+ 0x01,0x04,0xdc,0x85,0x27,0x3d,0x20,0x8d,
+ 0x01,0x04,0xdf,0x10,0x1e,0xaf,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x19,0xf0,0x60,0x01,0x30,0x6f,0x0e,0xc4,0x7a,0xff,0xfe,0x8f,0x66,0xec,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x1b,0xc0,0x00,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x01,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x1c,0x02,0x2f,0x18,0x0d,0x00,0xb6,0x2e,0x99,0xff,0xfe,0x49,0xd4,0x92,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x41,0x00,0x00,0x00,0x00,0x64,0xdc,0xaf,0xaf,0xff,0xfe,0x00,0x67,0x07,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x04,0x70,0x00,0x0a,0x0c,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x48,0x01,0x78,0x19,0x00,0x74,0xb7,0x45,0xb9,0xd5,0xff,0x10,0xa6,0x1a,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x4b,0xa0,0xff,0xfa,0x00,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x10,0x19,0x08,0xff,0x01,0xf8,0x16,0x3e,0xff,0xfe,0x33,0x2e,0x32,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x38,0xa0,0x00,0x41,0x40,0x00,0x00,0x00,0x00,0xff,0xff,0x01,0x91,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x48,0x28,0x00,0x01,0x31,0x4b,0x1f,0xf6,0xfc,0x20,0xf7,0xf9,0x9f,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x78,0x07,0xdc,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x78,0x0c,0xc8,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x10,0x00,0x88,0x4e,0x28,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x12,0x20,0x08,0x0c,0x00,0x00,0x00,0x00,0x93,0xe5,0x0d,0xd2,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x12,0x20,0x08,0x0c,0xe5,0xdc,0xad,0x0c,0x92,0x89,0xc2,0x8f,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x16,0xdc,0x12,0x01,0x50,0x54,0x00,0xff,0xfe,0x17,0x4d,0xac,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x23,0x54,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x26,0xb4,0x00,0x12,0x7a,0xe3,0xb5,0xff,0xfe,0x04,0x6f,0x9c,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x06,0x7c,0x02,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xfa,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x07,0x18,0x08,0x01,0x03,0x11,0x50,0x54,0x00,0xff,0xfe,0x19,0xc4,0x83,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x08,0xd8,0x08,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x99,0x03,0xc1,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x08,0xf1,0x14,0x04,0x37,0x00,0x8e,0x49,0x71,0x5a,0x2e,0x09,0xb6,0x34,0x24,0xe4,
+ 0x02,0x10,0x20,0x01,0x0b,0x07,0x5d,0x29,0x99,0xa5,0x19,0x4b,0x38,0x74,0xd6,0x5e,0xa9,0x0d,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xa8,0x01,0xf1,0xf0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0b,0xc8,0x12,0x00,0x00,0x00,0xda,0xc4,0x97,0xff,0xfe,0x2a,0x35,0x54,0x4e,0x28,
+ 0x02,0x10,0x20,0x01,0x0d,0xa8,0x10,0x0d,0x00,0x22,0x10,0xfa,0xd8,0x5f,0x10,0xf2,0x21,0xfd,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0d,0xa8,0x80,0x01,0x7a,0x39,0xf0,0x35,0x00,0x7d,0xb9,0x9f,0xeb,0x79,0x20,0x8d,
+ 0x02,0x10,0x20,0x01,0x0e,0x42,0x01,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x24,0x12,0x01,0x03,0xc9,0x00,0x08,0x25,0x8f,0x20,0xea,0xff,0x65,0xc2,0x20,0x8d,
+ 0x02,0x10,0x24,0x00,0x40,0x52,0x0e,0x20,0x4f,0x00,0x69,0xfe,0xbb,0x33,0x7b,0x1c,0xa1,0xca,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0x18,0x00,0x78,0x00,0x01,0x05,0xbe,0x76,0x4e,0xff,0xfe,0x1c,0x0b,0x35,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0x39,0x00,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xb1,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x01,0x50,0x20,0x8d,
+ 0x02,0x10,0x24,0x01,0xd0,0x02,0x44,0x02,0x00,0x00,0x8f,0x28,0x59,0x1a,0x6e,0xa0,0xc6,0x83,0x20,0x8d,
+ 0x02,0x10,0x24,0x03,0x62,0x00,0x88,0x21,0x3d,0x68,0x19,0x5b,0x87,0xe9,0x68,0x19,0xd5,0xc8,0x20,0x8d,
+ 0x02,0x10,0x24,0x05,0x65,0x80,0x21,0x40,0x3a,0x00,0xc2,0x8c,0x09,0x83,0x36,0x4b,0x5d,0x70,0x20,0x8d,
+ 0x02,0x10,0x24,0x05,0x98,0x00,0xb9,0x11,0xa1,0x8a,0x58,0xeb,0xcd,0x3c,0x9d,0x82,0xea,0x4a,0x20,0x8d,
+ 0x02,0x10,0x24,0x05,0xaa,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x20,0x8d,
+ 0x02,0x10,0x24,0x09,0x00,0x10,0xca,0x20,0x1d,0xf0,0x02,0x24,0xe8,0xff,0xfe,0x1f,0x60,0xd9,0x20,0x8d,
+ 0x02,0x10,0x24,0x09,0x8a,0x1e,0xa9,0xaf,0x36,0x60,0x1c,0x5a,0x5b,0x6b,0x8a,0x2d,0x98,0x48,0x20,0x8d,
+ 0x02,0x10,0x24,0x09,0x8a,0x1e,0xa9,0xaf,0x36,0x60,0x04,0x04,0x39,0xba,0x88,0xf2,0xe8,0xdf,0x20,0x8d,
+ 0x02,0x10,0x24,0x0b,0x00,0x10,0x91,0x41,0x04,0x00,0x49,0xb4,0x3a,0x2e,0x01,0xe5,0x08,0x4c,0x20,0x8d,
+ 0x02,0x10,0x24,0x0d,0x00,0x1a,0x07,0x59,0x60,0x00,0xa7,0xb1,0x45,0x1a,0x88,0x74,0xe1,0xac,0x20,0x8d,
+ 0x02,0x10,0x24,0x0d,0x00,0x1a,0x07,0x59,0x60,0x00,0xdd,0xab,0x31,0x41,0x4d,0xa0,0x88,0x78,0x20,0x8d,
+ 0x02,0x10,0x26,0x00,0x88,0x05,0x24,0x00,0x01,0x4e,0x12,0xdd,0xb1,0xff,0xfe,0xf2,0x30,0x13,0x20,0x8d,
+ 0x02,0x10,0x26,0x01,0x06,0x02,0x8d,0x80,0x0b,0x63,0xdc,0x3e,0x24,0xff,0xfe,0x92,0x05,0xeb,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0xff,0xb6,0x00,0x04,0x27,0x98,0xf8,0x16,0x3e,0xff,0xfe,0x2f,0x54,0x41,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0xff,0xb6,0x00,0x04,0x73,0x9e,0xf8,0x16,0x3e,0xff,0xfe,0x00,0xc2,0xb3,0x20,0x8d,
+ 0x02,0x10,0x26,0x02,0xff,0xb8,0x00,0x00,0x00,0x00,0x02,0x08,0x00,0x72,0x00,0x57,0x02,0x00,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x13,0x80,0x41,0x11,0x93,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x43,0x00,0x00,0x0a,0x00,0x2e,0x02,0x1b,0x21,0xff,0xfe,0x11,0x03,0x92,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2e,0x06,0x1f,0xb0,
+ 0x02,0x10,0x26,0x04,0x55,0x00,0x70,0x6a,0x40,0x00,0xfc,0x79,0xb9,0xbb,0x01,0xd7,0xc3,0x25,0x20,0x8d,
+ 0x02,0x10,0x26,0x04,0x55,0x00,0xc1,0x34,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc,0x80,0x1d,
+ 0x02,0x10,0x26,0x04,0x68,0x00,0x5e,0x11,0x01,0x62,0x5c,0x8f,0xd2,0xff,0xfe,0x26,0x14,0x6f,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0x64,0x00,0x00,0x20,0x13,0xbf,0xdf,0x1d,0x18,0x1c,0x83,0xbb,0x22,0xe8,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0xae,0x00,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x20,0x8d,
+ 0x02,0x10,0x26,0x05,0xc0,0x00,0x2a,0x0a,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xf2,0xc0,0xf0,0x0e,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xf2,0xf8,0xad,0x40,0x0b,0xc1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xf4,0x70,0x00,0x08,0x10,0x48,0xae,0x1f,0x6b,0xff,0xfe,0x70,0x72,0x40,0x20,0x8d,
+ 0x02,0x10,0x26,0x07,0xff,0x28,0x80,0x0f,0x00,0x97,0x02,0x25,0x90,0xff,0xfe,0x75,0x11,0x10,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x01,0x1c,0x50,0x01,0x11,0x18,0xd2,0x67,0xe5,0xff,0xfe,0xe9,0xe6,0x73,0x20,0x8d,
+ 0x02,0x10,0x26,0x20,0x00,0x6e,0xa0,0x00,0x20,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x20,0x8d,
+ 0x02,0x10,0x28,0x04,0x01,0x4d,0x4c,0x93,0x98,0x09,0x97,0x69,0xda,0x80,0x18,0x32,0x34,0x80,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x13,0x28,0xe1,0x01,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x63,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x13,0x98,0x00,0x04,0x2a,0x03,0x02,0x15,0x5d,0xff,0xfe,0xd6,0x10,0x33,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x13,0xa0,0x30,0x15,0x00,0x01,0x00,0x85,0x00,0x14,0x00,0x79,0x00,0x26,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x16,0x30,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x17,0x68,0x20,0x01,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x6a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x18,0x28,0xa0,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x66,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x18,0x38,0x00,0x36,0x00,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0xcb,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x18,0x38,0x00,0x36,0x00,0x7d,0x00,0x00,0x00,0x00,0x00,0x00,0xd3,0xc6,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x58,0xf7,0xe0,0xff,0xfe,0x24,0xa0,0xba,0x56,0xcc,
+ 0x02,0x10,0x2a,0x00,0x1c,0x10,0x00,0x02,0x07,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x17,0x56,0xcc,
+ 0x02,0x10,0x2a,0x00,0x1f,0x40,0x50,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x60,0x20,0x13,0x95,0x14,0x00,0xba,0xf7,0x2d,0x43,0x60,0xb3,0x19,0x8b,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x7c,0x80,0x00,0x00,0x01,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xaf,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x8a,0x60,0xe0,0x12,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xab,0x00,0x06,0x03,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xbb,0xe0,0x00,0xcc,0x00,0x00,0x62,0xa4,0x4c,0xff,0xfe,0x23,0x75,0x10,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0c,0xa8,0x0a,0x1f,0x30,0x25,0xf9,0x49,0xe4,0x42,0xc9,0x40,0x13,0xe8,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd2,0xa0,0x00,0x0a,0x3d,0x00,0x1c,0xdf,0x38,0xbb,0xa7,0xd6,0xc2,0x51,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xd8,0x80,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0e,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0x0e,0xc0,0x72,0x07,0x91,0x00,0x5f,0x8f,0x25,0xdd,0x25,0x74,0x39,0x82,0x20,0x8d,
+ 0x02,0x10,0x2a,0x00,0xf8,0x20,0x04,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x01,0x38,0xa0,0x17,0xb0,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0x30,0x00,0x17,0x00,0x01,0x00,0x00,0x00,0x00,0xff,0xff,0x11,0x53,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0x90,0x00,0x16,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x4b,0x00,0x80,0x7c,0x1b,0x00,0xcd,0xa1,0x0c,0x6a,0x2b,0xad,0x24,0x18,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x4b,0x00,0x80,0xe7,0x54,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x04,0xf8,0x01,0x92,0x42,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0xf1,
+ 0x02,0x10,0x2a,0x01,0x07,0xa0,0x00,0x02,0x13,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xa7,0x00,0x02,0x14,0x67,0x0e,0xc4,0x7a,0xff,0xfe,0xe2,0x56,0x90,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x02,0x01,0x0f,0x50,0x54,0x00,0xff,0xfe,0x5c,0xda,0xc7,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x07,0xc8,0xd0,0x02,0x03,0x18,0x50,0x54,0x00,0xff,0xfe,0xbe,0xcb,0xb1,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x87,0x40,0x00,0x01,0xff,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x8c,0x6a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0xcb,0x00,0x0f,0x98,0xca,0x00,0x50,0x54,0x00,0xff,0xfe,0xd4,0x76,0x3d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0xcb,0x14,0x0c,0xf6,0xbc,0x00,0x21,0xe5,0xf1,0x2e,0x32,0xc8,0x01,0x45,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x00,0xd0,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x45,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x00,0xd0,0xbe,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x20,0x8d,
+ 0x02,0x10,0x2a,0x01,0x0e,0x35,0x2e,0x40,0x68,0x30,0x02,0x11,0x32,0xff,0xfe,0xa6,0xde,0x3d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x12,0x05,0xc6,0xaa,0x60,0xc0,0x70,0xd8,0xaa,0xee,0xa8,0x2d,0x99,0x3c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x69,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x01,0x80,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x5b,0x8f,0x53,0x8c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x03,0x48,0x00,0x62,0x5e,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x03,0x90,0x90,0x00,0x00,0x00,0x02,0x18,0x7d,0xff,0xfe,0x10,0xbe,0x33,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7a,0xa0,0x16,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xdc,0x8d,0xe0,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0xb0,0xdf,0x89,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x7b,0x40,0xb9,0x05,0x37,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x81,0x0d,0x8c,0xbf,0xf3,0xa8,0x96,0xc6,0x91,0xff,0xfe,0x17,0xae,0x1d,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x83,0x89,0x01,0xc0,0x96,0x80,0x02,0x01,0x2e,0xff,0xfe,0x82,0xb3,0xcc,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0xa4,0x54,0xa5,0x16,0x00,0x01,0x05,0x17,0x09,0x28,0x7e,0x0d,0x95,0x7c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x0a,0xf8,0xfa,0xb0,0x08,0x04,0x01,0x51,0x02,0x36,0x00,0x34,0x01,0x61,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x0a,0xf8,0xfa,0xb0,0x08,0x08,0x00,0x85,0x02,0x34,0x01,0x45,0x01,0x32,0x20,0x8d,
+ 0x02,0x10,0x2a,0x02,0x0e,0x00,0xff,0xf0,0x01,0xe2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x22,0x60,0x30,0x06,0x00,0x0d,0xd3,0x07,0x5d,0x1d,0x32,0xca,0x1f,0xe8,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x60,0x00,0x08,0x70,0x00,0x00,0x00,0x46,0x00,0x23,0x00,0x87,0x02,0x18,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0x9d,0xa0,0x00,0xf6,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xc9,0x80,0x00,0xdb,0x00,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x8d,
+ 0x02,0x10,0x2a,0x03,0xe2,0xc0,0x01,0xce,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x35,0x44,0x10,0x00,0x15,0x10,0x70,0x6c,0xab,0xff,0xfe,0x6c,0x50,0x1c,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x01,0x03,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0x87,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0x52,0xc0,0x01,0x01,0x03,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0x4c,0x27,0x20,0x8d,
+ 0x02,0x10,0x2a,0x04,0xee,0x41,0x00,0x83,0x50,0xdf,0xd9,0x08,0xf7,0x1d,0x2a,0x86,0xb3,0x37,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0x6d,0x40,0xb9,0x4e,0xd1,0x00,0x02,0x25,0x90,0xff,0xfe,0x0d,0xcf,0xc2,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xe5,0xc0,0x00,0x00,0x01,0x00,0x02,0x50,0x56,0xff,0xfe,0xb9,0xd6,0xcb,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xfc,0x87,0x00,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x05,0xfc,0x87,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x20,0x8d,
+ 0x02,0x10,0x2a,0x07,0x57,0x41,0x00,0x00,0x11,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2a,0x07,0xa8,0x80,0x46,0x01,0x10,0x62,0xb4,0xb4,0xbd,0x2a,0x39,0xd4,0x7a,0xcf,0xc8,0xc9,
+ 0x02,0x10,0x2a,0x07,0xab,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x09,0x46,0x20,0x8d,
+ 0x02,0x10,0x2a,0x07,0xb4,0x00,0x00,0x01,0x03,0x4c,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x02,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0a,0x8c,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb4,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0a,0xc8,0x01,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x83,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0b,0xae,0x40,0x00,0x03,0x4a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x20,0x8d,
+ 0x02,0x10,0x2a,0x0f,0xdf,0x00,0x00,0x00,0x02,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x20,0x8d,
+ 0x02,0x10,0x2c,0x0f,0xf5,0x98,0x00,0x05,0x00,0x01,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x02,0x10,0x2c,0x0f,0xfc,0xe8,0x00,0x00,0x04,0x00,0x0b,0x7c,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
+ 0x04,0x20,0xd1,0xbb,0x02,0x8d,0x4d,0xd5,0x6a,0x20,0xc0,0xf9,0x16,0x2b,0x84,0x22,0x66,0xe0,0x89,0x45,0x60,0x37,0x52,0xe2,0x0b,0xa5,0xb4,0xf8,0x26,0xb3,0x8f,0x5a,0x30,0xed,0x20,0x8d,
+ 0x04,0x20,0xd2,0x59,0x3b,0xd7,0x14,0x7e,0xd0,0x98,0xfe,0x9e,0xa5,0x69,0xf4,0x26,0x6d,0x72,0x6f,0xc3,0x76,0xce,0x1d,0x40,0x41,0xa2,0xa1,0xaf,0xf9,0x6e,0x57,0x2d,0x9d,0xc3,0x20,0x8d,
+ 0x04,0x20,0xdf,0xd9,0xed,0x59,0xbf,0x1e,0x77,0x48,0x3c,0x13,0x3b,0xc5,0xc8,0x15,0x86,0x88,0x68,0xf0,0x08,0xe9,0xee,0x9b,0x3d,0xa4,0x33,0x0a,0x68,0x67,0x86,0x9d,0xe2,0x83,0x20,0x8d,
+ 0x04,0x20,0xe9,0xbf,0xa7,0xbd,0x9b,0x54,0x54,0xe8,0xc8,0xae,0x78,0x99,0xa0,0xa3,0xf6,0x5d,0x78,0xe3,0x9e,0x5c,0xa7,0x18,0xb9,0x13,0x0c,0x04,0x9b,0xf3,0x7f,0x27,0x18,0xb0,0x20,0x8d,
+ 0x04,0x20,0xf8,0x8d,0x64,0xd2,0xc8,0xe9,0x0f,0x51,0x03,0x1c,0x98,0x33,0x8f,0xe0,0x1e,0xe7,0xb6,0x16,0x8d,0x2a,0xf5,0xf3,0x19,0xce,0xdd,0x9e,0xee,0x17,0xc3,0x8f,0xd6,0xa1,0x20,0x8d,
+ 0x04,0x20,0xfb,0xf1,0x17,0xd6,0x03,0x3b,0x01,0x8b,0x98,0xcf,0x16,0x20,0xde,0xaf,0x6c,0xed,0x60,0xab,0x6e,0x14,0x0b,0x58,0x6b,0x2d,0xf8,0x06,0x98,0x37,0x7a,0xff,0x7a,0x0f,0x20,0x8d,
+ 0x04,0x20,0x0f,0xb9,0x71,0x05,0x64,0x83,0x2c,0x68,0x6a,0x9c,0xf0,0x4f,0xc3,0x90,0xcd,0x5c,0x73,0x9a,0xdd,0xb3,0xc6,0x42,0xca,0x09,0xbb,0xcc,0xfe,0x29,0x49,0x9f,0xc7,0x28,0x20,0x8d,
+ 0x04,0x20,0x22,0x6e,0x42,0xe4,0xbd,0x2b,0xe5,0x3e,0x30,0xda,0x8a,0x03,0xf3,0x45,0x52,0xac,0x84,0xbf,0xbf,0xc5,0xaa,0x5f,0xe0,0x1b,0x26,0x28,0xb5,0x83,0x2e,0xed,0x4c,0xee,0x20,0x8d,
+ 0x04,0x20,0x2a,0x47,0x8b,0xa0,0x4f,0x67,0x1d,0xcd,0x5d,0x84,0x1a,0xec,0xbd,0xd2,0xaa,0xe9,0x99,0x01,0x96,0x5d,0x4e,0xff,0x64,0x47,0xba,0xde,0xbf,0x56,0x89,0x39,0xac,0xde,0x20,0x8d,
+ 0x04,0x20,0x2b,0xf3,0xe8,0xf5,0xef,0x90,0x14,0xab,0x61,0xe9,0x11,0x97,0x9f,0x18,0x4d,0xb4,0xff,0x89,0x94,0xf7,0x92,0x94,0x53,0xe6,0x9e,0xd4,0xdb,0x85,0x89,0x4d,0x3e,0xc9,0x20,0x8d,
+ 0x04,0x20,0x2e,0x4e,0xde,0x51,0xd7,0x28,0x4b,0x29,0x7c,0xff,0x1f,0x8a,0x50,0xb7,0x5e,0xf0,0x81,0xcd,0xe8,0x8a,0x08,0x73,0x58,0x4e,0x43,0x1f,0x7b,0x85,0x9a,0xed,0xe2,0x68,0x20,0x8d,
+ 0x04,0x20,0x35,0xdd,0xd0,0x36,0xa5,0x69,0x4a,0xd2,0xcc,0xb8,0xe9,0x62,0xa3,0x55,0xeb,0x86,0xe2,0xf3,0x03,0x48,0x26,0xe6,0x20,0xad,0xda,0xaa,0xff,0xde,0x16,0xad,0x39,0x9d,0x20,0x8d,
+ 0x04,0x20,0x41,0x47,0x4e,0xc2,0xa1,0x71,0x63,0x3e,0x11,0x54,0x46,0x91,0x80,0xed,0x41,0x16,0x32,0x29,0x19,0x60,0xc9,0xef,0xa3,0xb7,0x96,0x2c,0x94,0xa8,0xdf,0x55,0xd7,0x21,0x20,0x8d,
+ 0x04,0x20,0x44,0xf3,0xb7,0x5e,0x48,0x3c,0xbd,0xa6,0x52,0xaa,0x68,0xb5,0xbf,0xdc,0x01,0x5f,0x4b,0xeb,0x7a,0x25,0xcb,0x4a,0x70,0xbc,0x18,0x8c,0x97,0x5d,0x27,0x54,0x09,0x17,0x20,0x8d,
+ 0x04,0x20,0x5c,0x52,0x7f,0x17,0x16,0x4c,0x27,0x36,0x2d,0x05,0xa1,0x19,0x0d,0xbe,0x87,0xab,0x24,0x7b,0xe7,0x38,0x3b,0xa1,0x7f,0xd1,0xd4,0x28,0x16,0x8e,0xfc,0x98,0x7d,0x08,0x20,0x8d,
+ 0x04,0x20,0x67,0xc4,0x17,0xa5,0xcb,0x77,0xbd,0xaa,0x11,0x7f,0x8b,0xc0,0x81,0xf3,0xc0,0x96,0x9d,0x31,0x27,0x9c,0xad,0x6c,0x6d,0x98,0x42,0x70,0xdb,0x50,0x12,0x96,0x0b,0x36,0x20,0x8d,
+ 0x04,0x20,0x73,0xdb,0x82,0xe0,0x88,0x40,0x49,0xd8,0x3b,0xa0,0xdd,0x83,0x7c,0x84,0x3c,0xb8,0xd0,0x03,0x0b,0x7a,0x08,0x44,0x4e,0x79,0xd6,0x61,0x23,0x31,0xa9,0xb3,0x07,0x58,0x20,0x8d,
+ 0x04,0x20,0x75,0x93,0x21,0xdd,0x99,0x58,0x3c,0x3f,0xae,0x36,0x50,0x58,0x49,0xe2,0xd0,0xc3,0x3a,0x2c,0x4a,0xcf,0x41,0xc4,0x82,0x48,0xab,0xec,0x07,0x5d,0x56,0x2c,0xb4,0x8d,0x20,0x8d,
+ 0x04,0x20,0x87,0xd4,0x66,0x0f,0xed,0xf9,0xf5,0xf1,0xcb,0x85,0x37,0xec,0xe1,0x19,0xa8,0xa4,0x03,0xb7,0x13,0x59,0xbb,0xf8,0xd2,0x93,0x92,0x50,0xfa,0x30,0x7a,0xd8,0x43,0xd0,0x20,0x8d,
+ 0x04,0x20,0x8b,0xfe,0xad,0x19,0xdb,0x97,0x57,0x84,0xec,0xad,0x4f,0xb2,0xdf,0x69,0x53,0x04,0x57,0x19,0x16,0x7a,0x71,0xd7,0x2b,0xab,0x03,0xfd,0x76,0x4d,0xa0,0x70,0xc3,0xe7,0x20,0x8d,
+ 0x04,0x20,0x96,0x25,0xde,0x4a,0xbc,0xbd,0x76,0x76,0xee,0x43,0x45,0x76,0xe0,0x0d,0x99,0x83,0xcd,0x83,0x8f,0x94,0xe5,0xde,0x7a,0xf2,0xf0,0x57,0xb8,0x25,0x54,0x17,0xcb,0x3b,0x20,0x8d,
+ 0x04,0x20,0x98,0xc6,0x44,0x27,0x90,0x41,0xa6,0x98,0xf9,0x25,0x6c,0x59,0x0f,0x06,0x6d,0x44,0x59,0x0e,0xb2,0x46,0xb0,0xa4,0x37,0x88,0x69,0x8f,0xc1,0x32,0xcd,0x9f,0x15,0xd7,0x20,0x8d,
+ 0x04,0x20,0xaa,0x3a,0x16,0x86,0xea,0x59,0x09,0x04,0x78,0xe5,0x10,0x92,0xe1,0x1d,0xad,0xf7,0x56,0x2b,0xac,0xb0,0x97,0x29,0x63,0x30,0xf4,0x1b,0xcf,0xde,0xf3,0x28,0x0a,0x29,0x20,0x8d,
+ 0x04,0x20,0xbc,0x27,0xae,0x89,0xc1,0x67,0x73,0x0a,0x08,0x02,0xdf,0xb7,0xcc,0x94,0xc7,0x9f,0xf4,0x72,0x7a,0x9b,0x20,0x0c,0x5c,0x11,0x3d,0x22,0xd6,0x13,0x88,0x66,0x74,0xbf,0x20,0x8d,
+ 0x05,0x20,0xfe,0x97,0xba,0x09,0x2a,0xa4,0x85,0x10,0xa1,0x04,0x7b,0x88,0x7a,0x5a,0x06,0x53,0x71,0x93,0x3b,0xf9,0xa2,0x2f,0xd9,0xe3,0x8f,0xa5,0xa2,0xac,0x1e,0x6c,0x6c,0x8c,0x20,0x8d,
+ 0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x20,0x8d,
+ 0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x20,0x8d,
+ 0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x20,0x8d,
+ 0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x20,0x8d,
+ 0x05,0x20,0x7a,0x65,0xf7,0x47,0x42,0x9d,0x66,0x42,0x3b,0xb3,0xa7,0x03,0x6c,0x46,0x78,0x19,0x28,0x78,0x1e,0xa3,0x7c,0x67,0x44,0xb7,0x83,0x05,0xe3,0xfe,0xa5,0xe4,0x0a,0x6e,0x20,0x8d,
+ 0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x20,0x8d,
+ 0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x20,0x8d,
};
-static SeedSpec6 pnSeed6_test[] = {
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xcb,0x26,0x31,0xba,0x48,0x51,0x31,0x39,0x0d}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x44,0xf4,0xf4,0xf0,0xbf,0xf7,0x7e,0x6d,0xc4,0xe8}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x6a,0x8b,0xd2,0x78,0x3f,0x7a,0xf8,0x92,0x8f,0x80}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xe6,0x4e,0xa4,0x47,0x4e,0x2a,0xfe,0xe8,0x95,0xcc}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x9f,0xae,0x9f,0x59,0x0b,0x3f,0x31,0x3a,0x8a,0x5f}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x47,0xb1,0xe4,0x55,0xd1,0xb0,0x14,0x3f,0xb6,0xdb}, 18333},
- {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa0,0x60,0x9e,0x46,0x54,0xdb,0x61,0x3b,0xb2,0x6f}, 18333}
+static const uint8_t chainparams_seed_test[] = {
+ 0x04,0x20,0xdf,0x55,0xaa,0x83,0xd5,0xc5,0xb8,0xe7,0x75,0x78,0xd4,0x29,0x51,0x4b,0x26,0x1c,0x23,0xdf,0x28,0x4d,0x29,0x85,0x07,0xb5,0xe2,0x29,0x69,0x3e,0x25,0xbb,0x61,0xcf,0x47,0x9d,
+ 0x04,0x20,0x0a,0xdd,0xa2,0x48,0xb5,0x56,0xa3,0x1f,0xca,0x3c,0x4c,0x9e,0xca,0x6e,0xb3,0xd5,0x5e,0x68,0xf6,0x28,0x31,0x57,0x24,0xfb,0x9d,0x2b,0x55,0x4f,0xd7,0x90,0x62,0xd3,0x47,0x9d,
+ 0x04,0x20,0x2d,0x04,0xa1,0x4a,0xd4,0x7c,0x7b,0x16,0x2e,0xb7,0xd2,0xa1,0x08,0xc5,0xd2,0xbd,0x53,0x87,0x34,0xdc,0x38,0x26,0xca,0x56,0xf2,0xac,0xc5,0x62,0x70,0x72,0x3f,0x63,0x47,0x9d,
+ 0x04,0x20,0x30,0x57,0x85,0xe0,0x02,0x4a,0xd1,0x31,0xeb,0x16,0x1b,0x1d,0xa8,0x43,0x0b,0xb4,0xc6,0xac,0x7d,0x46,0x24,0x0b,0x55,0x9d,0x16,0xe6,0x46,0x03,0x72,0xfe,0xd4,0xef,0x47,0x9d,
+ 0x04,0x20,0x36,0x6c,0xf1,0xd2,0xbb,0xda,0xff,0x8c,0x93,0x61,0x10,0xf2,0x9d,0xa1,0xa4,0x0a,0x30,0x9b,0x0c,0x69,0x6d,0xaa,0xd4,0x9c,0xfd,0xb5,0x5b,0x5e,0x30,0x9f,0xf3,0x13,0x47,0x9d,
+ 0x04,0x20,0x3e,0xe2,0xf3,0xe5,0xc5,0xbe,0x61,0xdd,0x4c,0x3e,0xdb,0x0d,0xd2,0xf9,0x42,0xe3,0x31,0xb2,0xa8,0x51,0x31,0xf6,0xce,0xc2,0x38,0x20,0x27,0x39,0x73,0x68,0x5a,0x42,0x47,0x9d,
+ 0x04,0x20,0x51,0x79,0x05,0x9c,0x8a,0xdf,0x03,0xb5,0x1b,0x17,0xc3,0x86,0xb6,0x54,0xcc,0xe0,0x6e,0x58,0xa6,0x41,0x4c,0xcc,0x0c,0x60,0x08,0xa6,0x0f,0x1d,0x11,0xd8,0x29,0xa6,0x47,0x9d,
+ 0x04,0x20,0x60,0xbe,0xae,0x7d,0xa3,0x4d,0x6a,0x71,0x1a,0x5d,0xe5,0x98,0x9c,0xde,0xa0,0x99,0x39,0x19,0xd3,0x01,0x0a,0x5d,0x1c,0x21,0x43,0x94,0x92,0x71,0x5d,0x77,0xd7,0xdf,0x47,0x9d,
+ 0x04,0x20,0x64,0x4e,0x86,0xa1,0x02,0xa1,0x8a,0xef,0xb0,0xd1,0xb5,0x77,0x69,0xb9,0x6a,0xdc,0xdf,0x35,0x8a,0xda,0xa4,0x3e,0x83,0xfa,0x50,0xe6,0xca,0x0e,0x2b,0x99,0x0a,0x17,0x47,0x9d,
+ 0x04,0x20,0xa2,0x28,0x3c,0x5a,0x5b,0x82,0x32,0x66,0x11,0xe5,0x71,0xff,0x6b,0x25,0x92,0x75,0xdd,0x7a,0x4f,0x90,0x8b,0x1d,0x34,0xa4,0xf1,0x6e,0xb9,0xfb,0xb5,0x2e,0x7c,0x7f,0x47,0x9d,
+ 0x04,0x20,0xc8,0xb5,0x6a,0xba,0x02,0x26,0x45,0x12,0xfb,0x93,0x8a,0x51,0xe4,0xb0,0xf3,0x94,0xb7,0xc0,0x74,0x72,0xeb,0x67,0x91,0x9e,0x04,0x36,0x6a,0x4b,0xef,0x0d,0x88,0xfe,0x47,0x9d,
+ 0x04,0x20,0xc8,0xfa,0xcd,0x8c,0xc3,0x6f,0x3c,0xd0,0x27,0x7e,0x7d,0xeb,0x51,0x01,0x65,0xb6,0x9e,0x02,0x09,0x64,0xf4,0x87,0x78,0x7b,0x8f,0x9d,0xaf,0x3b,0xa5,0xcc,0x56,0x2c,0x47,0x9d,
};
#endif // BITCOIN_CHAINPARAMSSEEDS_H
diff --git a/src/coins.h b/src/coins.h
index 5a6f73652b..816b4864a3 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -20,8 +20,6 @@
#include <functional>
#include <unordered_map>
-class ChainstateManager;
-
/**
* A UTXO entry.
*
diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h
index 5f50cde3ff..7a254c3b67 100644
--- a/src/compat/assumptions.h
+++ b/src/compat/assumptions.h
@@ -36,11 +36,6 @@ static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 double assumed")
// Example(s): Everywhere :-)
static_assert(std::numeric_limits<unsigned char>::digits == 8, "8-bit byte assumed");
-// Assumption: We assume floating-point widths.
-// Example(s): Type punning in serialization code (ser_{float,double}_to_uint{32,64}).
-static_assert(sizeof(float) == 4, "32-bit float assumed");
-static_assert(sizeof(double) == 8, "64-bit double assumed");
-
// Assumption: We assume integer widths.
// Example(s): GetSizeOfCompactSize and WriteCompactSize in the serialization
// code.
diff --git a/src/compressor.cpp b/src/compressor.cpp
index a70306d320..a161c42866 100644
--- a/src/compressor.cpp
+++ b/src/compressor.cpp
@@ -52,7 +52,7 @@ static bool IsToPubKey(const CScript& script, CPubKey &pubkey)
return false;
}
-bool CompressScript(const CScript& script, std::vector<unsigned char> &out)
+bool CompressScript(const CScript& script, CompressedScript& out)
{
CKeyID keyID;
if (IsToKeyID(script, keyID)) {
@@ -92,7 +92,7 @@ unsigned int GetSpecialScriptSize(unsigned int nSize)
return 0;
}
-bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &in)
+bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in)
{
switch(nSize) {
case 0x00:
@@ -124,7 +124,7 @@ bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<uns
unsigned char vch[33] = {};
vch[0] = nSize - 2;
memcpy(&vch[1], in.data(), 32);
- CPubKey pubkey(&vch[0], &vch[33]);
+ CPubKey pubkey{vch};
if (!pubkey.Decompress())
return false;
assert(pubkey.size() == 65);
diff --git a/src/compressor.h b/src/compressor.h
index 478bfff0b6..40b2496f06 100644
--- a/src/compressor.h
+++ b/src/compressor.h
@@ -6,14 +6,26 @@
#ifndef BITCOIN_COMPRESSOR_H
#define BITCOIN_COMPRESSOR_H
+#include <prevector.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <serialize.h>
#include <span.h>
-bool CompressScript(const CScript& script, std::vector<unsigned char> &out);
+/**
+ * This saves us from making many heap allocations when serializing
+ * and deserializing compressed scripts.
+ *
+ * This prevector size is determined by the largest .resize() in the
+ * CompressScript function. The largest compressed script format is a
+ * compressed public key, which is 33 bytes.
+ */
+using CompressedScript = prevector<33, unsigned char>;
+
+
+bool CompressScript(const CScript& script, CompressedScript& out);
unsigned int GetSpecialScriptSize(unsigned int nSize);
-bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out);
+bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in);
/**
* Compress amount.
@@ -51,7 +63,7 @@ struct ScriptCompression
template<typename Stream>
void Ser(Stream &s, const CScript& script) {
- std::vector<unsigned char> compr;
+ CompressedScript compr;
if (CompressScript(script, compr)) {
s << MakeSpan(compr);
return;
@@ -66,7 +78,7 @@ struct ScriptCompression
unsigned int nSize = 0;
s >> VARINT(nSize);
if (nSize < nSpecialScripts) {
- std::vector<unsigned char> vch(GetSpecialScriptSize(nSize), 0x00);
+ CompressedScript vch(GetSpecialScriptSize(nSize), 0x00);
s >> MakeSpan(vch);
DecompressScript(script, nSize, vch);
return;
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 217cb019e1..28c95e0884 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -29,6 +29,11 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
+ /** If lock in occurs, delay activation until at least this block
+ * height. Note that activation will only occur on a retarget
+ * boundary.
+ */
+ int min_activation_height{0};
/** Constant for nTimeout very far in the future. */
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
@@ -38,6 +43,11 @@ struct BIP9Deployment {
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
* behaviour during activation cannot use this. */
static constexpr int64_t ALWAYS_ACTIVE = -1;
+
+ /** Special value for nStartTime indicating that the deployment is never active.
+ * This is useful for integrating the code changes for a new feature
+ * prior to deploying it on some or all networks. */
+ static constexpr int64_t NEVER_ACTIVE = -2;
};
/**
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index e78dc9f2a5..d5fd43e131 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -24,7 +24,7 @@ namespace Consensus {
* @param[out] txfee Set to the transaction fee if successful.
* Preconditions: tx.IsCoinBase() is false.
*/
-bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
+[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
} // namespace Consensus
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
index e5a0d4cb9c..a2b769cd56 100644
--- a/src/crypto/muhash.cpp
+++ b/src/crypto/muhash.cpp
@@ -341,6 +341,6 @@ MuHash3072& MuHash3072::Insert(Span<const unsigned char> in) noexcept {
}
MuHash3072& MuHash3072::Remove(Span<const unsigned char> in) noexcept {
- m_numerator.Divide(ToNum3072(in));
+ m_denominator.Multiply(ToNum3072(in));
return *this;
}
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index d7694108f5..3a1086bf4c 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -220,10 +220,9 @@ const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8;
*/
std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
{
- unsigned char buff[OBFUSCATE_KEY_NUM_BYTES];
- GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES);
- return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]);
-
+ std::vector<uint8_t> ret(OBFUSCATE_KEY_NUM_BYTES);
+ GetRandBytes(ret.data(), OBFUSCATE_KEY_NUM_BYTES);
+ return ret;
}
bool CDBWrapper::IsEmpty()
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index bb06c95e7d..95886d3138 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -50,6 +50,7 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-flushwallet",
"-privdb",
"-walletrejectlongchains",
+ "-unsafesqlitesync",
});
}
diff --git a/src/wallet/external_signer.cpp b/src/external_signer.cpp
index 3396111760..f16d21fa60 100644
--- a/src/wallet/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -7,45 +7,46 @@
#include <psbt.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <wallet/external_signer.h>
+#include <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) {}
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const 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)
+bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string chain)
{
// 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));
+ throw std::runtime_error(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 std::runtime_error(strprintf("'%s' error", command));
}
- throw ExternalSignerException(strprintf("'%s' error: %s", command, error.getValStr()));
+ throw std::runtime_error(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));
+ throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
}
- std::string fingerprintStr = fingerprint.get_str();
+ const std::string fingerprintStr = fingerprint.get_str();
// Skip duplicate signer
bool duplicate = false;
- for (ExternalSigner signer : signers) {
+ for (const ExternalSigner& signer : signers) {
if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
}
if (duplicate) break;
@@ -64,7 +65,7 @@ UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
}
-UniValue ExternalSigner::GetDescriptors(int account)
+UniValue ExternalSigner::GetDescriptors(const int account)
{
return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
}
@@ -79,7 +80,7 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
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) {
+ for (const auto& entry : input.hd_keypaths) {
if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true;
}
}
@@ -89,8 +90,8 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return false;
}
- std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
- std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";
+ const std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
+ const std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";
const UniValue signer_result = RunCommandParseJSON(command, stdinStr);
@@ -116,4 +117,4 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return true;
}
-#endif
+#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/wallet/external_signer.h b/src/external_signer.h
index 4b9711107b..b3b202091a 100644
--- a/src/wallet/external_signer.h
+++ b/src/external_signer.h
@@ -2,20 +2,18 @@
// 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
+#ifndef BITCOIN_EXTERNAL_SIGNER_H
+#define BITCOIN_EXTERNAL_SIGNER_H
-#include <stdexcept>
-#include <string>
#include <univalue.h>
#include <util/system.h>
-struct PartiallySignedTransaction;
+#include <string>
+#include <vector>
-class ExternalSignerException : public std::runtime_error {
-public:
- using std::runtime_error::runtime_error;
-};
+#ifdef ENABLE_EXTERNAL_SIGNER
+
+struct PartiallySignedTransaction;
//! Enables interaction with an external signing device or service, such as
//! a hardware wallet. See doc/external-signer.md
@@ -30,7 +28,7 @@ public:
//! @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);
+ ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name);
//! Master key fingerprint of the signer
std::string m_fingerprint;
@@ -43,13 +41,12 @@ public:
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);
+ //! @returns success
+ static bool Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string chain);
//! Display address on the device. Calls `<command> displayaddress --desc <descriptor>`.
//! @param[in] descriptor Descriptor specifying which address to display.
@@ -59,15 +56,15 @@ public:
//! 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);
+ //! @returns see doc/external-signer.md
+ UniValue GetDescriptors(const 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
+#endif // ENABLE_EXTERNAL_SIGNER
+
+#endif // BITCOIN_EXTERNAL_SIGNER_H
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 16ab38e0b2..e11e4acb5c 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -159,7 +159,8 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
return false;
}
- JSONRPCRequest jreq(context);
+ JSONRPCRequest jreq;
+ jreq.context = context;
jreq.peerAddr = req->GetPeer().ToString();
if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
@@ -294,7 +295,7 @@ bool StartHTTPRPC(const std::any& context)
if (!InitRPCAuthentication())
return false;
- auto handle_rpc = [&context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
+ auto handle_rpc = [context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
RegisterHTTPHandler("/", true, handle_rpc);
if (g_wallet_init_interface.HasWalletSupport()) {
RegisterHTTPHandler("/wallet/", false, handle_rpc);
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 12395f5b24..45c049c3be 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -63,12 +63,11 @@ template <typename WorkItem>
class WorkQueue
{
private:
- /** Mutex protects entire object */
Mutex cs;
- std::condition_variable cond;
- std::deque<std::unique_ptr<WorkItem>> queue;
- bool running;
- size_t maxDepth;
+ std::condition_variable cond GUARDED_BY(cs);
+ std::deque<std::unique_ptr<WorkItem>> queue GUARDED_BY(cs);
+ bool running GUARDED_BY(cs);
+ const size_t maxDepth;
public:
explicit WorkQueue(size_t _maxDepth) : running(true),
diff --git a/src/i2p.cpp b/src/i2p.cpp
index a44f09f043..2ae164633b 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -147,7 +147,9 @@ bool Session::Accept(Connection& conn)
try {
while (!*m_interrupt) {
Sock::Event occurred;
- conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred);
+ if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
+ throw std::runtime_error("wait on socket failed");
+ }
if ((occurred & Sock::RECV) == 0) {
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 25644c3b41..3a61af28b7 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -4,15 +4,16 @@
#include <chainparams.h>
#include <index/base.h>
+#include <node/blockstorage.h>
#include <node/ui_interface.h>
#include <shutdown.h>
#include <tinyformat.h>
-#include <util/system.h>
+#include <util/thread.h>
#include <util/translation.h>
-#include <validation.h>
+#include <validation.h> // For g_chainman
#include <warnings.h>
-constexpr char DB_BEST_BLOCK = 'B';
+constexpr uint8_t DB_BEST_BLOCK{'B'};
constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
@@ -59,33 +60,34 @@ bool BaseIndex::Init()
}
LOCK(cs_main);
+ CChain& active_chain = m_chainstate->m_chain;
if (locator.IsNull()) {
m_best_block_index = nullptr;
} else {
- m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
+ m_best_block_index = m_chainstate->m_blockman.FindForkInGlobalIndex(active_chain, locator);
}
- m_synced = m_best_block_index.load() == ::ChainActive().Tip();
+ m_synced = m_best_block_index.load() == active_chain.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();
+ const CBlockIndex* block = active_chain.Tip();
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
- prune_violation = block != ::ChainActive().Genesis();
+ prune_violation = block != active_chain.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 (!active_chain.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);
+ block_to_test = active_chain.FindFork(block_to_test);
}
- const CBlockIndex* block = ::ChainActive().Tip();
+ const CBlockIndex* block = active_chain.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)) {
@@ -97,28 +99,26 @@ bool BaseIndex::Init()
}
}
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 InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
}
}
return true;
}
-static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
if (!pindex_prev) {
- return ::ChainActive().Genesis();
+ return chain.Genesis();
}
- const CBlockIndex* pindex = ::ChainActive().Next(pindex_prev);
+ const CBlockIndex* pindex = chain.Next(pindex_prev);
if (pindex) {
return pindex;
}
- return ::ChainActive().Next(::ChainActive().FindFork(pindex_prev));
+ return chain.Next(chain.FindFork(pindex_prev));
}
void BaseIndex::ThreadSync()
@@ -141,7 +141,7 @@ void BaseIndex::ThreadSync()
{
LOCK(cs_main);
- const CBlockIndex* pindex_next = NextSyncBlock(pindex);
+ const CBlockIndex* pindex_next = NextSyncBlock(pindex, m_chainstate->m_chain);
if (!pindex_next) {
m_best_block_index = pindex;
m_synced = true;
@@ -204,7 +204,7 @@ bool BaseIndex::Commit()
bool BaseIndex::CommitInternal(CDBBatch& batch)
{
LOCK(cs_main);
- GetDB().WriteBestBlock(batch, ::ChainActive().GetLocator(m_best_block_index));
+ GetDB().WriteBestBlock(batch, m_chainstate->m_chain.GetLocator(m_best_block_index));
return true;
}
@@ -280,7 +280,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
const CBlockIndex* locator_tip_index;
{
LOCK(cs_main);
- locator_tip_index = g_chainman.m_blockman.LookupBlockIndex(locator_tip_hash);
+ locator_tip_index = m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
}
if (!locator_tip_index) {
@@ -321,7 +321,7 @@ bool BaseIndex::BlockUntilSyncedToCurrentChain() const
// Skip the queue-draining stuff if we know we're caught up with
// ::ChainActive().Tip().
LOCK(cs_main);
- const CBlockIndex* chain_tip = ::ChainActive().Tip();
+ const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();
const CBlockIndex* best_block_index = m_best_block_index.load();
if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
return true;
@@ -338,18 +338,19 @@ void BaseIndex::Interrupt()
m_interrupt();
}
-void BaseIndex::Start()
+bool BaseIndex::Start(CChainState& active_chainstate)
{
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ m_chainstate = &active_chainstate;
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
RegisterValidationInterface(this);
if (!Init()) {
- FatalError("%s: %s failed to initialize", __func__, GetName());
- return;
+ return false;
}
- m_thread_sync = std::thread(&TraceThread<std::function<void()>>, GetName(),
- std::bind(&BaseIndex::ThreadSync, this));
+ m_thread_sync = std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
+ return true;
}
void BaseIndex::Stop()
diff --git a/src/index/base.h b/src/index/base.h
index 8559e3cb64..df4bdff1ea 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -12,6 +12,7 @@
#include <validationinterface.h>
class CBlockIndex;
+class CChainState;
struct IndexSummary {
std::string name;
@@ -75,14 +76,17 @@ private:
/// to a chain reorganization), the index must halt until Commit succeeds or else it could end up
/// getting corrupted.
bool Commit();
-
protected:
+ CChainState* m_chainstate{nullptr};
+
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override;
void ChainStateFlushed(const CBlockLocator& locator) override;
+ const CBlockIndex* CurrentIndex() { return m_best_block_index.load(); };
+
/// Initialize internal state from the database and block index.
- virtual bool Init();
+ [[nodiscard]] virtual bool Init();
/// Write update index entries for a newly connected block.
virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { return true; }
@@ -109,13 +113,13 @@ public:
/// sync once and only needs to process blocks in the ValidationInterface
/// queue. If the index is catching up from far behind, this method does
/// not block and immediately returns false.
- bool BlockUntilSyncedToCurrentChain() const;
+ bool BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main);
void Interrupt();
/// Start initializes the sync state and registers the instance as a
/// ValidationInterface so that it stays in sync with blockchain updates.
- void Start();
+ [[nodiscard]] bool Start(CChainState& active_chainstate);
/// Stops the instance from staying in sync with blockchain updates.
void Stop();
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
index 32271fb7ab..b485776732 100644
--- a/src/index/blockfilterindex.cpp
+++ b/src/index/blockfilterindex.cpp
@@ -6,8 +6,8 @@
#include <dbwrapper.h>
#include <index/blockfilterindex.h>
+#include <node/blockstorage.h>
#include <util/system.h>
-#include <validation.h>
/* The index database stores three items for each block: the disk location of the encoded filter,
* its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
@@ -24,9 +24,9 @@
* as big-endian so that sequential reads of filters by height are fast.
* Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
*/
-constexpr char DB_BLOCK_HASH = 's';
-constexpr char DB_BLOCK_HEIGHT = 't';
-constexpr char DB_FILTER_POS = 'P';
+constexpr uint8_t DB_BLOCK_HASH{'s'};
+constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
+constexpr uint8_t DB_FILTER_POS{'P'};
constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for fltr?????.dat files */
@@ -63,7 +63,7 @@ struct DBHeightKey {
template<typename Stream>
void Unserialize(Stream& s)
{
- char prefix = ser_readdata8(s);
+ const uint8_t prefix{ser_readdata8(s)};
if (prefix != DB_BLOCK_HEIGHT) {
throw std::ios_base::failure("Invalid format for block filter index DB height key");
}
@@ -77,7 +77,7 @@ struct DBHashKey {
explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
SERIALIZE_METHODS(DBHashKey, obj) {
- char prefix = DB_BLOCK_HASH;
+ uint8_t prefix{DB_BLOCK_HASH};
READWRITE(prefix);
if (prefix != DB_BLOCK_HASH) {
throw std::ios_base::failure("Invalid format for block filter index DB hash key");
@@ -98,7 +98,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
const std::string& filter_name = BlockFilterTypeName(filter_type);
if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
- fs::path path = GetDataDir() / "indexes" / "blockfilter" / filter_name;
+ fs::path path = gArgs.GetDataDirNet() / "indexes" / "blockfilter" / filter_name;
fs::create_directories(path);
m_name = filter_name + " block filter index";
@@ -149,7 +149,7 @@ bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& f
}
uint256 block_hash;
- std::vector<unsigned char> encoded_filter;
+ std::vector<uint8_t> encoded_filter;
try {
filein >> block_hash >> encoded_filter;
filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter));
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
new file mode 100644
index 0000000000..8c6046489b
--- /dev/null
+++ b/src/index/coinstatsindex.cpp
@@ -0,0 +1,472 @@
+// 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 <chainparams.h>
+#include <coins.h>
+#include <crypto/muhash.h>
+#include <index/coinstatsindex.h>
+#include <node/blockstorage.h>
+#include <serialize.h>
+#include <txdb.h>
+#include <undo.h>
+#include <validation.h>
+
+static constexpr uint8_t DB_BLOCK_HASH{'s'};
+static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
+static constexpr uint8_t DB_MUHASH{'M'};
+
+namespace {
+
+struct DBVal {
+ uint256 muhash;
+ uint64_t transaction_output_count;
+ uint64_t bogo_size;
+ CAmount total_amount;
+ CAmount total_subsidy;
+ CAmount block_unspendable_amount;
+ CAmount block_prevout_spent_amount;
+ CAmount block_new_outputs_ex_coinbase_amount;
+ CAmount block_coinbase_amount;
+ CAmount unspendables_genesis_block;
+ CAmount unspendables_bip30;
+ CAmount unspendables_scripts;
+ CAmount unspendables_unclaimed_rewards;
+
+ SERIALIZE_METHODS(DBVal, obj)
+ {
+ READWRITE(obj.muhash);
+ READWRITE(obj.transaction_output_count);
+ READWRITE(obj.bogo_size);
+ READWRITE(obj.total_amount);
+ READWRITE(obj.total_subsidy);
+ READWRITE(obj.block_unspendable_amount);
+ READWRITE(obj.block_prevout_spent_amount);
+ READWRITE(obj.block_new_outputs_ex_coinbase_amount);
+ READWRITE(obj.block_coinbase_amount);
+ READWRITE(obj.unspendables_genesis_block);
+ READWRITE(obj.unspendables_bip30);
+ READWRITE(obj.unspendables_scripts);
+ READWRITE(obj.unspendables_unclaimed_rewards);
+ }
+};
+
+struct DBHeightKey {
+ int height;
+
+ explicit DBHeightKey(int height_in) : height(height_in) {}
+
+ template <typename Stream>
+ void Serialize(Stream& s) const
+ {
+ ser_writedata8(s, DB_BLOCK_HEIGHT);
+ ser_writedata32be(s, height);
+ }
+
+ template <typename Stream>
+ void Unserialize(Stream& s)
+ {
+ const uint8_t prefix{ser_readdata8(s)};
+ if (prefix != DB_BLOCK_HEIGHT) {
+ throw std::ios_base::failure("Invalid format for coinstatsindex DB height key");
+ }
+ height = ser_readdata32be(s);
+ }
+};
+
+struct DBHashKey {
+ uint256 block_hash;
+
+ explicit DBHashKey(const uint256& hash_in) : block_hash(hash_in) {}
+
+ SERIALIZE_METHODS(DBHashKey, obj)
+ {
+ uint8_t prefix{DB_BLOCK_HASH};
+ READWRITE(prefix);
+ if (prefix != DB_BLOCK_HASH) {
+ throw std::ios_base::failure("Invalid format for coinstatsindex DB hash key");
+ }
+
+ READWRITE(obj.block_hash);
+ }
+};
+
+}; // namespace
+
+std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
+
+CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
+{
+ fs::path path{gArgs.GetDataDirNet() / "indexes" / "coinstats"};
+ fs::create_directories(path);
+
+ m_db = std::make_unique<CoinStatsIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
+}
+
+bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
+{
+ CBlockUndo block_undo;
+ const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
+ m_total_subsidy += block_subsidy;
+
+ // Ignore genesis block
+ if (pindex->nHeight > 0) {
+ if (!UndoReadFromDisk(block_undo, pindex)) {
+ return false;
+ }
+
+ std::pair<uint256, DBVal> read_out;
+ if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
+ return false;
+ }
+
+ uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
+ if (read_out.first != expected_block_hash) {
+ if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
+ return error("%s: previous block header belongs to unexpected block %s; expected %s",
+ __func__, read_out.first.ToString(), expected_block_hash.ToString());
+ }
+ }
+
+ // TODO: Deduplicate BIP30 related code
+ bool is_bip30_block{(pindex->nHeight == 91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
+ (pindex->nHeight == 91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f"))};
+
+ // Add the new utxos created from the block
+ for (size_t i = 0; i < block.vtx.size(); ++i) {
+ const auto& tx{block.vtx.at(i)};
+
+ // Skip duplicate txid coinbase transactions (BIP30).
+ if (is_bip30_block && tx->IsCoinBase()) {
+ m_block_unspendable_amount += block_subsidy;
+ m_unspendables_bip30 += block_subsidy;
+ continue;
+ }
+
+ for (size_t j = 0; j < tx->vout.size(); ++j) {
+ const CTxOut& out{tx->vout[j]};
+ Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
+ COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
+
+ // Skip unspendable coins
+ if (coin.out.scriptPubKey.IsUnspendable()) {
+ m_block_unspendable_amount += coin.out.nValue;
+ m_unspendables_scripts += coin.out.nValue;
+ continue;
+ }
+
+ m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+
+ if (tx->IsCoinBase()) {
+ m_block_coinbase_amount += coin.out.nValue;
+ } else {
+ m_block_new_outputs_ex_coinbase_amount += coin.out.nValue;
+ }
+
+ ++m_transaction_output_count;
+ m_total_amount += coin.out.nValue;
+ m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
+ }
+
+ // The coinbase tx has no undo data since no former output is spent
+ if (!tx->IsCoinBase()) {
+ const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
+
+ for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
+ Coin coin{tx_undo.vprevout[j]};
+ COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
+
+ m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
+
+ m_block_prevout_spent_amount += coin.out.nValue;
+
+ --m_transaction_output_count;
+ m_total_amount -= coin.out.nValue;
+ m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
+ }
+ }
+ }
+ } else {
+ // genesis block
+ m_block_unspendable_amount += block_subsidy;
+ m_unspendables_genesis_block += block_subsidy;
+ }
+
+ // If spent prevouts + block subsidy are still a higher amount than
+ // new outputs + coinbase + current unspendable amount this means
+ // the miner did not claim the full block reward. Unclaimed block
+ // rewards are also unspendable.
+ const CAmount unclaimed_rewards{(m_block_prevout_spent_amount + m_total_subsidy) - (m_block_new_outputs_ex_coinbase_amount + m_block_coinbase_amount + m_block_unspendable_amount)};
+ m_block_unspendable_amount += unclaimed_rewards;
+ m_unspendables_unclaimed_rewards += unclaimed_rewards;
+
+ std::pair<uint256, DBVal> value;
+ value.first = pindex->GetBlockHash();
+ value.second.transaction_output_count = m_transaction_output_count;
+ value.second.bogo_size = m_bogo_size;
+ value.second.total_amount = m_total_amount;
+ value.second.total_subsidy = m_total_subsidy;
+ value.second.block_unspendable_amount = m_block_unspendable_amount;
+ value.second.block_prevout_spent_amount = m_block_prevout_spent_amount;
+ value.second.block_new_outputs_ex_coinbase_amount = m_block_new_outputs_ex_coinbase_amount;
+ value.second.block_coinbase_amount = m_block_coinbase_amount;
+ value.second.unspendables_genesis_block = m_unspendables_genesis_block;
+ value.second.unspendables_bip30 = m_unspendables_bip30;
+ value.second.unspendables_scripts = m_unspendables_scripts;
+ value.second.unspendables_unclaimed_rewards = m_unspendables_unclaimed_rewards;
+
+ uint256 out;
+ m_muhash.Finalize(out);
+ value.second.muhash = out;
+
+ return m_db->Write(DBHeightKey(pindex->nHeight), value) && m_db->Write(DB_MUHASH, m_muhash);
+}
+
+static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
+ const std::string& index_name,
+ int start_height, int stop_height)
+{
+ DBHeightKey key{start_height};
+ db_it.Seek(key);
+
+ for (int height = start_height; height <= stop_height; ++height) {
+ if (!db_it.GetKey(key) || key.height != height) {
+ return error("%s: unexpected key in %s: expected (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ std::pair<uint256, DBVal> value;
+ if (!db_it.GetValue(value)) {
+ return error("%s: unable to read value in %s at key (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ batch.Write(DBHashKey(value.first), std::move(value.second));
+
+ db_it.Next();
+ }
+ return true;
+}
+
+bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
+{
+ assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
+
+ CDBBatch batch(*m_db);
+ std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
+
+ // During a reorg, we need to copy all hash digests for blocks that are
+ // getting disconnected from the height index to the hash index so we can
+ // still find them when the height index entries are overwritten.
+ if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) {
+ return false;
+ }
+
+ if (!m_db->WriteBatch(batch)) return false;
+
+ {
+ LOCK(cs_main);
+ CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
+ const auto& consensus_params{Params().GetConsensus()};
+
+ do {
+ CBlock block;
+
+ if (!ReadBlockFromDisk(block, iter_tip, consensus_params)) {
+ return error("%s: Failed to read block %s from disk",
+ __func__, iter_tip->GetBlockHash().ToString());
+ }
+
+ ReverseBlock(block, iter_tip);
+
+ iter_tip = iter_tip->GetAncestor(iter_tip->nHeight - 1);
+ } while (new_tip != iter_tip);
+ }
+
+ return BaseIndex::Rewind(current_tip, new_tip);
+}
+
+static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result)
+{
+ // First check if the result is stored under the height index and the value
+ // there matches the block hash. This should be the case if the block is on
+ // the active chain.
+ std::pair<uint256, DBVal> read_out;
+ if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
+ return false;
+ }
+ if (read_out.first == block_index->GetBlockHash()) {
+ result = std::move(read_out.second);
+ return true;
+ }
+
+ // If value at the height index corresponds to an different block, the
+ // result will be stored in the hash index.
+ return db.Read(DBHashKey(block_index->GetBlockHash()), result);
+}
+
+bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const
+{
+ DBVal entry;
+ if (!LookUpOne(*m_db, block_index, entry)) {
+ return false;
+ }
+
+ coins_stats.hashSerialized = entry.muhash;
+ coins_stats.nTransactionOutputs = entry.transaction_output_count;
+ coins_stats.nBogoSize = entry.bogo_size;
+ coins_stats.nTotalAmount = entry.total_amount;
+ coins_stats.total_subsidy = entry.total_subsidy;
+ coins_stats.block_unspendable_amount = entry.block_unspendable_amount;
+ coins_stats.block_prevout_spent_amount = entry.block_prevout_spent_amount;
+ coins_stats.block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
+ coins_stats.block_coinbase_amount = entry.block_coinbase_amount;
+ coins_stats.unspendables_genesis_block = entry.unspendables_genesis_block;
+ coins_stats.unspendables_bip30 = entry.unspendables_bip30;
+ coins_stats.unspendables_scripts = entry.unspendables_scripts;
+ coins_stats.unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards;
+
+ return true;
+}
+
+bool CoinStatsIndex::Init()
+{
+ if (!m_db->Read(DB_MUHASH, m_muhash)) {
+ // Check that the cause of the read failure is that the key does not
+ // exist. Any other errors indicate database corruption or a disk
+ // failure, and starting the index would cause further corruption.
+ if (m_db->Exists(DB_MUHASH)) {
+ return error("%s: Cannot read current %s state; index may be corrupted",
+ __func__, GetName());
+ }
+ }
+
+ if (BaseIndex::Init()) {
+ const CBlockIndex* pindex{CurrentIndex()};
+
+ if (pindex) {
+ DBVal entry;
+ if (!LookUpOne(*m_db, pindex, entry)) {
+ return false;
+ }
+
+ m_transaction_output_count = entry.transaction_output_count;
+ m_bogo_size = entry.bogo_size;
+ m_total_amount = entry.total_amount;
+ m_total_subsidy = entry.total_subsidy;
+ m_block_unspendable_amount = entry.block_unspendable_amount;
+ m_block_prevout_spent_amount = entry.block_prevout_spent_amount;
+ m_block_new_outputs_ex_coinbase_amount = entry.block_new_outputs_ex_coinbase_amount;
+ m_block_coinbase_amount = entry.block_coinbase_amount;
+ m_unspendables_genesis_block = entry.unspendables_genesis_block;
+ m_unspendables_bip30 = entry.unspendables_bip30;
+ m_unspendables_scripts = entry.unspendables_scripts;
+ m_unspendables_unclaimed_rewards = entry.unspendables_unclaimed_rewards;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// Reverse a single block as part of a reorg
+bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex)
+{
+ CBlockUndo block_undo;
+ std::pair<uint256, DBVal> read_out;
+
+ const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
+ m_total_subsidy -= block_subsidy;
+
+ // Ignore genesis block
+ if (pindex->nHeight > 0) {
+ if (!UndoReadFromDisk(block_undo, pindex)) {
+ return false;
+ }
+
+ if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
+ return false;
+ }
+
+ uint256 expected_block_hash{pindex->pprev->GetBlockHash()};
+ if (read_out.first != expected_block_hash) {
+ if (!m_db->Read(DBHashKey(expected_block_hash), read_out)) {
+ return error("%s: previous block header belongs to unexpected block %s; expected %s",
+ __func__, read_out.first.ToString(), expected_block_hash.ToString());
+ }
+ }
+ }
+
+ // Remove the new UTXOs that were created from the block
+ for (size_t i = 0; i < block.vtx.size(); ++i) {
+ const auto& tx{block.vtx.at(i)};
+
+ for (size_t j = 0; j < tx->vout.size(); ++j) {
+ const CTxOut& out{tx->vout[j]};
+ COutPoint outpoint{tx->GetHash(), static_cast<uint32_t>(j)};
+ Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
+
+ // Skip unspendable coins
+ if (coin.out.scriptPubKey.IsUnspendable()) {
+ m_block_unspendable_amount -= coin.out.nValue;
+ m_unspendables_scripts -= coin.out.nValue;
+ continue;
+ }
+
+ m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
+
+ if (tx->IsCoinBase()) {
+ m_block_coinbase_amount -= coin.out.nValue;
+ } else {
+ m_block_new_outputs_ex_coinbase_amount -= coin.out.nValue;
+ }
+
+ --m_transaction_output_count;
+ m_total_amount -= coin.out.nValue;
+ m_bogo_size -= GetBogoSize(coin.out.scriptPubKey);
+ }
+
+ // The coinbase tx has no undo data since no former output is spent
+ if (!tx->IsCoinBase()) {
+ const auto& tx_undo{block_undo.vtxundo.at(i - 1)};
+
+ for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
+ Coin coin{tx_undo.vprevout[j]};
+ COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
+
+ m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+
+ m_block_prevout_spent_amount -= coin.out.nValue;
+
+ m_transaction_output_count++;
+ m_total_amount += coin.out.nValue;
+ m_bogo_size += GetBogoSize(coin.out.scriptPubKey);
+ }
+ }
+ }
+
+ const CAmount unclaimed_rewards{(m_block_new_outputs_ex_coinbase_amount + m_block_coinbase_amount + m_block_unspendable_amount) - (m_block_prevout_spent_amount + m_total_subsidy)};
+ m_block_unspendable_amount -= unclaimed_rewards;
+ m_unspendables_unclaimed_rewards -= unclaimed_rewards;
+
+ // Check that the rolled back internal values are consistent with the DB read out
+ uint256 out;
+ m_muhash.Finalize(out);
+ Assert(read_out.second.muhash == out);
+
+ Assert(m_transaction_output_count == read_out.second.transaction_output_count);
+ Assert(m_total_amount == read_out.second.total_amount);
+ Assert(m_bogo_size == read_out.second.bogo_size);
+ Assert(m_total_subsidy == read_out.second.total_subsidy);
+ Assert(m_block_unspendable_amount == read_out.second.block_unspendable_amount);
+ Assert(m_block_prevout_spent_amount == read_out.second.block_prevout_spent_amount);
+ Assert(m_block_new_outputs_ex_coinbase_amount == read_out.second.block_new_outputs_ex_coinbase_amount);
+ Assert(m_block_coinbase_amount == read_out.second.block_coinbase_amount);
+ Assert(m_unspendables_genesis_block == read_out.second.unspendables_genesis_block);
+ Assert(m_unspendables_bip30 == read_out.second.unspendables_bip30);
+ Assert(m_unspendables_scripts == read_out.second.unspendables_scripts);
+ Assert(m_unspendables_unclaimed_rewards == read_out.second.unspendables_unclaimed_rewards);
+
+ return m_db->Write(DB_MUHASH, m_muhash);
+}
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
new file mode 100644
index 0000000000..6149f9b4b3
--- /dev/null
+++ b/src/index/coinstatsindex.h
@@ -0,0 +1,61 @@
+// 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_INDEX_COINSTATSINDEX_H
+#define BITCOIN_INDEX_COINSTATSINDEX_H
+
+#include <chain.h>
+#include <crypto/muhash.h>
+#include <flatfile.h>
+#include <index/base.h>
+#include <node/coinstats.h>
+
+/**
+ * CoinStatsIndex maintains statistics on the UTXO set.
+ */
+class CoinStatsIndex final : public BaseIndex
+{
+private:
+ std::string m_name;
+ std::unique_ptr<BaseIndex::DB> m_db;
+
+ MuHash3072 m_muhash;
+ uint64_t m_transaction_output_count{0};
+ uint64_t m_bogo_size{0};
+ CAmount m_total_amount{0};
+ CAmount m_total_subsidy{0};
+ CAmount m_block_unspendable_amount{0};
+ CAmount m_block_prevout_spent_amount{0};
+ CAmount m_block_new_outputs_ex_coinbase_amount{0};
+ CAmount m_block_coinbase_amount{0};
+ CAmount m_unspendables_genesis_block{0};
+ CAmount m_unspendables_bip30{0};
+ CAmount m_unspendables_scripts{0};
+ CAmount m_unspendables_unclaimed_rewards{0};
+
+ bool ReverseBlock(const CBlock& block, const CBlockIndex* pindex);
+
+protected:
+ bool Init() override;
+
+ bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
+
+ bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override;
+
+ BaseIndex::DB& GetDB() const override { return *m_db; }
+
+ const char* GetName() const override { return "coinstatsindex"; }
+
+public:
+ // Constructs the index, which becomes available to be queried.
+ explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+ // Look up stats for a specific block using CBlockIndex
+ bool LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const;
+};
+
+/// The global UTXO set hash object.
+extern std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
+
+#endif // BITCOIN_INDEX_COINSTATSINDEX_H
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index f41985c344..782e557478 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -4,20 +4,20 @@
#include <index/disktxpos.h>
#include <index/txindex.h>
+#include <node/blockstorage.h>
#include <node/ui_interface.h>
#include <shutdown.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
-constexpr char DB_BEST_BLOCK = 'B';
-constexpr char DB_TXINDEX = 't';
-constexpr char DB_TXINDEX_BLOCK = 'T';
+constexpr uint8_t DB_BEST_BLOCK{'B'};
+constexpr uint8_t DB_TXINDEX{'t'};
+constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
std::unique_ptr<TxIndex> g_txindex;
-
/** Access to the txindex database (indexes/txindex/) */
class TxIndex::DB : public BaseIndex::DB
{
@@ -37,7 +37,7 @@ public:
};
TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
- BaseIndex::DB(GetDataDir() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe)
+ BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe)
{}
bool TxIndex::DB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const
@@ -60,8 +60,8 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
*/
static void WriteTxIndexMigrationBatches(CDBWrapper& newdb, CDBWrapper& olddb,
CDBBatch& batch_newdb, CDBBatch& batch_olddb,
- const std::pair<unsigned char, uint256>& begin_key,
- const std::pair<unsigned char, uint256>& end_key)
+ const std::pair<uint8_t, uint256>& begin_key,
+ const std::pair<uint8_t, uint256>& end_key)
{
// Sync new DB changes to disk before deleting from old DB.
newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
@@ -113,9 +113,9 @@ bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator&
CDBBatch batch_newdb(*this);
CDBBatch batch_olddb(block_tree_db);
- std::pair<unsigned char, uint256> key;
- std::pair<unsigned char, uint256> begin_key{DB_TXINDEX, uint256()};
- std::pair<unsigned char, uint256> prev_key = begin_key;
+ std::pair<uint8_t, uint256> key;
+ std::pair<uint8_t, uint256> begin_key{DB_TXINDEX, uint256()};
+ std::pair<uint8_t, uint256> prev_key = begin_key;
bool interrupted = false;
std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
@@ -204,7 +204,7 @@ bool TxIndex::Init()
// Attempt to migrate txindex from the old database to the new one. Even if
// chain_tip is null, the node could be reindexing and we still want to
// delete txindex records in the old database.
- if (!m_db->MigrateData(*pblocktree, ::ChainActive().GetLocator())) {
+ if (!m_db->MigrateData(*pblocktree, m_chainstate->m_chain.GetLocator())) {
return false;
}
diff --git a/src/init.cpp b/src/init.cpp
index 17b216573f..7f64b1acfa 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -16,22 +16,23 @@
#include <chain.h>
#include <chainparams.h>
#include <compat/sanity.h>
-#include <consensus/validation.h>
#include <fs.h>
#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
+#include <index/coinstatsindex.h>
#include <index/txindex.h>
+#include <init/common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
-#include <key.h>
#include <mapport.h>
#include <miner.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
+#include <node/blockstorage.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <policy/feerate.h>
@@ -58,10 +59,10 @@
#include <util/moneystr.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/thread.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
-
#include <validationinterface.h>
#include <walletinitinterface.h>
@@ -90,7 +91,6 @@
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
-static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
@@ -153,10 +153,6 @@ static fs::path GetPidFile(const ArgsManager& args)
// shutdown thing.
//
-static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
-
-static std::thread g_load_block;
-
void Interrupt(NodeContext& node)
{
InterruptHTTPServer();
@@ -171,6 +167,9 @@ void Interrupt(NodeContext& node)
g_txindex->Interrupt();
}
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
+ if (g_coin_stats_index) {
+ g_coin_stats_index->Interrupt();
+ }
}
void Shutdown(NodeContext& node)
@@ -200,27 +199,14 @@ void Shutdown(NodeContext& node)
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peerman) UnregisterValidationInterface(node.peerman.get());
- // Follow the lock order requirements:
- // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraFullOutboundCount
- // which locks cs_vNodes.
- // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which
- // locks cs_vNodes.
- // * CConnman::Stop calls DeleteNode, which calls FinalizeNode, which locks cs_main and calls
- // EraseOrphansFor, which locks g_cs_orphans.
- //
- // Thus the implicit locking order requirement is: (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
- if (node.connman) {
- node.connman->StopThreads();
- LOCK2(::cs_main, ::g_cs_orphans);
- node.connman->StopNodes();
- }
+ if (node.connman) node.connman->Stop();
StopTorControl();
// After everything has been shut down, but before things get flushed, stop the
// CScheduler/checkqueue, scheduler and load block thread.
if (node.scheduler) node.scheduler->stop();
- if (g_load_block.joinable()) g_load_block.join();
+ if (node.chainman && node.chainman->m_load_block.joinable()) node.chainman->m_load_block.join();
StopScriptCheckWorkerThreads();
// After the threads that potentially access these pointers have been stopped,
@@ -256,6 +242,10 @@ void Shutdown(NodeContext& node)
g_txindex->Stop();
g_txindex.reset();
}
+ if (g_coin_stats_index) {
+ g_coin_stats_index->Stop();
+ g_coin_stats_index.reset();
+ }
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Stop(); });
DestroyAllBlockFilterIndexes();
@@ -290,8 +280,7 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- globalVerifyHandle.reset();
- ECC_Stop();
+ init::UnsetGlobals();
node.mempool.reset();
node.fee_estimator.reset();
node.chainman = nullptr;
@@ -367,6 +356,8 @@ void SetupServerArgs(NodeContext& node)
SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
+ init::AddLoggingArgs(argsman);
+
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET);
@@ -394,12 +385,11 @@ void SetupServerArgs(NodeContext& node)
#endif
argsman.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless the peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-loadblock=<file>", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -410,7 +400,7 @@ void SetupServerArgs(NodeContext& node)
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
+ argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -coinstatsindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -430,7 +420,7 @@ void SetupServerArgs(NodeContext& node)
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-addnode=<ip>", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bind=<addr>[:<port>][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
@@ -443,7 +433,7 @@ void SetupServerArgs(NodeContext& node)
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);
- argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u)", DEFAULT_MAX_PEER_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
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);
@@ -526,43 +516,28 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
- "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.",
- ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except the specified category. This option can be specified multiple times to exclude multiple categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
-#ifdef HAVE_THREAD_LOCAL
- argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
-#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);
argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-printpriority", strprintf("Log transaction fee rate in " + CURRENCY_UNIT + "/kvB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
- argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
- argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
+ argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
- argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
+ argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
- argsman.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
+ argsman.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kvB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
@@ -627,61 +602,6 @@ static void BlockNotifyGenesisWait(const CBlockIndex* pBlockIndex)
}
}
-struct CImportingNow
-{
- CImportingNow() {
- assert(fImporting == false);
- fImporting = true;
- }
-
- ~CImportingNow() {
- assert(fImporting == true);
- fImporting = false;
- }
-};
-
-
-// If we're using -prune with -reindex, then delete block files that will be ignored by the
-// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
-// is missing, do the same here to delete any later block files after a gap. Also delete all
-// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile
-// is in sync with what's actually on disk by the time we start downloading, so that pruning
-// works correctly.
-static void CleanupBlockRevFiles()
-{
- std::map<std::string, fs::path> mapBlockFiles;
-
- // Glob all blk?????.dat and rev?????.dat files from the blocks directory.
- // Remove the rev files immediately and insert the blk file paths into an
- // ordered map keyed by block file index.
- LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
- fs::path blocksdir = GetBlocksDir();
- for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
- if (fs::is_regular_file(*it) &&
- it->path().filename().string().length() == 12 &&
- it->path().filename().string().substr(8,4) == ".dat")
- {
- if (it->path().filename().string().substr(0,3) == "blk")
- mapBlockFiles[it->path().filename().string().substr(3,5)] = it->path();
- else if (it->path().filename().string().substr(0,3) == "rev")
- remove(it->path());
- }
- }
-
- // Remove all block files that aren't part of a contiguous set starting at
- // zero by walking the ordered map (keys are block file indices) by
- // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
- // start removing block files.
- int nContigCounter = 0;
- for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
- if (atoi(item.first) == nContigCounter) {
- nContigCounter++;
- continue;
- }
- remove(item.second);
- }
-}
-
#if HAVE_SYSTEM
static void StartupNotify(const ArgsManager& args)
{
@@ -693,102 +613,7 @@ static void StartupNotify(const ArgsManager& args)
}
#endif
-static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
-{
- const CChainParams& chainparams = Params();
- ScheduleBatchPriority();
-
- {
- CImportingNow imp;
-
- // -reindex
- if (fReindex) {
- int nFile = 0;
- while (true) {
- FlatFilePos pos(nFile, 0);
- if (!fs::exists(GetBlockPosFilename(pos)))
- break; // No block files left to reindex
- FILE *file = OpenBlockFile(pos, true);
- if (!file)
- break; // This error is logged in OpenBlockFile
- LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
- ::ChainstateActive().LoadExternalBlockFile(chainparams, file, &pos);
- if (ShutdownRequested()) {
- LogPrintf("Shutdown requested. Exit %s\n", __func__);
- return;
- }
- nFile++;
- }
- pblocktree->WriteReindexing(false);
- fReindex = false;
- LogPrintf("Reindexing finished\n");
- // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
- ::ChainstateActive().LoadGenesisBlock(chainparams);
- }
-
- // -loadblock=
- for (const fs::path& path : vImportFiles) {
- FILE *file = fsbridge::fopen(path, "rb");
- if (file) {
- LogPrintf("Importing blocks file %s...\n", path.string());
- ::ChainstateActive().LoadExternalBlockFile(chainparams, file);
- if (ShutdownRequested()) {
- LogPrintf("Shutdown requested. Exit %s\n", __func__);
- return;
- }
- } else {
- LogPrintf("Warning: Could not open blocks file %s\n", path.string());
- }
- }
-
- // scan for better chains in the block chain database, that are not yet connected in the active best chain
-
- // We can't hold cs_main during ActivateBestChain even though we're accessing
- // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
- // the relevant pointers before the ABC call.
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
- BlockValidationState state;
- if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
- LogPrintf("Failed to connect best block (%s)\n", state.ToString());
- StartShutdown();
- return;
- }
- }
-
- if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
- LogPrintf("Stopping after block import\n");
- StartShutdown();
- return;
- }
- } // End scope of CImportingNow
- chainman.ActiveChainstate().LoadMempool(args);
-}
-
-/** Sanity checks
- * Ensure that Bitcoin is running in a usable environment with all
- * necessary library support.
- */
-static bool InitSanityCheck()
-{
- if (!ECC_InitSanityCheck()) {
- return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
- }
-
- 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;
-}
-
-static bool AppInitServers(const std::any& context, NodeContext& node)
+static bool AppInitServers(NodeContext& node)
{
const ArgsManager& args = *Assert(node.args);
RPCServer::OnStarted(&OnRPCStarted);
@@ -797,9 +622,9 @@ static bool AppInitServers(const std::any& context, NodeContext& node)
return false;
StartRPC();
node.rpc_interruption_point = RpcInterruptionPoint;
- if (!StartHTTPRPC(context))
+ if (!StartHTTPRPC(&node))
return false;
- if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
+ if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(&node);
StartHTTPServer();
return true;
}
@@ -885,25 +710,8 @@ void InitParameterInteraction(ArgsManager& args)
*/
void InitLogging(const ArgsManager& args)
{
- LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
- LogInstance().m_file_path = AbsPathForConfigVal(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
- LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
- LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
- LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
-#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);
-
- std::string version_string = FormatFullVersion();
-#ifdef DEBUG
- version_string += " (debug build)";
-#else
- version_string += " (release build)";
-#endif
- LogPrintf(PACKAGE_NAME " version %s\n", version_string);
+ init::SetLoggingOptions(args);
+ init::LogPackageVersion();
}
namespace { // Variables internal to initialization process only
@@ -1008,7 +816,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
InitWarning(warnings);
}
- if (!fs::is_directory(GetBlocksDir())) {
+ if (!fs::is_directory(gArgs.GetBlocksDirPath())) {
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", "")));
}
@@ -1036,10 +844,12 @@ bool AppInitParameterInteraction(const ArgsManager& args)
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
- // if using block pruning, then disallow txindex
+ // if using block pruning, then disallow txindex and coinstatsindex
if (args.GetArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
+ if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX))
+ return InitError(_("Prune mode is incompatible with -coinstatsindex."));
}
// -bind and -whitebind can't be set when not listening
@@ -1071,26 +881,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
- if (args.IsArgSet("-debug")) {
- // Special-case: if -debug=0/-nodebug is set, turn off debugging messages
- const std::vector<std::string> categories = args.GetArgs("-debug");
-
- if (std::none_of(categories.begin(), categories.end(),
- [](std::string cat){return cat == "0" || cat == "none";})) {
- for (const auto& cat : categories) {
- if (!LogInstance().EnableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
- }
- }
- }
- }
-
- // Now remove the logging categories which were explicitly excluded
- for (const std::string& cat : args.GetArgs("-debugexclude")) {
- if (!LogInstance().DisableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
- }
- }
+ init::SetLoggingCategories(args);
fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
@@ -1223,7 +1014,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
static bool LockDataDirectory(bool probeOnly)
{
// Make sure only a single Bitcoin process is using the data directory.
- fs::path datadir = GetDataDir();
+ fs::path datadir = gArgs.GetDataDirNet();
if (!DirIsWritable(datadir)) {
return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string()));
}
@@ -1237,16 +1028,11 @@ bool AppInitSanityChecks()
{
// ********************************************************* Step 4: sanity checks
- // Initialize elliptic curve code
- std::string sha256_algo = SHA256AutoDetect();
- LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
- RandomInit();
- ECC_Start();
- globalVerifyHandle.reset(new ECCVerifyHandle());
+ init::SetGlobals();
- // Sanity check
- if (!InitSanityCheck())
+ if (!init::SanityChecks()) {
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
+ }
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
@@ -1277,7 +1063,7 @@ bool AppInitInterfaces(NodeContext& node)
return true;
}
-bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
+bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{
const ArgsManager& args = *Assert(node.args);
const CChainParams& chainparams = Params();
@@ -1286,38 +1072,11 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
// Detailed error printed inside CreatePidFile().
return false;
}
- if (LogInstance().m_print_to_file) {
- if (args.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
- // Do this first since it both loads a bunch of debug.log into memory,
- // and because this needs to happen before any other debug.log printing
- LogInstance().ShrinkDebugFile();
- }
- }
- if (!LogInstance().StartLogging()) {
- return InitError(strprintf(Untranslated("Could not open debug log file %s"),
- LogInstance().m_file_path.string()));
- }
-
- if (!LogInstance().m_log_timestamps)
- LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
- LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
- LogPrintf("Using data directory %s\n", GetDataDir().string());
-
- // Only log conf file usage message if conf file actually exists.
- fs::path config_file_path = GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME));
- if (fs::exists(config_file_path)) {
- LogPrintf("Config file: %s\n", config_file_path.string());
- } else if (args.IsArgSet("-conf")) {
- // Warn if no conf file exists at path provided by user
- InitWarning(strprintf(_("The specified config file %s does not exist"), config_file_path.string()));
- } else {
- // Not categorizing as "Warning" because it's the default behavior
- LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
+ if (!init::StartLogging(args)) {
+ // Detailed error printed inside StartLogging().
+ return false;
}
- // Log the config arguments to debug.log
- args.LogArgs();
-
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
// Warn about relative -datadir path.
@@ -1355,7 +1114,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
node.scheduler = std::make_unique<CScheduler>();
// Start the lightweight task scheduler thread
- node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); });
+ node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { node.scheduler->serviceQueue(); });
// Gather some entropy once per minute.
node.scheduler->scheduleEvery([]{
@@ -1382,7 +1141,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
*/
if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
- if (!AppInitServers(context, node))
+ if (!AppInitServers(node))
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
@@ -1406,7 +1165,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
assert(!node.addrman);
node.addrman = std::make_unique<CAddrMan>();
assert(!node.banman);
- node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
@@ -1516,7 +1275,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
asmap_path = DEFAULT_ASMAP_FILENAME;
}
if (!asmap_path.is_absolute()) {
- asmap_path = GetDataDir() / asmap_path;
+ asmap_path = gArgs.GetDataDirNet() / asmap_path;
}
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"), asmap_path));
@@ -1581,13 +1340,13 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
- bool fReset = fReindex;
+ const bool fReset = fReindex;
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
};
bilingual_str strLoadError;
- uiInterface.InitMessage(_("Loading block index...").translated);
+ uiInterface.InitMessage(_("Loading block index…").translated);
do {
const int64_t load_block_index_start_time = GetTimeMillis();
@@ -1702,29 +1461,17 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
break;
}
- bool failed_rewind{false};
- // Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
- // chainstates beforehand.
- for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
- if (!fReset) {
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
- // It both disconnects blocks based on the chainstate, and drops block data in
- // BlockIndex() based on lack of available witness data.
- uiInterface.InitMessage(_("Rewinding blocks...").translated);
- if (!chainstate->RewindBlockIndex(chainparams)) {
- strLoadError = _(
- "Unable to rewind the database to a pre-fork state. "
- "You will need to redownload the blockchain");
- failed_rewind = true;
- break; // out of the per-chainstate loop
- }
+ if (!fReset) {
+ LOCK(cs_main);
+ auto chainstates{chainman.GetAll()};
+ if (std::any_of(chainstates.begin(), chainstates.end(),
+ [&chainparams](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(chainparams); })) {
+ strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
+ chainparams.GetConsensus().SegwitHeight);
+ break;
}
}
- if (failed_rewind) {
- break; // out of the chainstate activation do-while
- }
-
bool failed_verification = false;
try {
@@ -1732,7 +1479,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
- uiInterface.InitMessage(_("Verifying blocks...").translated);
+ uiInterface.InitMessage(_("Verifying blocks…").translated);
if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
@@ -1748,11 +1495,8 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
break;
}
- // Only verify the DB of the active chainstate. This is fixed in later
- // work when we allow VerifyDB to be parameterized by chainstate.
- if (&::ChainstateActive() == chainstate &&
- !CVerifyDB().VerifyDB(
- chainparams, *chainstate, &chainstate->CoinsDB(),
+ if (!CVerifyDB().VerifyDB(
+ *chainstate, chainparams, chainstate->CoinsDB(),
args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
@@ -1805,12 +1549,23 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
- g_txindex->Start();
+ if (!g_txindex->Start(::ChainstateActive())) {
+ return false;
+ }
}
for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
- GetBlockFilterIndex(filter_type)->Start();
+ if (!GetBlockFilterIndex(filter_type)->Start(::ChainstateActive())) {
+ return false;
+ }
+ }
+
+ if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
+ g_coin_stats_index = std::make_unique<CoinStatsIndex>(/* cache size */ 0, false, fReindex);
+ if (!g_coin_stats_index->Start(::ChainstateActive())) {
+ return false;
+ }
}
// ********************************************************* Step 9: load wallet
@@ -1830,7 +1585,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
if (!fReindex) {
LOCK(cs_main);
for (CChainState* chainstate : chainman.GetAll()) {
- uiInterface.InitMessage(_("Pruning blockstore...").translated);
+ uiInterface.InitMessage(_("Pruning blockstore…").translated);
chainstate->PruneAndFlush();
}
}
@@ -1844,12 +1599,12 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
// ********************************************************* Step 11: import blocks
- if (!CheckDiskSpace(GetDataDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir()));
+ if (!CheckDiskSpace(gArgs.GetDataDirNet())) {
+ InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetDataDirNet()));
return false;
}
- if (!CheckDiskSpace(GetBlocksDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir()));
+ if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
+ InitError(strprintf(_("Error: Disk space is low for %s"), gArgs.GetBlocksDirPath()));
return false;
}
@@ -1880,7 +1635,7 @@ bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAn
vImportFiles.push_back(strFile);
}
- g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman, &args] {
+ chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
ThreadImport(chainman, vImportFiles, args);
});
diff --git a/src/init.h b/src/init.h
index 5d01d4c1ac..328eda9c7e 100644
--- a/src/init.h
+++ b/src/init.h
@@ -64,7 +64,7 @@ bool AppInitInterfaces(NodeContext& node);
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/
-bool AppInitMain(const std::any& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
+bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
/**
* Register all arguments with the ArgsManager
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
new file mode 100644
index 0000000000..49684ede83
--- /dev/null
+++ b/src/init/bitcoin-node.cpp
@@ -0,0 +1,45 @@
+// 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 <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <node/context.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+const char* EXE_NAME = "bitcoin-node";
+
+class BitcoinNodeInit : public interfaces::Init
+{
+public:
+ BitcoinNodeInit(NodeContext& node, const char* arg0)
+ : m_node(node),
+ m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
+ {
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ interfaces::Ipc* ipc() override { return m_ipc.get(); }
+ NodeContext& m_node;
+ std::unique_ptr<interfaces::Ipc> m_ipc;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status)
+{
+ auto init = std::make_unique<init::BitcoinNodeInit>(node, argc > 0 ? argv[0] : "");
+ // Check if bitcoin-node is being invoked as an IPC server. If so, then
+ // bypass normal execution and just respond to requests over the IPC
+ // channel and return null.
+ if (init->m_ipc->startSpawnedProcess(argc, argv, exit_status)) {
+ return nullptr;
+ }
+ return init;
+}
+} // namespace interfaces
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
new file mode 100644
index 0000000000..1e17ce4d3c
--- /dev/null
+++ b/src/init/bitcoind.cpp
@@ -0,0 +1,29 @@
+// 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 <interfaces/init.h>
+#include <node/context.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+class BitcoindInit : public interfaces::Init
+{
+public:
+ BitcoindInit(NodeContext& node) : m_node(node)
+ {
+ m_node.init = this;
+ }
+ NodeContext& m_node;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status)
+{
+ return std::make_unique<init::BitcoindInit>(node);
+}
+} // namespace interfaces
diff --git a/src/init/common.cpp b/src/init/common.cpp
new file mode 100644
index 0000000000..5c1f469081
--- /dev/null
+++ b/src/init/common.cpp
@@ -0,0 +1,167 @@
+// 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.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <clientversion.h>
+#include <compat/sanity.h>
+#include <crypto/sha256.h>
+#include <key.h>
+#include <logging.h>
+#include <node/ui_interface.h>
+#include <pubkey.h>
+#include <random.h>
+#include <util/system.h>
+#include <util/time.h>
+#include <util/translation.h>
+
+#include <memory>
+
+static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
+
+namespace init {
+void SetGlobals()
+{
+ std::string sha256_algo = SHA256AutoDetect();
+ LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
+ RandomInit();
+ ECC_Start();
+ globalVerifyHandle.reset(new ECCVerifyHandle());
+}
+
+void UnsetGlobals()
+{
+ globalVerifyHandle.reset();
+ ECC_Stop();
+}
+
+bool SanityChecks()
+{
+ if (!ECC_InitSanityCheck()) {
+ return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
+ }
+
+ 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;
+}
+
+void AddLoggingArgs(ArgsManager& argsman)
+{
+ argsman.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
+ "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.",
+ ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except the specified category. This option can be specified multiple times to exclude multiple categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+#ifdef HAVE_THREAD_LOCAL
+ argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+#else
+ argsman.AddHiddenArgs({"-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("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+}
+
+void SetLoggingOptions(const ArgsManager& args)
+{
+ LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
+ LogInstance().m_file_path = AbsPathForConfigVal(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
+ LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
+ LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
+ LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+#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);
+}
+
+void SetLoggingCategories(const ArgsManager& args)
+{
+ if (args.IsArgSet("-debug")) {
+ // Special-case: if -debug=0/-nodebug is set, turn off debugging messages
+ const std::vector<std::string> categories = args.GetArgs("-debug");
+
+ if (std::none_of(categories.begin(), categories.end(),
+ [](std::string cat){return cat == "0" || cat == "none";})) {
+ for (const auto& cat : categories) {
+ if (!LogInstance().EnableCategory(cat)) {
+ InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
+ }
+ }
+ }
+ }
+
+ // Now remove the logging categories which were explicitly excluded
+ for (const std::string& cat : args.GetArgs("-debugexclude")) {
+ if (!LogInstance().DisableCategory(cat)) {
+ InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
+ }
+ }
+}
+
+bool StartLogging(const ArgsManager& args)
+{
+ if (LogInstance().m_print_to_file) {
+ if (args.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
+ // Do this first since it both loads a bunch of debug.log into memory,
+ // and because this needs to happen before any other debug.log printing
+ LogInstance().ShrinkDebugFile();
+ }
+ }
+ if (!LogInstance().StartLogging()) {
+ return InitError(strprintf(Untranslated("Could not open debug log file %s"),
+ LogInstance().m_file_path.string()));
+ }
+
+ if (!LogInstance().m_log_timestamps)
+ LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
+ LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
+ LogPrintf("Using data directory %s\n", gArgs.GetDataDirNet().string());
+
+ // Only log conf file usage message if conf file actually exists.
+ fs::path config_file_path = GetConfigFile(args.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ if (fs::exists(config_file_path)) {
+ LogPrintf("Config file: %s\n", config_file_path.string());
+ } else if (args.IsArgSet("-conf")) {
+ // Warn if no conf file exists at path provided by user
+ InitWarning(strprintf(_("The specified config file %s does not exist"), config_file_path.string()));
+ } else {
+ // Not categorizing as "Warning" because it's the default behavior
+ LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
+ }
+
+ // Log the config arguments to debug.log
+ args.LogArgs();
+
+ return true;
+}
+
+void LogPackageVersion()
+{
+ std::string version_string = FormatFullVersion();
+#ifdef DEBUG
+ version_string += " (debug build)";
+#else
+ version_string += " (release build)";
+#endif
+ LogPrintf(PACKAGE_NAME " version %s\n", version_string);
+}
+} // namespace init
diff --git a/src/init/common.h b/src/init/common.h
new file mode 100644
index 0000000000..fc4bc1b280
--- /dev/null
+++ b/src/init/common.h
@@ -0,0 +1,28 @@
+// 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.
+
+//! @file
+//! @brief Common init functions shared by bitcoin-node, bitcoin-wallet, etc.
+
+#ifndef BITCOIN_INIT_COMMON_H
+#define BITCOIN_INIT_COMMON_H
+
+class ArgsManager;
+
+namespace init {
+void SetGlobals();
+void UnsetGlobals();
+/**
+ * Ensure a usable environment with all
+ * necessary library support.
+ */
+bool SanityChecks();
+void AddLoggingArgs(ArgsManager& args);
+void SetLoggingOptions(const ArgsManager& args);
+void SetLoggingCategories(const ArgsManager& args);
+bool StartLogging(const ArgsManager& args);
+void LogPackageVersion();
+} // namespace init
+
+#endif // BITCOIN_INIT_COMMON_H
diff --git a/src/interfaces/README.md b/src/interfaces/README.md
index f77d172153..97167d5298 100644
--- a/src/interfaces/README.md
+++ b/src/interfaces/README.md
@@ -12,6 +12,8 @@ The following interfaces are defined here:
* [`Handler`](handler.h) — returned by `handleEvent` methods on interfaces above and used to manage lifetimes of event handlers.
-* [`Init`](init.h) — used by multiprocess code to access interfaces above on startup. Added in [#10102](https://github.com/bitcoin/bitcoin/pull/10102).
+* [`Init`](init.h) — used by multiprocess code to access interfaces above on startup. Added in [#19160](https://github.com/bitcoin/bitcoin/pull/19160).
-The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in different processes, and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.
+* [`Ipc`](ipc.h) — used by multiprocess code to access `Init` interface across processes. Added in [#19160](https://github.com/bitcoin/bitcoin/pull/19160).
+
+The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in [different processes](../../doc/multiprocess.md), and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.
diff --git a/src/interfaces/echo.cpp b/src/interfaces/echo.cpp
new file mode 100644
index 0000000000..9bbb42217b
--- /dev/null
+++ b/src/interfaces/echo.cpp
@@ -0,0 +1,18 @@
+// 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 <interfaces/echo.h>
+
+#include <memory>
+
+namespace interfaces {
+namespace {
+class EchoImpl : public Echo
+{
+public:
+ std::string echo(const std::string& echo) override { return echo; }
+};
+} // namespace
+std::unique_ptr<Echo> MakeEcho() { return std::make_unique<EchoImpl>(); }
+} // namespace interfaces
diff --git a/src/interfaces/echo.h b/src/interfaces/echo.h
new file mode 100644
index 0000000000..5578d9d9e6
--- /dev/null
+++ b/src/interfaces/echo.h
@@ -0,0 +1,26 @@
+// 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_INTERFACES_ECHO_H
+#define BITCOIN_INTERFACES_ECHO_H
+
+#include <memory>
+#include <string>
+
+namespace interfaces {
+//! Simple string echoing interface for testing.
+class Echo
+{
+public:
+ virtual ~Echo() {}
+
+ //! Echo provided string.
+ virtual std::string echo(const std::string& echo) = 0;
+};
+
+//! Return implementation of Echo interface.
+std::unique_ptr<Echo> MakeEcho();
+} // namespace interfaces
+
+#endif // BITCOIN_INTERFACES_ECHO_H
diff --git a/src/interfaces/init.cpp b/src/interfaces/init.cpp
new file mode 100644
index 0000000000..a3c949e616
--- /dev/null
+++ b/src/interfaces/init.cpp
@@ -0,0 +1,17 @@
+// 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 <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+
+namespace interfaces {
+std::unique_ptr<Node> Init::makeNode() { return {}; }
+std::unique_ptr<Chain> Init::makeChain() { return {}; }
+std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; }
+std::unique_ptr<Echo> Init::makeEcho() { return {}; }
+Ipc* Init::ipc() { return nullptr; }
+} // namespace interfaces
diff --git a/src/interfaces/init.h b/src/interfaces/init.h
new file mode 100644
index 0000000000..2a38054a17
--- /dev/null
+++ b/src/interfaces/init.h
@@ -0,0 +1,52 @@
+// 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_INTERFACES_INIT_H
+#define BITCOIN_INTERFACES_INIT_H
+
+#include <memory>
+
+struct NodeContext;
+
+namespace interfaces {
+class Chain;
+class Echo;
+class Ipc;
+class Node;
+class WalletClient;
+
+//! Initial interface created when a process is first started, and used to give
+//! and get access to other interfaces (Node, Chain, Wallet, etc).
+//!
+//! There is a different Init interface implementation for each process
+//! (bitcoin-gui, bitcoin-node, bitcoin-wallet, bitcoind, bitcoin-qt) and each
+//! implementation can implement the make methods for interfaces it supports.
+//! The default make methods all return null.
+class Init
+{
+public:
+ virtual ~Init() = default;
+ virtual std::unique_ptr<Node> makeNode();
+ virtual std::unique_ptr<Chain> makeChain();
+ virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain);
+ virtual std::unique_ptr<Echo> makeEcho();
+ virtual Ipc* ipc();
+};
+
+//! Return implementation of Init interface for the node process. If the argv
+//! indicates that this is a child process spawned to handle requests from a
+//! parent process, this blocks and handles requests, then returns null and a
+//! status code to exit with. If this returns non-null, the caller can start up
+//! normally and use the Init object to spawn and connect to other processes
+//! while it is running.
+std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status);
+
+//! Return implementation of Init interface for the wallet process.
+std::unique_ptr<Init> MakeWalletInit(int argc, char* argv[], int& exit_status);
+
+//! Return implementation of Init interface for the gui process.
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[]);
+} // namespace interfaces
+
+#endif // BITCOIN_INTERFACES_INIT_H
diff --git a/src/interfaces/ipc.h b/src/interfaces/ipc.h
new file mode 100644
index 0000000000..e9e6c78053
--- /dev/null
+++ b/src/interfaces/ipc.h
@@ -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.
+
+#ifndef BITCOIN_INTERFACES_IPC_H
+#define BITCOIN_INTERFACES_IPC_H
+
+#include <functional>
+#include <memory>
+#include <typeindex>
+
+namespace interfaces {
+class Init;
+
+//! Interface providing access to interprocess-communication (IPC)
+//! functionality. The IPC implementation is responsible for establishing
+//! connections between a controlling process and a process being controlled.
+//! When a connection is established, the process being controlled returns an
+//! interfaces::Init pointer to the controlling process, which the controlling
+//! process can use to get access to other interfaces and functionality.
+//!
+//! When spawning a new process, the steps are:
+//!
+//! 1. The controlling process calls interfaces::Ipc::spawnProcess(), which
+//! calls ipc::Process::spawn(), which spawns a new process and returns a
+//! socketpair file descriptor for communicating with it.
+//! interfaces::Ipc::spawnProcess() then calls ipc::Protocol::connect()
+//! passing the socketpair descriptor, which returns a local proxy
+//! interfaces::Init implementation calling remote interfaces::Init methods.
+//! 2. The spawned process calls interfaces::Ipc::startSpawnProcess(), which
+//! calls ipc::Process::checkSpawned() to read command line arguments and
+//! determine whether it is a spawned process and what socketpair file
+//! descriptor it should use. It then calls ipc::Protocol::serve() to handle
+//! incoming requests from the socketpair and invoke interfaces::Init
+//! interface methods, and exit when the socket is closed.
+//! 3. The controlling process calls local proxy interfaces::Init object methods
+//! to make other proxy objects calling other remote interfaces. It can also
+//! destroy the initial interfaces::Init object to close the connection and
+//! shut down the spawned process.
+class Ipc
+{
+public:
+ virtual ~Ipc() = default;
+
+ //! Spawn a child process returning pointer to its Init interface.
+ virtual std::unique_ptr<Init> spawnProcess(const char* exe_name) = 0;
+
+ //! If this is a spawned process, block and handle requests from the parent
+ //! process by forwarding them to this process's Init interface, then return
+ //! true. If this is not a spawned child process, return false.
+ virtual bool startSpawnedProcess(int argc, char* argv[], int& exit_status) = 0;
+
+ //! Add cleanup callback to remote interface that will run when the
+ //! interface is deleted.
+ template<typename Interface>
+ void addCleanup(Interface& iface, std::function<void()> cleanup)
+ {
+ addCleanup(typeid(Interface), &iface, std::move(cleanup));
+ }
+
+protected:
+ //! Internal implementation of public addCleanup method (above) as a
+ //! type-erased virtual function, since template functions can't be virtual.
+ virtual void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) = 0;
+};
+
+//! Return implementation of Ipc interface.
+std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init);
+} // namespace interfaces
+
+#endif // BITCOIN_INTERFACES_IPC_H
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 6ccfd7fc20..88f93321f9 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -112,14 +112,11 @@ public:
//! Get wallet address list.
virtual std::vector<WalletAddress> getAddresses() = 0;
- //! Add dest data.
- virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0;
+ //! Get receive requests.
+ virtual std::vector<std::string> getAddressReceiveRequests() = 0;
- //! Erase dest data.
- virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0;
-
- //! Get dest values with prefix.
- virtual std::vector<std::string> getDestValues(const std::string& prefix) = 0;
+ //! Save or remove receive request.
+ virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0;
//! Lock coin.
virtual void lockCoin(const COutPoint& output) = 0;
diff --git a/src/ipc/capnp/.gitignore b/src/ipc/capnp/.gitignore
new file mode 100644
index 0000000000..036df1430c
--- /dev/null
+++ b/src/ipc/capnp/.gitignore
@@ -0,0 +1,2 @@
+# capnp generated files
+*.capnp.*
diff --git a/src/ipc/capnp/echo.capnp b/src/ipc/capnp/echo.capnp
new file mode 100644
index 0000000000..df36ee0de3
--- /dev/null
+++ b/src/ipc/capnp/echo.capnp
@@ -0,0 +1,17 @@
+# 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.
+
+@0x888b4f7f51e691f7;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("ipc::capnp::messages");
+
+using Proxy = import "/mp/proxy.capnp";
+$Proxy.include("interfaces/echo.h");
+$Proxy.include("ipc/capnp/echo.capnp.h");
+
+interface Echo $Proxy.wrap("interfaces::Echo") {
+ destroy @0 (context :Proxy.Context) -> ();
+ echo @1 (context :Proxy.Context, echo: Text) -> (result :Text);
+}
diff --git a/src/ipc/capnp/init-types.h b/src/ipc/capnp/init-types.h
new file mode 100644
index 0000000000..42031441b5
--- /dev/null
+++ b/src/ipc/capnp/init-types.h
@@ -0,0 +1,10 @@
+// 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_IPC_CAPNP_INIT_TYPES_H
+#define BITCOIN_IPC_CAPNP_INIT_TYPES_H
+
+#include <ipc/capnp/echo.capnp.proxy-types.h>
+
+#endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H
diff --git a/src/ipc/capnp/init.capnp b/src/ipc/capnp/init.capnp
new file mode 100644
index 0000000000..e6d358c665
--- /dev/null
+++ b/src/ipc/capnp/init.capnp
@@ -0,0 +1,20 @@
+# 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.
+
+@0xf2c5cfa319406aa6;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("ipc::capnp::messages");
+
+using Proxy = import "/mp/proxy.capnp";
+$Proxy.include("interfaces/echo.h");
+$Proxy.include("interfaces/init.h");
+$Proxy.includeTypes("ipc/capnp/init-types.h");
+
+using Echo = import "echo.capnp";
+
+interface Init $Proxy.wrap("interfaces::Init") {
+ construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
+ makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
+}
diff --git a/src/ipc/capnp/protocol.cpp b/src/ipc/capnp/protocol.cpp
new file mode 100644
index 0000000000..74c66c899a
--- /dev/null
+++ b/src/ipc/capnp/protocol.cpp
@@ -0,0 +1,90 @@
+// 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 <interfaces/init.h>
+#include <ipc/capnp/init.capnp.h>
+#include <ipc/capnp/init.capnp.proxy.h>
+#include <ipc/capnp/protocol.h>
+#include <ipc/exception.h>
+#include <ipc/protocol.h>
+#include <kj/async.h>
+#include <logging.h>
+#include <mp/proxy-io.h>
+#include <mp/proxy-types.h>
+#include <mp/util.h>
+#include <util/threadnames.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <thread>
+
+namespace ipc {
+namespace capnp {
+namespace {
+void IpcLogFn(bool raise, std::string message)
+{
+ LogPrint(BCLog::IPC, "%s\n", message);
+ if (raise) throw Exception(message);
+}
+
+class CapnpProtocol : public Protocol
+{
+public:
+ ~CapnpProtocol() noexcept(true)
+ {
+ if (m_loop) {
+ std::unique_lock<std::mutex> lock(m_loop->m_mutex);
+ m_loop->removeClient(lock);
+ }
+ if (m_loop_thread.joinable()) m_loop_thread.join();
+ assert(!m_loop);
+ };
+ std::unique_ptr<interfaces::Init> connect(int fd, const char* exe_name) override
+ {
+ startLoop(exe_name);
+ return mp::ConnectStream<messages::Init>(*m_loop, fd);
+ }
+ void serve(int fd, const char* exe_name, interfaces::Init& init) override
+ {
+ assert(!m_loop);
+ mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
+ m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ mp::ServeStream<messages::Init>(*m_loop, fd, init);
+ m_loop->loop();
+ m_loop.reset();
+ }
+ void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
+ {
+ mp::ProxyTypeRegister::types().at(type)(iface).cleanup.emplace_back(std::move(cleanup));
+ }
+ void startLoop(const char* exe_name)
+ {
+ if (m_loop) return;
+ std::promise<void> promise;
+ m_loop_thread = std::thread([&] {
+ util::ThreadRename("capnp-loop");
+ m_loop.emplace(exe_name, &IpcLogFn, nullptr);
+ {
+ std::unique_lock<std::mutex> lock(m_loop->m_mutex);
+ m_loop->addClient(lock);
+ }
+ promise.set_value();
+ m_loop->loop();
+ m_loop.reset();
+ });
+ promise.get_future().wait();
+ }
+ std::thread m_loop_thread;
+ std::optional<mp::EventLoop> m_loop;
+};
+} // namespace
+
+std::unique_ptr<Protocol> MakeCapnpProtocol() { return std::make_unique<CapnpProtocol>(); }
+} // namespace capnp
+} // namespace ipc
diff --git a/src/ipc/capnp/protocol.h b/src/ipc/capnp/protocol.h
new file mode 100644
index 0000000000..eb057949d2
--- /dev/null
+++ b/src/ipc/capnp/protocol.h
@@ -0,0 +1,17 @@
+// 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_IPC_CAPNP_PROTOCOL_H
+#define BITCOIN_IPC_CAPNP_PROTOCOL_H
+
+#include <memory>
+
+namespace ipc {
+class Protocol;
+namespace capnp {
+std::unique_ptr<Protocol> MakeCapnpProtocol();
+} // namespace capnp
+} // namespace ipc
+
+#endif // BITCOIN_IPC_CAPNP_PROTOCOL_H
diff --git a/src/ipc/exception.h b/src/ipc/exception.h
new file mode 100644
index 0000000000..53dee8124a
--- /dev/null
+++ b/src/ipc/exception.h
@@ -0,0 +1,20 @@
+// 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_IPC_EXCEPTION_H
+#define BITCOIN_IPC_EXCEPTION_H
+
+#include <stdexcept>
+
+namespace ipc {
+//! Exception class thrown when a call to remote method fails due to an IPC
+//! error, like a socket getting disconnected.
+class Exception : public std::runtime_error
+{
+public:
+ using std::runtime_error::runtime_error;
+};
+} // namespace ipc
+
+#endif // BITCOIN_IPC_EXCEPTION_H
diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp
new file mode 100644
index 0000000000..ad4b78ed81
--- /dev/null
+++ b/src/ipc/interfaces.cpp
@@ -0,0 +1,77 @@
+// 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 <fs.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <ipc/capnp/protocol.h>
+#include <ipc/process.h>
+#include <ipc/protocol.h>
+#include <logging.h>
+#include <tinyformat.h>
+#include <util/system.h>
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <utility>
+#include <vector>
+
+namespace ipc {
+namespace {
+class IpcImpl : public interfaces::Ipc
+{
+public:
+ IpcImpl(const char* exe_name, const char* process_argv0, interfaces::Init& init)
+ : m_exe_name(exe_name), m_process_argv0(process_argv0), m_init(init),
+ m_protocol(ipc::capnp::MakeCapnpProtocol()), m_process(ipc::MakeProcess())
+ {
+ }
+ std::unique_ptr<interfaces::Init> spawnProcess(const char* new_exe_name) override
+ {
+ int pid;
+ int fd = m_process->spawn(new_exe_name, m_process_argv0, pid);
+ LogPrint(::BCLog::IPC, "Process %s pid %i launched\n", new_exe_name, pid);
+ auto init = m_protocol->connect(fd, m_exe_name);
+ Ipc::addCleanup(*init, [this, new_exe_name, pid] {
+ int status = m_process->waitSpawned(pid);
+ LogPrint(::BCLog::IPC, "Process %s pid %i exited with status %i\n", new_exe_name, pid, status);
+ });
+ return init;
+ }
+ bool startSpawnedProcess(int argc, char* argv[], int& exit_status) override
+ {
+ exit_status = EXIT_FAILURE;
+ int32_t fd = -1;
+ if (!m_process->checkSpawned(argc, argv, fd)) {
+ return false;
+ }
+ m_protocol->serve(fd, m_exe_name, m_init);
+ exit_status = EXIT_SUCCESS;
+ return true;
+ }
+ void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
+ {
+ m_protocol->addCleanup(type, iface, std::move(cleanup));
+ }
+ const char* m_exe_name;
+ const char* m_process_argv0;
+ interfaces::Init& m_init;
+ std::unique_ptr<Protocol> m_protocol;
+ std::unique_ptr<Process> m_process;
+};
+} // namespace
+} // namespace ipc
+
+namespace interfaces {
+std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
+{
+ return std::make_unique<ipc::IpcImpl>(exe_name, process_argv0, init);
+}
+} // namespace interfaces
diff --git a/src/ipc/process.cpp b/src/ipc/process.cpp
new file mode 100644
index 0000000000..43ed1f1bae
--- /dev/null
+++ b/src/ipc/process.cpp
@@ -0,0 +1,61 @@
+// 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 <fs.h>
+#include <ipc/process.h>
+#include <ipc/protocol.h>
+#include <mp/util.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+
+#include <cstdint>
+#include <exception>
+#include <iostream>
+#include <stdexcept>
+#include <stdlib.h>
+#include <string.h>
+#include <system_error>
+#include <unistd.h>
+#include <utility>
+#include <vector>
+
+namespace ipc {
+namespace {
+class ProcessImpl : public Process
+{
+public:
+ int spawn(const std::string& new_exe_name, const fs::path& argv0_path, int& pid) override
+ {
+ return mp::SpawnProcess(pid, [&](int fd) {
+ fs::path path = argv0_path;
+ path.remove_filename();
+ path.append(new_exe_name);
+ return std::vector<std::string>{path.string(), "-ipcfd", strprintf("%i", fd)};
+ });
+ }
+ int waitSpawned(int pid) override { return mp::WaitProcess(pid); }
+ bool checkSpawned(int argc, char* argv[], int& fd) override
+ {
+ // If this process was not started with a single -ipcfd argument, it is
+ // not a process spawned by the spawn() call above, so return false and
+ // do not try to serve requests.
+ if (argc != 3 || strcmp(argv[1], "-ipcfd") != 0) {
+ return false;
+ }
+ // If a single -ipcfd argument was provided, return true and get the
+ // file descriptor so Protocol::serve() can be called to handle
+ // requests from the parent process. The -ipcfd argument is not valid
+ // in combination with other arguments because the parent process
+ // should be able to control the child process through the IPC protocol
+ // without passing information out of band.
+ if (!ParseInt32(argv[2], &fd)) {
+ throw std::runtime_error(strprintf("Invalid -ipcfd number '%s'", argv[2]));
+ }
+ return true;
+ }
+};
+} // namespace
+
+std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
+} // namespace ipc
diff --git a/src/ipc/process.h b/src/ipc/process.h
new file mode 100644
index 0000000000..4bb2930d9c
--- /dev/null
+++ b/src/ipc/process.h
@@ -0,0 +1,42 @@
+// 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_IPC_PROCESS_H
+#define BITCOIN_IPC_PROCESS_H
+
+#include <memory>
+#include <string>
+
+namespace ipc {
+class Protocol;
+
+//! IPC process interface for spawning bitcoin processes and serving requests
+//! in processes that have been spawned.
+//!
+//! There will be different implementations of this interface depending on the
+//! platform (e.g. unix, windows).
+class Process
+{
+public:
+ virtual ~Process() = default;
+
+ //! Spawn process and return socket file descriptor for communicating with
+ //! it.
+ virtual int spawn(const std::string& new_exe_name, const fs::path& argv0_path, int& pid) = 0;
+
+ //! Wait for spawned process to exit and return its exit code.
+ virtual int waitSpawned(int pid) = 0;
+
+ //! Parse command line and determine if current process is a spawned child
+ //! process. If so, return true and a file descriptor for communicating
+ //! with the parent process.
+ virtual bool checkSpawned(int argc, char* argv[], int& fd) = 0;
+};
+
+//! Constructor for Process interface. Implementation will vary depending on
+//! the platform (unix or windows).
+std::unique_ptr<Process> MakeProcess();
+} // namespace ipc
+
+#endif // BITCOIN_IPC_PROCESS_H
diff --git a/src/ipc/protocol.h b/src/ipc/protocol.h
new file mode 100644
index 0000000000..af955b0007
--- /dev/null
+++ b/src/ipc/protocol.h
@@ -0,0 +1,39 @@
+// 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_IPC_PROTOCOL_H
+#define BITCOIN_IPC_PROTOCOL_H
+
+#include <interfaces/init.h>
+
+#include <functional>
+#include <memory>
+#include <typeindex>
+
+namespace ipc {
+//! IPC protocol interface for calling IPC methods over sockets.
+//!
+//! There may be different implementations of this interface for different IPC
+//! protocols (e.g. Cap'n Proto, gRPC, JSON-RPC, or custom protocols).
+class Protocol
+{
+public:
+ virtual ~Protocol() = default;
+
+ //! Return Init interface that forwards requests over given socket descriptor.
+ //! Socket communication is handled on a background thread.
+ virtual std::unique_ptr<interfaces::Init> connect(int fd, const char* exe_name) = 0;
+
+ //! Handle requests on provided socket descriptor, forwarding them to the
+ //! provided Init interface. Socket communication is handled on the
+ //! current thread, and this call blocks until the socket is closed.
+ virtual void serve(int fd, const char* exe_name, interfaces::Init& init) = 0;
+
+ //! Add cleanup callback to interface that will run when the interface is
+ //! deleted.
+ virtual void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) = 0;
+};
+} // namespace ipc
+
+#endif // BITCOIN_IPC_PROTOCOL_H
diff --git a/src/key.cpp b/src/key.cpp
index 1e59b301cb..5666adebb8 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -293,7 +293,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = key.GetPubKey().GetID();
- memcpy(&out.vchFingerprint[0], &id, 4);
+ memcpy(out.vchFingerprint, &id, 4);
out.nChild = _nChild;
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
@@ -312,7 +312,7 @@ void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) {
CExtPubKey CExtKey::Neuter() const {
CExtPubKey ret;
ret.nDepth = nDepth;
- memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4);
+ memcpy(ret.vchFingerprint, vchFingerprint, 4);
ret.nChild = nChild;
ret.pubkey = key.GetPubKey();
ret.chaincode = chaincode;
diff --git a/src/key.h b/src/key.h
index 206322956c..3ee49a778b 100644
--- a/src/key.h
+++ b/src/key.h
@@ -151,7 +151,7 @@ struct CExtKey {
friend bool operator==(const CExtKey& a, const CExtKey& b)
{
return a.nDepth == b.nDepth &&
- memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ memcmp(a.vchFingerprint, b.vchFingerprint, sizeof(vchFingerprint)) == 0 &&
a.nChild == b.nChild &&
a.chaincode == b.chaincode &&
a.key == b.key;
diff --git a/src/key_io.cpp b/src/key_io.cpp
index dbcbfa1f29..615f4c9312 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -54,6 +54,14 @@ public:
return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
}
+ std::string operator()(const WitnessV1Taproot& tap) const
+ {
+ std::vector<unsigned char> data = {1};
+ data.reserve(53);
+ ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, tap.begin(), tap.end());
+ return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
+ }
+
std::string operator()(const WitnessUnknown& id) const
{
if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
@@ -135,6 +143,13 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
return CNoDestination();
}
+ if (version == 1 && data.size() == WITNESS_V1_TAPROOT_SIZE) {
+ static_assert(WITNESS_V1_TAPROOT_SIZE == WitnessV1Taproot::size());
+ WitnessV1Taproot tap;
+ std::copy(data.begin(), data.end(), tap.begin());
+ return tap;
+ }
+
if (version > 16) {
error_str = "Invalid Bech32 address witness version";
return CNoDestination();
diff --git a/src/logging.cpp b/src/logging.cpp
index 866213786e..e5187fd596 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -157,6 +157,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::LEVELDB, "leveldb"},
{BCLog::VALIDATION, "validation"},
{BCLog::I2P, "i2p"},
+ {BCLog::IPC, "ipc"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
diff --git a/src/logging.h b/src/logging.h
index 436f0cd12e..d04bc99268 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -58,6 +58,7 @@ namespace BCLog {
LEVELDB = (1 << 20),
VALIDATION = (1 << 21),
I2P = (1 << 22),
+ IPC = (1 << 23),
ALL = ~(uint32_t)0,
};
diff --git a/src/mapport.cpp b/src/mapport.cpp
index 2df4ce45d2..135efb561e 100644
--- a/src/mapport.cpp
+++ b/src/mapport.cpp
@@ -15,6 +15,7 @@
#include <netbase.h>
#include <threadinterrupt.h>
#include <util/system.h>
+#include <util/thread.h>
#ifdef USE_NATPMP
#include <compat.h>
@@ -255,7 +256,7 @@ void StartThreadMapPort()
{
if (!g_mapport_thread.joinable()) {
assert(!g_mapport_interrupt);
- g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort));
+ g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
}
}
diff --git a/src/miner.cpp b/src/miner.cpp
index 8a9406f810..eccddbb04f 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -39,13 +39,22 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
return nNewTime - nOldTime;
}
-void RegenerateCommitments(CBlock& block, BlockManager& blockman)
+void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
{
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, assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); return blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus());
+ CBlockIndex* prev_block;
+ {
+ // TODO: Temporary scope to check correctness of refactored code.
+ // Should be removed manually after merge of
+ // https://github.com/bitcoin/bitcoin/pull/20158
+ LOCK(::cs_main);
+ assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman.m_blockman));
+ prev_block = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
+ }
+ GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
}
diff --git a/src/miner.h b/src/miner.h
index c400c90f6c..10a80f4392 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -202,8 +202,7 @@ private:
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
-// TODO just accept a CBlockIndex*
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
-void RegenerateCommitments(CBlock& block, BlockManager& blockman);
+void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
#endif // BITCOIN_MINER_H
diff --git a/src/net.cpp b/src/net.cpp
index c43c53795e..6f9f17ed4e 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <crypto/sha256.h>
#include <i2p.h>
#include <net_permissions.h>
+#include <netaddress.h>
#include <netbase.h>
#include <node/ui_interface.h>
#include <protocol.h>
@@ -23,6 +24,7 @@
#include <scheduler.h>
#include <util/sock.h>
#include <util/strencodings.h>
+#include <util/thread.h>
#include <util/translation.h>
#ifdef WIN32
@@ -145,8 +147,8 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
return nBestScore >= 0;
}
-//! Convert the pnSeed6 array into usable address objects.
-static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn)
+//! Convert the serialized seeds into usable address objects.
+static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
@@ -154,13 +156,14 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// weeks ago.
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
- vSeedsOut.reserve(vSeedsIn.size());
FastRandomContext rng;
- for (const auto& seed_in : vSeedsIn) {
- struct in6_addr ip;
- memcpy(&ip, seed_in.addr, sizeof(ip));
- CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE));
+ CDataStream s(vSeedsIn, SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ while (!s.eof()) {
+ CService endpoint;
+ s >> endpoint;
+ CAddress addr{endpoint, GetDesirableServiceFlags(NODE_NONE)};
addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek;
+ LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToString());
vSeedsOut.push_back(addr);
}
return vSeedsOut;
@@ -680,19 +683,19 @@ int V1TransportDeserializer::readHeader(Span<const uint8_t> msg_bytes)
hdrbuf >> hdr;
}
catch (const std::exception&) {
- LogPrint(BCLog::NET, "HEADER ERROR - UNABLE TO DESERIALIZE, peer=%d\n", m_node_id);
+ LogPrint(BCLog::NET, "Header error: Unable to deserialize, peer=%d\n", m_node_id);
return -1;
}
// Check start string, network magic
if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.pchMessageStart), m_node_id);
+ LogPrint(BCLog::NET, "Header error: Wrong MessageStart %s received, peer=%d\n", HexStr(hdr.pchMessageStart), m_node_id);
return -1;
}
// reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
- LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes), peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, m_node_id);
+ LogPrint(BCLog::NET, "Header error: Size too large (%s, %u bytes), peer=%d\n", SanitizeString(hdr.GetCommand()), hdr.nMessageSize, m_node_id);
return -1;
}
@@ -745,7 +748,7 @@ std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono
// Check checksum and header command string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
- LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
+ LogPrint(BCLog::NET, "Header error: Wrong checksum (%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)),
HexStr(hdr.pchChecksum),
@@ -753,8 +756,8 @@ std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono
out_err_raw_size = msg->m_raw_message_size;
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);
+ LogPrint(BCLog::NET, "Header error: Invalid message type (%s, %u bytes), peer=%d\n",
+ SanitizeString(hdr.GetCommand()), msg->m_message_size, m_node_id);
out_err_raw_size = msg->m_raw_message_size;
msg.reset();
}
@@ -793,7 +796,7 @@ size_t CConnman::SocketSendData(CNode& node) const
nBytes = send(node.hSocket, reinterpret_cast<const char*>(data.data()) + node.nSendOffset, data.size() - node.nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
}
if (nBytes > 0) {
- node.nLastSend = GetSystemTimeInSeconds();
+ node.nLastSend = GetTimeSeconds();
node.nSendBytes += nBytes;
node.nSendOffset += nBytes;
nSentSize += nBytes;
@@ -1003,7 +1006,7 @@ bool CConnman::AttemptToEvictConnection()
LOCK(cs_vNodes);
for (const CNode* node : vNodes) {
- if (node->HasPermission(PF_NOBAN))
+ if (node->HasPermission(NetPermissionFlags::NoBan))
continue;
if (!node->IsInboundConn())
continue;
@@ -1060,7 +1063,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
const CAddress addr_bind = GetBindAddress(hSocket);
- NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
+ NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr);
@@ -1075,12 +1078,12 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
int nMaxInbound = nMaxConnections - m_max_outbound;
AddWhitelistPermissionFlags(permissionFlags, addr);
- if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
- NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
- if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
- if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
- NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
- NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::Implicit)) {
+ NetPermissions::ClearFlag(permissionFlags, NetPermissionFlags::Implicit);
+ if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::ForceRelay);
+ if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::Relay);
+ NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::Mempool);
+ NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::NoBan);
}
{
@@ -1109,7 +1112,7 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
- if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && banned)
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && banned)
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -1118,7 +1121,7 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
// Only accept connections from discouraged peers if our inbound slots aren't (almost) full.
bool discouraged = m_banman && m_banman->IsDiscouraged(addr);
- if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && nInbound + 1 >= nMaxInbound && discouraged)
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && nInbound + 1 >= nMaxInbound && discouraged)
{
LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
CloseSocket(hSocket);
@@ -1139,7 +1142,7 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
ServiceFlags nodeServices = nLocalServices;
- if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::BloomFilter)) {
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
}
@@ -1223,19 +1226,10 @@ void CConnman::DisconnectNodes()
std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
for (CNode* pnode : vNodesDisconnectedCopy)
{
- // wait until threads are done using it
+ // Destroy the object only after other threads have stopped using it.
if (pnode->GetRefCount() <= 0) {
- bool fDelete = false;
- {
- TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend) {
- fDelete = true;
- }
- }
- if (fDelete) {
- vNodesDisconnected.remove(pnode);
- DeleteNode(pnode);
- }
+ vNodesDisconnected.remove(pnode);
+ DeleteNode(pnode);
}
}
}
@@ -1257,7 +1251,7 @@ void CConnman::NotifyNumConnectionsChanged()
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now_in) const
{
- const int64_t now = now_in ? now_in.value() : GetSystemTimeInSeconds();
+ const int64_t now = now_in ? now_in.value() : GetTimeSeconds();
return node.nTimeConnected + m_peer_connect_timeout < now;
}
@@ -1265,7 +1259,7 @@ 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();
+ int64_t now = GetTimeSeconds();
if (!ShouldRunInactivityChecks(node, now)) return false;
@@ -1644,7 +1638,7 @@ void CConnman::ThreadDNSAddressSeed()
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (pnode->fSuccessfullyConnected && pnode->IsOutboundOrBlockRelayConn()) ++nRelevant;
+ if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant;
}
}
if (nRelevant >= 2) {
@@ -1731,7 +1725,7 @@ void CConnman::ProcessAddrFetch()
}
}
-bool CConnman::GetTryNewOutboundPeer()
+bool CConnman::GetTryNewOutboundPeer() const
{
return m_try_another_outbound_peer;
}
@@ -1748,7 +1742,7 @@ void CConnman::SetTryNewOutboundPeer(bool flag)
// Also exclude peers that haven't finished initial connection handshake yet
// (so that we don't decide we're over our desired connection limit, and then
// evict some peer that has finished the handshake)
-int CConnman::GetExtraFullOutboundCount()
+int CConnman::GetExtraFullOutboundCount() const
{
int full_outbound_peers = 0;
{
@@ -1762,7 +1756,7 @@ int CConnman::GetExtraFullOutboundCount()
return std::max(full_outbound_peers - m_max_outbound_full_relay, 0);
}
-int CConnman::GetExtraBlockRelayCount()
+int CConnman::GetExtraBlockRelayCount() const
{
int block_relay_peers = 0;
{
@@ -1847,7 +1841,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (add_fixed_seeds_now) {
CNetAddr local;
local.SetInternal("fixedseeds");
- addrman.Add(convertSeed6(Params().FixedSeeds()), local);
+ addrman.Add(ConvertSeeds(Params().FixedSeeds()), local);
add_fixed_seeds = false;
}
}
@@ -2060,7 +2054,7 @@ std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
return ret;
}
-std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
+std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
{
std::vector<AddedNodeInfo> ret;
@@ -2252,7 +2246,7 @@ void CConnman::ThreadI2PAcceptIncoming()
}
if (!advertising_listen_addr) {
- AddLocal(conn.me, LOCAL_BIND);
+ AddLocal(conn.me, LOCAL_MANUAL);
advertising_listen_addr = true;
}
@@ -2260,7 +2254,7 @@ void CConnman::ThreadI2PAcceptIncoming()
continue;
}
- CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::PF_NONE,
+ CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::None,
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
}
}
@@ -2407,8 +2401,9 @@ NodeId CConnman::GetNewNodeId()
bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
- if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
+ if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) {
return false;
+ }
bilingual_str strError;
if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
@@ -2417,7 +2412,7 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags
return false;
}
- if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !(permissions & PF_NOBAN)) {
+ if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !NetPermissions::HasFlag(permissions, NetPermissionFlags::NoBan)) {
AddLocal(addr, LOCAL_BIND);
}
@@ -2431,7 +2426,7 @@ bool CConnman::InitBinds(
{
bool fBound = false;
for (const auto& addrBind : binds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::None);
}
for (const auto& addrBind : whiteBinds) {
fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
@@ -2440,12 +2435,12 @@ bool CConnman::InitBinds(
struct in_addr inaddr_any;
inaddr_any.s_addr = htonl(INADDR_ANY);
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
- fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
+ fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
}
for (const auto& addr_bind : onion_binds) {
- fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::None);
}
return fBound;
@@ -2466,7 +2461,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
proxyType i2p_sam;
if (GetProxy(NET_I2P, i2p_sam)) {
- m_i2p_sam_session = std::make_unique<i2p::sam::Session>(GetDataDir() / "i2p_private_key",
+ m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
i2p_sam.proxy, &interruptNet);
}
@@ -2475,7 +2470,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
if (clientInterface) {
- clientInterface->InitMessage(_("Loading P2P addresses...").translated);
+ clientInterface->InitMessage(_("Loading P2P addresses…").translated);
}
// Load addresses from peers.dat
int64_t nStart = GetTimeMillis();
@@ -2492,14 +2487,14 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (m_use_addrman_outgoing) {
// Load addresses from anchors.dat
- m_anchors = ReadAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME);
+ m_anchors = ReadAnchors(gArgs.GetDataDirNet() / ANCHORS_DATABASE_FILENAME);
if (m_anchors.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
m_anchors.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
LogPrintf("%i block-relay-only anchors will be tried for connections.\n", m_anchors.size());
}
- uiInterface.InitMessage(_("Starting network threads...").translated);
+ uiInterface.InitMessage(_("Starting network threads…").translated);
fAddressesInitialized = true;
@@ -2526,15 +2521,15 @@ 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)));
+ threadSocketHandler = std::thread(&util::TraceThread, "net", [this] { ThreadSocketHandler(); });
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)));
+ threadDNSAddressSeed = std::thread(&util::TraceThread, "dnsseed", [this] { ThreadDNSAddressSeed(); });
// Initiate manual connections
- threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));
+ threadOpenAddedConnections = std::thread(&util::TraceThread, "addcon", [this] { ThreadOpenAddedConnections(); });
if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
if (clientInterface) {
@@ -2544,16 +2539,18 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
return false;
}
- if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty())
- threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing)));
+ if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) {
+ threadOpenConnections = std::thread(
+ &util::TraceThread, "opencon",
+ [this, connect = connOptions.m_specified_outgoing] { ThreadOpenConnections(connect); });
+ }
// Process messages
- threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
+ threadMessageHandler = std::thread(&util::TraceThread, "msghand", [this] { ThreadMessageHandler(); });
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)));
+ std::thread(&util::TraceThread, "i2paccept", [this] { ThreadI2PAcceptIncoming(); });
}
// Dump network addresses
@@ -2630,27 +2627,30 @@ void CConnman::StopNodes()
if (anchors_to_dump.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
anchors_to_dump.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
- DumpAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
+ DumpAnchors(gArgs.GetDataDirNet() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
}
}
- // Close sockets
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
+ // Delete peer connections.
+ std::vector<CNode*> nodes;
+ WITH_LOCK(cs_vNodes, nodes.swap(vNodes));
+ for (CNode* pnode : nodes) {
pnode->CloseSocketDisconnect();
- for (ListenSocket& hListenSocket : vhListenSocket)
- if (hListenSocket.socket != INVALID_SOCKET)
- if (!CloseSocket(hListenSocket.socket))
- LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
-
- // clean up some globals (to help leak detection)
- for (CNode* pnode : vNodes) {
DeleteNode(pnode);
}
+
+ // Close listening sockets.
+ for (ListenSocket& hListenSocket : vhListenSocket) {
+ if (hListenSocket.socket != INVALID_SOCKET) {
+ if (!CloseSocket(hListenSocket.socket)) {
+ LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
+ }
+ }
+ }
+
for (CNode* pnode : vNodesDisconnected) {
DeleteNode(pnode);
}
- vNodes.clear();
vNodesDisconnected.clear();
vhListenSocket.clear();
semOutbound.reset();
@@ -2670,9 +2670,9 @@ CConnman::~CConnman()
Stop();
}
-std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct)
+std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
- std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct);
+ std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network);
if (m_banman) {
addresses.erase(std::remove_if(addresses.begin(), addresses.end(),
[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),
@@ -2692,7 +2692,7 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
CachedAddrResponse& cache_entry = r.first->second;
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
- cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
+ cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct, /* network */ std::nullopt);
// Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
// and the usefulness of ADDR responses to honest users.
//
@@ -2745,7 +2745,7 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
return false;
}
-size_t CConnman::GetNodeCount(ConnectionDirection flags)
+size_t CConnman::GetNodeCount(ConnectionDirection flags) const
{
LOCK(cs_vNodes);
if (flags == ConnectionDirection::Both) // Shortcut if we want total
@@ -2761,7 +2761,7 @@ size_t CConnman::GetNodeCount(ConnectionDirection flags)
return nNum;
}
-void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
+void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
{
vstats.clear();
LOCK(cs_vNodes);
@@ -2838,18 +2838,18 @@ void CConnman::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle += bytes;
}
-uint64_t CConnman::GetMaxOutboundTarget()
+uint64_t CConnman::GetMaxOutboundTarget() const
{
LOCK(cs_totalBytesSent);
return nMaxOutboundLimit;
}
-std::chrono::seconds CConnman::GetMaxOutboundTimeframe()
+std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const
{
return MAX_UPLOAD_TIMEFRAME;
}
-std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle()
+std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2863,7 +2863,7 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle()
return (cycleEndTime < now) ? 0s : cycleEndTime - now;
}
-bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)
+bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2883,7 +2883,7 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)
return false;
}
-uint64_t CConnman::GetOutboundTargetBytesLeft()
+uint64_t CConnman::GetOutboundTargetBytesLeft() const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2892,13 +2892,13 @@ uint64_t CConnman::GetOutboundTargetBytesLeft()
return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle;
}
-uint64_t CConnman::GetTotalBytesRecv()
+uint64_t CConnman::GetTotalBytesRecv() const
{
LOCK(cs_totalBytesRecv);
return nTotalBytesRecv;
}
-uint64_t CConnman::GetTotalBytesSent()
+uint64_t CConnman::GetTotalBytesSent() const
{
LOCK(cs_totalBytesSent);
return nTotalBytesSent;
@@ -2912,7 +2912,7 @@ ServiceFlags CConnman::GetLocalServices() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
CNode::CNode(NodeId idIn, 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)
- : nTimeConnected(GetSystemTimeInSeconds()),
+ : nTimeConnected(GetTimeSeconds()),
addr(addrIn),
addrBind(addrBindIn),
m_inbound_onion(inbound_onion),
@@ -2929,10 +2929,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
m_tx_relay = std::make_unique<TxRelay>();
}
- if (RelayAddrsWithConn()) {
- m_addr_known = std::make_unique<CRollingBloomFilter>(5000, 0.001);
- }
-
for (const std::string &msg : getAllNetMessageTypes())
mapRecvBytesPerMsgCmd[msg] = 0;
mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
@@ -3043,7 +3039,7 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
std::string clean_addr = addr.ToString();
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
- fs::path base_path = GetDataDir() / "message_capture" / clean_addr;
+ fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
fs::create_directories(base_path);
fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");
@@ -3052,7 +3048,7 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
ser_writedata64(f, now.count());
f.write(msg_type.data(), msg_type.length());
for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) {
- f << '\0';
+ f << uint8_t{'\0'};
}
uint32_t size = data.size();
ser_writedata32(f, size);
diff --git a/src/net.h b/src/net.h
index 13f86b6a91..b43916c55e 100644
--- a/src/net.h
+++ b/src/net.h
@@ -54,8 +54,6 @@ static const int TIMEOUT_INTERVAL = 20 * 60;
static constexpr auto FEELER_INTERVAL = 2min;
/** Run the extra block-relay-only connection loop once every 5 minutes. **/
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). */
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
/** Maximum length of the user agent string in `version` message */
@@ -402,7 +400,7 @@ public:
std::unique_ptr<TransportDeserializer> m_deserializer;
std::unique_ptr<TransportSerializer> m_serializer;
- NetPermissionFlags m_permissionFlags{PF_NONE};
+ NetPermissionFlags m_permissionFlags{NetPermissionFlags::None};
std::atomic<ServiceFlags> nServices{NODE_NONE};
SOCKET hSocket GUARDED_BY(cs_hSocket);
/** Total size of all vSendMsg entries */
@@ -447,17 +445,11 @@ public:
}
bool fClient{false}; // set by version message
bool m_limited_node{false}; //after BIP159, set by version message
- /**
- * Whether the peer has signaled support for receiving ADDRv2 (BIP155)
- * 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
std::atomic_bool fDisconnect{false};
- bool fSentAddr{false};
CSemaphoreGrant grantOutbound;
std::atomic<int> nRefCount{0};
@@ -504,15 +496,6 @@ public:
return m_conn_type == ConnectionType::INBOUND;
}
- /* Whether we send addr messages over this connection */
- bool RelayAddrsWithConn() const
- {
- // Don't relay addr messages to peers that we connect to as block-relay-only
- // peers (to prevent adversaries from inferring these links from addr
- // traffic).
- return m_conn_type != ConnectionType::BLOCK_RELAY;
- }
-
bool ExpectServicesFromConn() const {
switch (m_conn_type) {
case ConnectionType::INBOUND:
@@ -545,14 +528,6 @@ public:
// Peer selected us as (compact blocks) high-bandwidth peer (BIP152)
std::atomic<bool> m_bip152_highbandwidth_from{false};
- // flood relay
- std::vector<CAddress> vAddrToSend;
- std::unique_ptr<CRollingBloomFilter> m_addr_known{nullptr};
- bool fGetAddr{false};
- Mutex m_addr_send_times_mutex;
- std::chrono::microseconds m_next_addr_send GUARDED_BY(m_addr_send_times_mutex){0};
- std::chrono::microseconds m_next_local_addr_send GUARDED_BY(m_addr_send_times_mutex){0};
-
struct TxRelay {
mutable RecursiveMutex cs_filter;
// We use fRelayTxes for two purposes -
@@ -657,37 +632,6 @@ public:
nRefCount--;
}
- void AddAddressKnown(const CAddress& _addr)
- {
- assert(m_addr_known);
- m_addr_known->insert(_addr.GetKey());
- }
-
- /**
- * Whether the peer supports the address. For example, a peer that does not
- * implement BIP155 cannot receive Tor v3 addresses because it requires
- * ADDRv2 (BIP155) encoding.
- */
- bool IsAddrCompatible(const CAddress& addr) const
- {
- return m_wants_addrv2 || addr.IsAddrV1Compatible();
- }
-
- void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
- {
- // Known checking here is only to save space from duplicates.
- // SendMessages will filter it again for knowns that were added
- // after addresses were pushed.
- assert(m_addr_known);
- if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && IsAddrCompatible(_addr)) {
- if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
- vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
- } else {
- vAddrToSend.push_back(_addr);
- }
- }
- }
-
void AddKnownTx(const uint256& hash)
{
if (m_tx_relay != nullptr) {
@@ -900,30 +844,15 @@ public:
}
};
- template<typename Callable, typename CallableAfter>
- void ForEachNodeThen(Callable&& pre, CallableAfter&& post)
- {
- LOCK(cs_vNodes);
- for (auto&& node : vNodes) {
- if (NodeFullyConnected(node))
- pre(node);
- }
- post();
- };
-
- template<typename Callable, typename CallableAfter>
- void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const
- {
- LOCK(cs_vNodes);
- for (auto&& node : vNodes) {
- if (NodeFullyConnected(node))
- pre(node);
- }
- post();
- };
-
// Addrman functions
- std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct);
+ /**
+ * Return all or many randomly selected addresses, optionally by network.
+ *
+ * @param[in] max_addresses Maximum number of addresses to return (0 = all).
+ * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
+ * @param[in] network Select only addresses of this network (nullopt = all).
+ */
+ std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
/**
* Cache is used to minimize topology leaks, so it should
* be used for all non-trusted calls, for example, p2p.
@@ -935,7 +864,7 @@ public:
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
void SetTryNewOutboundPeer(bool flag);
- bool GetTryNewOutboundPeer();
+ bool GetTryNewOutboundPeer() const;
void StartExtraBlockRelayPeers() {
LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n");
@@ -948,13 +877,13 @@ public:
// return a value less than (num_outbound_connections - num_outbound_slots)
// in cases where some outbound connections are not yet fully connected, or
// not yet fully disconnected.
- int GetExtraFullOutboundCount();
+ int GetExtraFullOutboundCount() const;
// Count the number of block-relay-only peers we have over our limit.
- int GetExtraBlockRelayCount();
+ int GetExtraBlockRelayCount() const;
bool AddNode(const std::string& node);
bool RemoveAddedNode(const std::string& node);
- std::vector<AddedNodeInfo> GetAddedNodeInfo();
+ std::vector<AddedNodeInfo> GetAddedNodeInfo() const;
/**
* Attempts to open a connection. Currently only used from tests.
@@ -969,8 +898,8 @@ public:
*/
bool AddConnection(const std::string& address, ConnectionType conn_type);
- size_t GetNodeCount(ConnectionDirection);
- void GetNodeStats(std::vector<CNodeStats>& vstats);
+ size_t GetNodeCount(ConnectionDirection) const;
+ void GetNodeStats(std::vector<CNodeStats>& vstats) const;
bool DisconnectNode(const std::string& node);
bool DisconnectNode(const CSubNet& subnet);
bool DisconnectNode(const CNetAddr& addr);
@@ -984,24 +913,24 @@ public:
//! that peer during `net_processing.cpp:PushNodeVersion()`.
ServiceFlags GetLocalServices() const;
- uint64_t GetMaxOutboundTarget();
- std::chrono::seconds GetMaxOutboundTimeframe();
+ uint64_t GetMaxOutboundTarget() const;
+ std::chrono::seconds GetMaxOutboundTimeframe() const;
//! check if the outbound target is reached
//! if param historicalBlockServingLimit is set true, the function will
//! response true if the limit for serving historical blocks has been reached
- bool OutboundTargetReached(bool historicalBlockServingLimit);
+ bool OutboundTargetReached(bool historicalBlockServingLimit) const;
//! response the bytes left in the current max outbound cycle
//! in case of no limit, it will always response 0
- uint64_t GetOutboundTargetBytesLeft();
+ uint64_t GetOutboundTargetBytesLeft() const;
//! returns the time left in the current max outbound cycle
//! in case of no limit, it will always return 0
- std::chrono::seconds GetMaxOutboundTimeLeftInCycle();
+ std::chrono::seconds GetMaxOutboundTimeLeftInCycle() const;
- uint64_t GetTotalBytesRecv();
- uint64_t GetTotalBytesSent();
+ uint64_t GetTotalBytesRecv() const;
+ uint64_t GetTotalBytesSent() const;
/** Get a unique deterministic randomizer. */
CSipHasher GetDeterministicRandomizer(uint64_t id) const;
@@ -1106,8 +1035,8 @@ private:
static bool NodeFullyConnected(const CNode* pnode);
// Network usage totals
- RecursiveMutex cs_totalBytesRecv;
- RecursiveMutex cs_totalBytesSent;
+ mutable RecursiveMutex cs_totalBytesRecv;
+ mutable RecursiveMutex cs_totalBytesSent;
uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv) {0};
uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0};
@@ -1133,7 +1062,7 @@ private:
std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex);
RecursiveMutex m_addr_fetches_mutex;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
- RecursiveMutex cs_vAddedNodes;
+ mutable RecursiveMutex cs_vAddedNodes;
std::vector<CNode*> vNodes GUARDED_BY(cs_vNodes);
std::list<CNode*> vNodesDisconnected;
mutable RecursiveMutex cs_vNodes;
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
index 1fdcd97593..d0a45f90fa 100644
--- a/src/net_permissions.cpp
+++ b/src/net_permissions.cpp
@@ -20,15 +20,15 @@ const std::vector<std::string> NET_PERMISSIONS_DOC{
namespace {
-// The parse the following format "perm1,perm2@xxxxxx"
-bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, bilingual_str& error)
+// Parse the following format: "perm1,perm2@xxxxxx"
+bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, size_t& readen, bilingual_str& error)
{
- NetPermissionFlags flags = PF_NONE;
+ NetPermissionFlags flags = NetPermissionFlags::None;
const auto atSeparator = str.find('@');
// if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
if (atSeparator == std::string::npos) {
- NetPermissions::AddFlag(flags, PF_ISIMPLICIT);
+ NetPermissions::AddFlag(flags, NetPermissionFlags::Implicit);
readen = 0;
}
// else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
@@ -44,14 +44,14 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
readen += len; // We read "perm1"
if (commaSeparator != std::string::npos) readen++; // We read ","
- if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, PF_BLOOMFILTER);
- else if (permission == "noban") NetPermissions::AddFlag(flags, PF_NOBAN);
- else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
- else if (permission == "mempool") NetPermissions::AddFlag(flags, PF_MEMPOOL);
- else if (permission == "download") NetPermissions::AddFlag(flags, PF_DOWNLOAD);
- else if (permission == "all") NetPermissions::AddFlag(flags, PF_ALL);
- else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
- else if (permission == "addr") NetPermissions::AddFlag(flags, PF_ADDR);
+ if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
+ else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
+ else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
+ else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
+ else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
+ else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
+ else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
+ else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
else if (permission.length() == 0); // Allow empty entries
else {
error = strprintf(_("Invalid P2P permission: '%s'"), permission);
@@ -71,17 +71,17 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output,
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
{
std::vector<std::string> strings;
- if (NetPermissions::HasFlag(flags, PF_BLOOMFILTER)) strings.push_back("bloomfilter");
- if (NetPermissions::HasFlag(flags, PF_NOBAN)) strings.push_back("noban");
- if (NetPermissions::HasFlag(flags, PF_FORCERELAY)) strings.push_back("forcerelay");
- if (NetPermissions::HasFlag(flags, PF_RELAY)) strings.push_back("relay");
- if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) strings.push_back("mempool");
- if (NetPermissions::HasFlag(flags, PF_DOWNLOAD)) strings.push_back("download");
- if (NetPermissions::HasFlag(flags, PF_ADDR)) strings.push_back("addr");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.push_back("bloomfilter");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.push_back("noban");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.push_back("forcerelay");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.push_back("relay");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.push_back("mempool");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.push_back("download");
+ if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.push_back("addr");
return strings;
}
-bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error)
+bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
@@ -104,7 +104,7 @@ bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermis
return true;
}
-bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error)
+bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, bilingual_str& error)
{
NetPermissionFlags flags;
size_t offset;
diff --git a/src/net_permissions.h b/src/net_permissions.h
index bba0ea1695..c00689465e 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -5,6 +5,7 @@
#include <netaddress.h>
#include <string>
+#include <type_traits>
#include <vector>
#ifndef BITCOIN_NET_PERMISSIONS_H
@@ -14,60 +15,73 @@ struct bilingual_str;
extern const std::vector<std::string> NET_PERMISSIONS_DOC;
-enum NetPermissionFlags {
- PF_NONE = 0,
+enum class NetPermissionFlags : uint32_t {
+ None = 0,
// Can query bloomfilter even if -peerbloomfilters is false
- PF_BLOOMFILTER = (1U << 1),
+ BloomFilter = (1U << 1),
// Relay and accept transactions from this peer, even if -blocksonly is true
// This peer is also not subject to limits on how many transaction INVs are tracked
- PF_RELAY = (1U << 3),
+ Relay = (1U << 3),
// Always relay transactions from this peer, even if already in mempool
// Keep parameter interaction: forcerelay implies relay
- PF_FORCERELAY = (1U << 2) | PF_RELAY,
+ ForceRelay = (1U << 2) | Relay,
// Allow getheaders during IBD and block-download after maxuploadtarget limit
- PF_DOWNLOAD = (1U << 6),
+ Download = (1U << 6),
// Can't be banned/disconnected/discouraged for misbehavior
- PF_NOBAN = (1U << 4) | PF_DOWNLOAD,
+ NoBan = (1U << 4) | Download,
// Can query the mempool
- PF_MEMPOOL = (1U << 5),
+ Mempool = (1U << 5),
// Can request addrs without hitting a privacy-preserving cache
- PF_ADDR = (1U << 7),
+ Addr = (1U << 7),
// True if the user did not specifically set fine grained permissions
- PF_ISIMPLICIT = (1U << 31),
- PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL | PF_DOWNLOAD | PF_ADDR,
+ Implicit = (1U << 31),
+ All = BloomFilter | ForceRelay | Relay | NoBan | Mempool | Download | Addr,
};
+static inline constexpr NetPermissionFlags operator|(NetPermissionFlags a, NetPermissionFlags b)
+{
+ using t = typename std::underlying_type<NetPermissionFlags>::type;
+ return static_cast<NetPermissionFlags>(static_cast<t>(a) | static_cast<t>(b));
+}
class NetPermissions
{
public:
NetPermissionFlags m_flags;
static std::vector<std::string> ToStrings(NetPermissionFlags flags);
- static inline bool HasFlag(const NetPermissionFlags& flags, NetPermissionFlags f)
+ static inline bool HasFlag(NetPermissionFlags flags, NetPermissionFlags f)
{
- return (flags & f) == f;
+ using t = typename std::underlying_type<NetPermissionFlags>::type;
+ return (static_cast<t>(flags) & static_cast<t>(f)) == static_cast<t>(f);
}
static inline void AddFlag(NetPermissionFlags& flags, NetPermissionFlags f)
{
- flags = static_cast<NetPermissionFlags>(flags | f);
+ flags = flags | f;
}
+ //! ClearFlag is only called with `f` == NetPermissionFlags::Implicit.
+ //! If that should change in the future, be aware that ClearFlag should not
+ //! be called with a subflag of a multiflag, e.g. NetPermissionFlags::Relay
+ //! or NetPermissionFlags::Download, as that would leave `flags` in an
+ //! invalid state corresponding to none of the existing flags.
static inline void ClearFlag(NetPermissionFlags& flags, NetPermissionFlags f)
{
- flags = static_cast<NetPermissionFlags>(flags & ~f);
+ assert(f == NetPermissionFlags::Implicit);
+ using t = typename std::underlying_type<NetPermissionFlags>::type;
+ flags = static_cast<NetPermissionFlags>(static_cast<t>(flags) & ~static_cast<t>(f));
}
};
class NetWhitebindPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitebindPermissions& output, bilingual_str& error);
+ static bool TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error);
CService m_service;
};
class NetWhitelistPermissions : public NetPermissions
{
public:
- static bool TryParse(const std::string str, NetWhitelistPermissions& output, bilingual_str& error);
+ static bool TryParse(const std::string& str, NetWhitelistPermissions& output, bilingual_str& error);
CSubNet m_subnet;
};
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 4108de2c8a..65224b4259 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -16,6 +16,7 @@
#include <merkleblock.h>
#include <netbase.h>
#include <netmessagemaker.h>
+#include <node/blockstorage.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
@@ -24,6 +25,7 @@
#include <reverse_iterator.h>
#include <scheduler.h>
#include <streams.h>
+#include <sync.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <txorphanage.h>
@@ -122,11 +124,11 @@ static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h;
/** Average delay between peer address broadcasts */
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. */
+ * Blocks and peers with NetPermissionFlags::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. */
+ * Blocks and peers with NetPermissionFlags::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. */
@@ -150,6 +152,8 @@ static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
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;
+/** The maximum number of address records permitted in an ADDR message. */
+static constexpr size_t MAX_ADDR_TO_SEND{1000};
// Internal stuff
namespace {
@@ -181,7 +185,7 @@ struct Peer {
Mutex m_misbehavior_mutex;
/** Accumulated misbehavior score for this peer */
int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0};
- /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
+ /** Whether this peer should be disconnected and marked as discouraged (unless it has NetPermissionFlags::NoBan permission). */
bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false};
/** Protects block inventory data members */
@@ -210,6 +214,25 @@ struct Peer {
/** Whether a ping has been requested by the user */
std::atomic<bool> m_ping_queued{false};
+ /** A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
+ std::vector<CAddress> m_addrs_to_send;
+ /** Probabilistic filter of addresses that this peer already knows.
+ * Used to avoid relaying addresses to this peer more than once. */
+ const std::unique_ptr<CRollingBloomFilter> m_addr_known;
+ /** Whether a getaddr request to this peer is outstanding. */
+ bool m_getaddr_sent{false};
+ /** Guards address sending timers. */
+ mutable Mutex m_addr_send_times_mutex;
+ /** Time point to send the next ADDR message to this peer. */
+ std::chrono::microseconds m_next_addr_send GUARDED_BY(m_addr_send_times_mutex){0};
+ /** Time point to possibly re-announce our local address to this peer. */
+ std::chrono::microseconds m_next_local_addr_send GUARDED_BY(m_addr_send_times_mutex){0};
+ /** Whether the peer has signaled support for receiving ADDRv2 (BIP155)
+ * messages, indicating a preference to receive ADDRv2 instead of ADDR ones. */
+ std::atomic_bool m_wants_addrv2{false};
+ /** Whether this peer has already sent us a getaddr message. */
+ bool m_getaddr_recvd{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);
@@ -218,7 +241,10 @@ struct Peer {
/** Work queue of items requested by this peer **/
std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
- explicit Peer(NodeId id) : m_id(id) {}
+ explicit Peer(NodeId id, bool addr_relay)
+ : m_id(id)
+ , m_addr_known{addr_relay ? std::make_unique<CRollingBloomFilter>(5000, 0.001) : nullptr}
+ {}
};
using PeerRef = std::shared_ptr<Peer>;
@@ -245,7 +271,7 @@ public:
/** Implement PeerManager */
void CheckForStaleTipAndEvictPeers() override;
- bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override;
+ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
void SendPings() override;
void RelayTransaction(const uint256& txid, const uint256& wtxid) override;
@@ -255,6 +281,9 @@ public:
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override;
private:
+ void _RelayTransaction(const uint256& txid, const uint256& wtxid)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -324,7 +353,19 @@ private:
void MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now);
/** Send `addr` messages on a regular schedule. */
- void MaybeSendAddr(CNode& node, std::chrono::microseconds current_time);
+ void MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time);
+
+ /** Relay (gossip) an address to a few randomly chosen nodes.
+ *
+ * @param[in] originator The id of the peer that sent us the address. We don't want to relay it back.
+ * @param[in] addr Address to relay.
+ * @param[in] fReachable Whether the address' network is reachable. We relay unreachable
+ * addresses less.
+ */
+ void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable);
+
+ /** Send `feefilter` message. */
+ void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
const CChainParams& m_chainparams;
CConnman& m_connman;
@@ -448,7 +489,10 @@ private:
/** 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);
+ void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex) LOCKS_EXCLUDED(::cs_main);
+
+ /** Process a new block. Perform any post-processing housekeeping */
+ void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing);
/** Relay map (txid or wtxid -> CTransactionRef) */
typedef std::map<uint256, CTransactionRef> MapRelay;
@@ -668,12 +712,48 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
+static bool RelayAddrsWithPeer(const Peer& peer)
+{
+ return peer.m_addr_known != nullptr;
+}
+
+/**
+ * Whether the peer supports the address. For example, a peer that does not
+ * implement BIP155 cannot receive Tor v3 addresses because it requires
+ * ADDRv2 (BIP155) encoding.
+ */
+static bool IsAddrCompatible(const Peer& peer, const CAddress& addr)
+{
+ return peer.m_wants_addrv2 || addr.IsAddrV1Compatible();
+}
+
+static void AddAddressKnown(Peer& peer, const CAddress& addr)
+{
+ assert(peer.m_addr_known);
+ peer.m_addr_known->insert(addr.GetKey());
+}
+
+static void PushAddress(Peer& peer, const CAddress& addr, FastRandomContext& insecure_rand)
+{
+ // Known checking here is only to save space from duplicates.
+ // Before sending, we'll filter it again for known addresses that were
+ // added after addresses were pushed.
+ assert(peer.m_addr_known);
+ if (addr.IsValid() && !peer.m_addr_known->contains(addr.GetKey()) && IsAddrCompatible(peer, addr)) {
+ if (peer.m_addrs_to_send.size() >= MAX_ADDR_TO_SEND) {
+ peer.m_addrs_to_send[insecure_rand.randrange(peer.m_addrs_to_send.size())] = addr;
+ } else {
+ peer.m_addrs_to_send.push_back(addr);
+ }
+ }
+}
+
static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node.IsInboundConn() || node.HasPermission(PF_NOBAN)) && !node.IsAddrFetchConn() && !node.fClient;
+ state->fPreferredDownload = (!node.IsInboundConn() || node.HasPermission(NetPermissionFlags::NoBan)) && !node.IsAddrFetchConn() && !node.fClient;
nPreferredDownload += state->fPreferredDownload;
}
@@ -953,24 +1033,24 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
{
AssertLockHeld(::cs_main); // For m_txrequest
NodeId nodeid = node.GetId();
- if (!node.HasPermission(PF_RELAY) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
+ if (!node.HasPermission(NetPermissionFlags::Relay) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
// Too many queued announcements from this peer
return;
}
const CNodeState* state = State(nodeid);
// Decide the TxRequestTracker parameters for this announcement:
- // - "preferred": if fPreferredDownload is set (= outbound, or PF_NOBAN permission)
+ // - "preferred": if fPreferredDownload is set (= outbound, or NetPermissionFlags::NoBan permission)
// - "reqtime": current time plus delays for:
// - NONPREF_PEER_TX_DELAY for announcements from non-preferred connections
// - TXID_RELAY_DELAY for txid announcements while wtxid peers are available
// - OVERLOADED_PEER_TX_DELAY for announcements from peers which have at least
- // MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have PF_RELAY).
+ // MAX_PEER_TX_REQUEST_IN_FLIGHT requests in flight (and don't have NetPermissionFlags::Relay).
auto delay = std::chrono::microseconds{0};
const bool preferred = state->fPreferredDownload;
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
- const bool overloaded = !node.HasPermission(PF_RELAY) &&
+ const bool overloaded = !node.HasPermission(NetPermissionFlags::Relay) &&
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
m_txrequest.ReceivedInv(nodeid, gtxid, preferred, current_time + delay);
@@ -994,7 +1074,9 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
assert(m_txrequest.Count(nodeid) == 0);
}
{
- PeerRef peer = std::make_shared<Peer>(nodeid);
+ // Addr relay is disabled for outbound block-relay-only peers to
+ // prevent adversaries from inferring these links from addr traffic.
+ PeerRef peer = std::make_shared<Peer>(nodeid, /* addr_relay = */ !pnode->IsBlockOnlyConn());
LOCK(m_peer_mutex);
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, std::move(peer));
}
@@ -1012,7 +1094,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
if (tx != nullptr) {
LOCK(cs_main);
- RelayTransaction(txid, tx->GetWitnessHash());
+ _RelayTransaction(txid, tx->GetWitnessHash());
} else {
m_mempool.RemoveUnbroadcastTx(txid, true);
}
@@ -1100,7 +1182,7 @@ PeerRef PeerManagerImpl::RemovePeer(NodeId id)
return ret;
}
-bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats)
+bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const
{
{
LOCK(cs_main);
@@ -1509,6 +1591,11 @@ void PeerManagerImpl::SendPings()
void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid)
{
+ WITH_LOCK(cs_main, _RelayTransaction(txid, wtxid););
+}
+
+void PeerManagerImpl::_RelayTransaction(const uint256& txid, const uint256& wtxid)
+{
m_connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
@@ -1522,59 +1609,49 @@ void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid
});
}
-/**
- * Relay (gossip) an address to a few randomly chosen nodes.
- * We choose the same nodes within a given 24h window (if the list of connected
- * nodes does not change) and we don't relay to nodes that already know an
- * address. So within 24h we will likely relay a given address once. This is to
- * prevent a peer from unjustly giving their address better propagation by sending
- * it to us repeatedly.
- * @param[in] originator The peer that sent us the address. We don't want to relay it back.
- * @param[in] addr Address to relay.
- * @param[in] fReachable Whether the address' network is reachable. We relay unreachable
- * addresses less.
- * @param[in] connman Connection manager to choose nodes to relay to.
- */
-static void RelayAddress(const CNode& originator,
- const CAddress& addr,
- bool fReachable,
- const CConnman& connman)
+void PeerManagerImpl::RelayAddress(NodeId originator,
+ const CAddress& addr,
+ bool fReachable)
{
+ // We choose the same nodes within a given 24h window (if the list of connected
+ // nodes does not change) and we don't relay to nodes that already know an
+ // address. So within 24h we will likely relay a given address once. This is to
+ // prevent a peer from unjustly giving their address better propagation by sending
+ // it to us repeatedly.
+
if (!fReachable && !addr.IsRelayable()) return;
// Relay to a limited number of other nodes
// Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the m_addr_knowns of the chosen nodes prevent repeats
uint64_t hashAddr = addr.GetHash();
- const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24 * 60 * 60));
+ const CSipHasher hasher = m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24 * 60 * 60));
FastRandomContext insecure_rand;
// Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers.
unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1;
- std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}};
+ std::array<std::pair<uint64_t, Peer*>, 2> best{{{0, nullptr}, {0, nullptr}}};
assert(nRelayNodes <= best.size());
- auto sortfunc = [&best, &hasher, nRelayNodes, &originator, &addr](CNode* pnode) {
- if (pnode->RelayAddrsWithConn() && pnode != &originator && pnode->IsAddrCompatible(addr)) {
- uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
+ LOCK(m_peer_mutex);
+
+ for (auto& [id, peer] : m_peer_map) {
+ if (RelayAddrsWithPeer(*peer) && id != originator && IsAddrCompatible(*peer, addr)) {
+ uint64_t hashKey = CSipHasher(hasher).Write(id).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1);
- best[i] = std::make_pair(hashKey, pnode);
+ best[i] = std::make_pair(hashKey, peer.get());
break;
}
}
}
};
- auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] {
- for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
- best[i].second->PushAddress(addr, insecure_rand);
- }
- };
-
- connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+ for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {
+ PushAddress(*best[i].second, addr, insecure_rand);
+ }
}
void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv)
@@ -1625,14 +1702,14 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
// disconnect node in case we have reached the outbound limit for serving historical blocks
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
+ !pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (!pfrom.HasPermission(PF_NOBAN) && (
+ if (!pfrom.HasPermission(NetPermissionFlags::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, disconnect peer=%d\n", pfrom.GetId());
@@ -2062,7 +2139,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
/**
* Reconsider orphan transactions after a parent has been accepted to the mempool.
*
- * @param[in/out] orphan_work_set The set of orphan transactions to reconsider. Generally only one
+ * @param[in,out] orphan_work_set The set of orphan transactions to reconsider. Generally only one
* orphan will be reconsidered on each call of this function. This set
* may be added to if accepting an orphan causes its children to be
* reconsidered.
@@ -2084,7 +2161,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
+ _RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set);
m_orphanage.EraseTx(orphanHash);
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
@@ -2308,6 +2385,18 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv)
m_connman.PushMessage(&peer, std::move(msg));
}
+void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing)
+{
+ bool new_block{false};
+ m_chainman.ProcessNewBlock(m_chainparams, block, force_processing, &new_block);
+ if (new_block) {
+ node.nLastBlockTime = GetTime();
+ } else {
+ LOCK(cs_main);
+ mapBlockSource.erase(block->GetHash());
+ }
+}
+
void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received,
const std::atomic<bool>& interruptMsgProc)
@@ -2457,17 +2546,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (addr.IsRoutable())
{
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom.PushAddress(addr, insecure_rand);
+ PushAddress(*peer, addr, insecure_rand);
} else if (IsPeerAddrLocalGood(&pfrom)) {
addr.SetIP(addrMe);
LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString());
- pfrom.PushAddress(addr, insecure_rand);
+ PushAddress(*peer, addr, insecure_rand);
}
}
// Get recent addresses
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
- pfrom.fGetAddr = true;
+ peer->m_getaddr_sent = true;
}
if (!pfrom.IsInboundConn()) {
@@ -2626,7 +2715,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
pfrom.fDisconnect = true;
return;
}
- pfrom.m_wants_addrv2 = true;
+ peer->m_wants_addrv2 = true;
return;
}
@@ -2648,7 +2737,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
s >> vAddr;
- if (!pfrom.RelayAddrsWithConn()) {
+ if (!RelayAddrsWithPeer(*peer)) {
LogPrint(BCLog::NET, "ignoring %s message from %s peer=%d\n", msg_type, pfrom.ConnectionTypeAsString(), pfrom.GetId());
return;
}
@@ -2675,24 +2764,22 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
- pfrom.AddAddressKnown(addr);
+ AddAddressKnown(*peer, addr);
if (m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr))) {
// Do not process banned/discouraged addresses beyond remembering we received them
continue;
}
bool fReachable = IsReachable(addr);
- if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
- {
+ if (addr.nTime > nSince && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) {
// Relay to a limited number of other nodes
- RelayAddress(pfrom, addr, fReachable, m_connman);
+ RelayAddress(pfrom.GetId(), addr, fReachable);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
m_addrman.Add(vAddrOk, pfrom.addr, 2 * 60 * 60);
- if (vAddr.size() < 1000)
- pfrom.fGetAddr = false;
+ if (vAddr.size() < 1000) peer->m_getaddr_sent = false;
if (pfrom.IsAddrFetchConn()) {
LogPrint(BCLog::NET, "addrfetch connection completed peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
@@ -2714,7 +2801,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool fBlocksOnly = m_ignore_incoming_txs || (pfrom.m_tx_relay == nullptr);
// Allow peers with relay permission to send data other than blocks in blocks only mode
- if (pfrom.HasPermission(PF_RELAY)) {
+ if (pfrom.HasPermission(NetPermissionFlags::Relay)) {
fBlocksOnly = false;
}
@@ -2928,7 +3015,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
LOCK(cs_main);
- if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(PF_DOWNLOAD)) {
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && !pfrom.HasPermission(NetPermissionFlags::Download)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom.GetId());
return;
}
@@ -2987,7 +3074,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Stop processing the transaction early if
// 1) We are in blocks only mode and peer has no relay permission
// 2) This peer is a block-relay-only peer
- if ((m_ignore_incoming_txs && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr))
+ if ((m_ignore_incoming_txs && !pfrom.HasPermission(NetPermissionFlags::Relay)) || (pfrom.m_tx_relay == nullptr))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
@@ -3032,7 +3119,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid))) {
- if (pfrom.HasPermission(PF_FORCERELAY)) {
+ if (pfrom.HasPermission(NetPermissionFlags::ForceRelay)) {
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
// the node to function as a gateway for nodes hidden behind it.
@@ -3040,7 +3127,7 @@ 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());
+ _RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
}
}
return;
@@ -3055,7 +3142,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// requests for it.
m_txrequest.ForgetTxHash(tx.GetHash());
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
+ _RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
pfrom.nLastTXTime = GetTime();
@@ -3389,8 +3476,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
}
- bool fNewBlock = false;
- // Setting fForceProcessing to true means that we bypass some of
+ // Setting force_processing to true means that we bypass some of
// our anti-DoS protections in AcceptBlock, which filters
// unrequested blocks that might be trying to waste our resources
// (eg disk space). Because we only try to reconstruct blocks when
@@ -3399,13 +3485,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
- if (fNewBlock) {
- pfrom.nLastBlockTime = GetTime();
- } else {
- LOCK(cs_main);
- mapBlockSource.erase(pblock->GetHash());
- }
+ ProcessBlock(pfrom, pblock, /*force_processing=*/true);
LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
// Clear download state for this block, which is in
@@ -3482,20 +3562,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
} // Don't hold cs_main when we call into ProcessNewBlock
if (fBlockRead) {
- bool fNewBlock = false;
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
// This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
- if (fNewBlock) {
- pfrom.nLastBlockTime = GetTime();
- } else {
- LOCK(cs_main);
- mapBlockSource.erase(pblock->GetHash());
- }
+ ProcessBlock(pfrom, pblock, /*force_processing=*/true);
}
return;
}
@@ -3550,14 +3623,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// cs_main in ProcessNewBlock is fine.
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
- bool fNewBlock = false;
- m_chainman.ProcessNewBlock(m_chainparams, pblock, forceProcessing, &fNewBlock);
- if (fNewBlock) {
- pfrom.nLastBlockTime = GetTime();
- } else {
- LOCK(cs_main);
- mapBlockSource.erase(pblock->GetHash());
- }
+ ProcessBlock(pfrom, pblock, forceProcessing);
return;
}
@@ -3573,31 +3639,31 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
// Only send one GetAddr response per connection to reduce resource waste
- // and discourage addr stamping of INV announcements.
- if (pfrom.fSentAddr) {
+ // and discourage addr stamping of INV announcements.
+ if (peer->m_getaddr_recvd) {
LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom.GetId());
return;
}
- pfrom.fSentAddr = true;
+ peer->m_getaddr_recvd = true;
- pfrom.vAddrToSend.clear();
+ peer->m_addrs_to_send.clear();
std::vector<CAddress> vAddr;
- if (pfrom.HasPermission(PF_ADDR)) {
- vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
+ if (pfrom.HasPermission(NetPermissionFlags::Addr)) {
+ vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND, /* network */ std::nullopt);
} else {
vAddr = m_connman.GetAddresses(pfrom, MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
}
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- pfrom.PushAddress(addr, insecure_rand);
+ PushAddress(*peer, addr, insecure_rand);
}
return;
}
if (msg_type == NetMsgType::MEMPOOL) {
- if (!(pfrom.GetLocalServices() & NODE_BLOOM) && !pfrom.HasPermission(PF_MEMPOOL))
+ if (!(pfrom.GetLocalServices() & NODE_BLOOM) && !pfrom.HasPermission(NetPermissionFlags::Mempool))
{
- if (!pfrom.HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(NetPermissionFlags::NoBan))
{
LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
@@ -3605,9 +3671,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- if (m_connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
+ if (m_connman.OutboundTargetReached(false) && !pfrom.HasPermission(NetPermissionFlags::Mempool))
{
- if (!pfrom.HasPermission(PF_NOBAN))
+ if (!pfrom.HasPermission(NetPermissionFlags::NoBan))
{
LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom.GetId());
pfrom.fDisconnect = true;
@@ -3822,8 +3888,8 @@ bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
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
+ if (pnode.HasPermission(NetPermissionFlags::NoBan)) {
+ // We never disconnect or discourage peers for bad behavior if they have NetPermissionFlags::NoBan permission
LogPrintf("Warning: not punishing noban peer %d!\n", peer.m_id);
return false;
}
@@ -4147,72 +4213,113 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic
}
}
-void PeerManagerImpl::MaybeSendAddr(CNode& node, std::chrono::microseconds current_time)
+void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time)
{
// Nothing to do for non-address-relay peers
- if (!node.RelayAddrsWithConn()) return;
+ if (!RelayAddrsWithPeer(peer)) return;
- assert(node.m_addr_known);
-
- LOCK(node.m_addr_send_times_mutex);
+ LOCK(peer.m_addr_send_times_mutex);
// Periodically advertise our local address to the peer.
if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload() &&
- node.m_next_local_addr_send < current_time) {
+ peer.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
// over since our last self-announcement, but there is only a small
// bandwidth cost that we can incur by doing this (which happens
// once a day on average).
- if (node.m_next_local_addr_send != 0us) {
- node.m_addr_known->reset();
+ if (peer.m_next_local_addr_send != 0us) {
+ peer.m_addr_known->reset();
}
if (std::optional<CAddress> local_addr = GetLocalAddrForPeer(&node)) {
FastRandomContext insecure_rand;
- node.PushAddress(*local_addr, insecure_rand);
+ PushAddress(peer, *local_addr, insecure_rand);
}
- node.m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
+ peer.m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
// We sent an `addr` message to this peer recently. Nothing more to do.
- if (current_time <= node.m_next_addr_send) return;
+ if (current_time <= peer.m_next_addr_send) return;
- node.m_next_addr_send = PoissonNextSend(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
+ peer.m_next_addr_send = PoissonNextSend(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
- if (!Assume(node.vAddrToSend.size() <= MAX_ADDR_TO_SEND)) {
+ if (!Assume(peer.m_addrs_to_send.size() <= MAX_ADDR_TO_SEND)) {
// Should be impossible since we always check size before adding to
- // vAddrToSend. Recover by trimming the vector.
- node.vAddrToSend.resize(MAX_ADDR_TO_SEND);
+ // m_addrs_to_send. Recover by trimming the vector.
+ peer.m_addrs_to_send.resize(MAX_ADDR_TO_SEND);
}
// Remove addr records that the peer already knows about, and add new
// addrs to the m_addr_known filter on the same pass.
- auto addr_already_known = [&node](const CAddress& addr) {
- bool ret = node.m_addr_known->contains(addr.GetKey());
- if (!ret) node.m_addr_known->insert(addr.GetKey());
+ auto addr_already_known = [&peer](const CAddress& addr) {
+ bool ret = peer.m_addr_known->contains(addr.GetKey());
+ if (!ret) peer.m_addr_known->insert(addr.GetKey());
return ret;
};
- node.vAddrToSend.erase(std::remove_if(node.vAddrToSend.begin(), node.vAddrToSend.end(), addr_already_known),
- node.vAddrToSend.end());
+ peer.m_addrs_to_send.erase(std::remove_if(peer.m_addrs_to_send.begin(), peer.m_addrs_to_send.end(), addr_already_known),
+ peer.m_addrs_to_send.end());
// No addr messages to send
- if (node.vAddrToSend.empty()) return;
+ if (peer.m_addrs_to_send.empty()) return;
const char* msg_type;
int make_flags;
- if (node.m_wants_addrv2) {
+ if (peer.m_wants_addrv2) {
msg_type = NetMsgType::ADDRV2;
make_flags = ADDRV2_FORMAT;
} else {
msg_type = NetMsgType::ADDR;
make_flags = 0;
}
- m_connman.PushMessage(&node, CNetMsgMaker(node.GetCommonVersion()).Make(make_flags, msg_type, node.vAddrToSend));
- node.vAddrToSend.clear();
+ m_connman.PushMessage(&node, CNetMsgMaker(node.GetCommonVersion()).Make(make_flags, msg_type, peer.m_addrs_to_send));
+ peer.m_addrs_to_send.clear();
// we only send the big addr message once
- if (node.vAddrToSend.capacity() > 40) {
- node.vAddrToSend.shrink_to_fit();
+ if (peer.m_addrs_to_send.capacity() > 40) {
+ peer.m_addrs_to_send.shrink_to_fit();
+ }
+}
+
+void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds current_time)
+{
+ AssertLockHeld(cs_main);
+
+ if (m_ignore_incoming_txs) return;
+ if (!pto.m_tx_relay) return;
+ if (pto.GetCommonVersion() < FEEFILTER_VERSION) return;
+ // peers with the forcerelay permission should not filter txs to us
+ if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return;
+
+ CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
+
+ if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ // Received tx-inv messages are discarded when the active
+ // chainstate is in IBD, so tell the peer to not send them.
+ currentFilter = MAX_MONEY;
+ } else {
+ static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
+ 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->m_next_send_feefilter = 0us;
+ }
+ }
+ 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());
+ if (filterToSend != pto.m_tx_relay->lastSentFeeFilter) {
+ m_connman.PushMessage(&pto, CNetMsgMaker(pto.GetCommonVersion()).Make(NetMsgType::FEEFILTER, filterToSend));
+ pto.m_tx_relay->lastSentFeeFilter = filterToSend;
+ }
+ 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 (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->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
}
}
@@ -4261,7 +4368,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// MaybeSendPing may have marked peer for disconnection
if (pto->fDisconnect) return true;
- MaybeSendAddr(*pto, current_time);
+ MaybeSendAddr(*pto, *peer, current_time);
{
LOCK(cs_main);
@@ -4455,11 +4562,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
}
}
peer->m_blocks_for_inv_relay.clear();
+ }
- if (pto->m_tx_relay != nullptr) {
+ if (pto->m_tx_relay != nullptr) {
LOCK(pto->m_tx_relay->cs_tx_inventory);
// Check whether periodic sends should happen
- bool fSendTrickle = pto->HasPermission(PF_NOBAN);
+ bool fSendTrickle = pto->HasPermission(NetPermissionFlags::NoBan);
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->IsInboundConn()) {
@@ -4584,7 +4692,6 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
}
}
}
- }
}
if (!vInv.empty())
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
@@ -4617,12 +4724,12 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// Detect whether this is a stalling initial-headers-sync peer
if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
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,
+ // Disconnect a peer (without NetPermissionFlags::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
// disconnect our sync peer for stalling; we have bigger
// problems if we can't get any outbound peers.
- if (!pto->HasPermission(PF_NOBAN)) {
+ if (!pto->HasPermission(NetPermissionFlags::NoBan)) {
LogPrintf("Timeout downloading headers from peer=%d, disconnecting\n", pto->GetId());
pto->fDisconnect = true;
return true;
@@ -4702,46 +4809,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (!vGetData.empty())
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- //
- // Message: feefilter
- //
- if (pto->m_tx_relay != nullptr &&
- !m_ignore_incoming_txs &&
- pto->GetCommonVersion() >= FEEFILTER_VERSION &&
- gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !pto->HasPermission(PF_FORCERELAY) // peers with the forcerelay permission should not filter txs to us
- ) {
- CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
- static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
- if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
- // Received tx-inv messages are discarded when the active
- // chainstate is in IBD, so tell the peer to not send them.
- currentFilter = MAX_MONEY;
- } else {
- static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
- 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->m_next_send_feefilter = 0us;
- }
- }
- 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());
- if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
- pto->m_tx_relay->lastSentFeeFilter = filterToSend;
- }
- 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 (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->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
- }
- }
+ MaybeSendFeefilter(*pto, current_time);
} // release cs_main
return true;
}
diff --git a/src/net_processing.h b/src/net_processing.h
index 4556d32377..d5801aadd3 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -7,7 +7,6 @@
#define BITCOIN_NET_PROCESSING_H
#include <net.h>
-#include <sync.h>
#include <validationinterface.h>
class CAddrMan;
@@ -15,8 +14,6 @@ class CChainParams;
class CTxMemPool;
class ChainstateManager;
-extern RecursiveMutex cs_main;
-
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
@@ -43,14 +40,13 @@ public:
virtual ~PeerManager() { }
/** Get statistics from node state */
- virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) = 0;
+ virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0;
/** 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;
+ virtual void RelayTransaction(const uint256& txid, const uint256& wtxid) = 0;
/** Send ping message to all peers */
virtual void SendPings() = 0;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 69edc15c66..1ea3969978 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -32,14 +32,7 @@ CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
case NET_IPV6:
return BIP155Network::IPV6;
case NET_ONION:
- switch (m_addr.size()) {
- case ADDR_TORV2_SIZE:
- return BIP155Network::TORV2;
- case ADDR_TORV3_SIZE:
- return BIP155Network::TORV3;
- default:
- assert(false);
- }
+ return BIP155Network::TORV3;
case NET_I2P:
return BIP155Network::I2P;
case NET_CJDNS:
@@ -72,14 +65,6 @@ bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t addre
throw std::ios_base::failure(
strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size,
ADDR_IPV6_SIZE));
- case BIP155Network::TORV2:
- if (address_size == ADDR_TORV2_SIZE) {
- m_net = NET_ONION;
- return true;
- }
- throw std::ios_base::failure(
- strprintf("BIP155 TORv2 address with length %u (should be %u)", address_size,
- ADDR_TORV2_SIZE));
case BIP155Network::TORV3:
if (address_size == ADDR_TORV3_SIZE) {
m_net = NET_ONION;
@@ -130,7 +115,7 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE);
break;
case NET_ONION:
- assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE || ipIn.m_addr.size() == ADDR_TORV3_SIZE);
+ assert(ipIn.m_addr.size() == ADDR_TORV3_SIZE);
break;
case NET_I2P:
assert(ipIn.m_addr.size() == ADDR_I2P_SIZE);
@@ -161,9 +146,12 @@ void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
m_net = NET_IPV4;
skip = sizeof(IPV4_IN_IPV6_PREFIX);
} else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) {
- // TORv2-in-IPv6
- m_net = NET_ONION;
- skip = sizeof(TORV2_IN_IPV6_PREFIX);
+ // TORv2-in-IPv6 (unsupported). Unserialize as !IsValid(), thus ignoring them.
+ // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
+ // will not be gossiped, but continue reading next addresses from the stream.
+ m_net = NET_IPV6;
+ m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ return;
} else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) {
// Internal-in-IPv6
m_net = NET_INTERNAL;
@@ -254,12 +242,7 @@ bool CNetAddr::SetTor(const std::string& addr)
return false;
}
- switch (input.size()) {
- case ADDR_TORV2_SIZE:
- m_net = NET_ONION;
- m_addr.assign(input.begin(), input.end());
- return true;
- case torv3::TOTAL_LEN: {
+ if (input.size() == torv3::TOTAL_LEN) {
Span<const uint8_t> input_pubkey{input.data(), ADDR_TORV3_SIZE};
Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};
Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};
@@ -279,7 +262,6 @@ bool CNetAddr::SetTor(const std::string& addr)
m_addr.assign(input_pubkey.begin(), input_pubkey.end());
return true;
}
- }
return false;
}
@@ -528,7 +510,6 @@ bool CNetAddr::IsAddrV1Compatible() const
case NET_INTERNAL:
return true;
case NET_ONION:
- return m_addr.size() == ADDR_TORV2_SIZE;
case NET_I2P:
case NET_CJDNS:
return false;
@@ -551,64 +532,92 @@ enum Network CNetAddr::GetNetwork() const
return m_net;
}
-static std::string IPv6ToString(Span<const uint8_t> a)
+static std::string IPv4ToString(Span<const uint8_t> a)
{
- assert(a.size() == ADDR_IPV6_SIZE);
- // clang-format off
- return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- ReadBE16(&a[0]),
- ReadBE16(&a[2]),
- ReadBE16(&a[4]),
- ReadBE16(&a[6]),
- ReadBE16(&a[8]),
- ReadBE16(&a[10]),
- ReadBE16(&a[12]),
- ReadBE16(&a[14]));
- // clang-format on
+ return strprintf("%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
}
-std::string CNetAddr::ToStringIP() const
+// Return an IPv6 address text representation with zero compression as described in RFC 5952
+// ("A Recommendation for IPv6 Address Text Representation").
+static std::string IPv6ToString(Span<const uint8_t> a, uint32_t scope_id)
{
- switch (m_net) {
- case NET_IPV4:
- case NET_IPV6: {
- CService serv(*this, 0);
- struct sockaddr_storage sockaddr;
- socklen_t socklen = sizeof(sockaddr);
- if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
- char name[1025] = "";
- if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name,
- sizeof(name), nullptr, 0, NI_NUMERICHOST))
- return std::string(name);
+ assert(a.size() == ADDR_IPV6_SIZE);
+ const std::array groups{
+ ReadBE16(&a[0]),
+ ReadBE16(&a[2]),
+ ReadBE16(&a[4]),
+ ReadBE16(&a[6]),
+ ReadBE16(&a[8]),
+ ReadBE16(&a[10]),
+ ReadBE16(&a[12]),
+ ReadBE16(&a[14]),
+ };
+
+ // The zero compression implementation is inspired by Rust's std::net::Ipv6Addr, see
+ // https://github.com/rust-lang/rust/blob/cc4103089f40a163f6d143f06359cba7043da29b/library/std/src/net/ip.rs#L1635-L1683
+ struct ZeroSpan {
+ size_t start_index{0};
+ size_t len{0};
+ };
+
+ // Find longest sequence of consecutive all-zero fields. Use first zero sequence if two or more
+ // zero sequences of equal length are found.
+ ZeroSpan longest, current;
+ for (size_t i{0}; i < groups.size(); ++i) {
+ if (groups[i] != 0) {
+ current = {i + 1, 0};
+ continue;
}
- if (m_net == NET_IPV4) {
- return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]);
+ current.len += 1;
+ if (current.len > longest.len) {
+ longest = current;
}
- return IPv6ToString(m_addr);
}
- case NET_ONION:
- switch (m_addr.size()) {
- case ADDR_TORV2_SIZE:
- return EncodeBase32(m_addr) + ".onion";
- case ADDR_TORV3_SIZE: {
- uint8_t checksum[torv3::CHECKSUM_LEN];
- torv3::Checksum(m_addr, checksum);
+ std::string r;
+ r.reserve(39);
+ for (size_t i{0}; i < groups.size(); ++i) {
+ // Replace the longest sequence of consecutive all-zero fields with two colons ("::").
+ if (longest.len >= 2 && i >= longest.start_index && i < longest.start_index + longest.len) {
+ if (i == longest.start_index) {
+ r += "::";
+ }
+ continue;
+ }
+ r += strprintf("%s%x", ((!r.empty() && r.back() != ':') ? ":" : ""), groups[i]);
+ }
+
+ if (scope_id != 0) {
+ r += strprintf("%%%u", scope_id);
+ }
- // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
- prevector<torv3::TOTAL_LEN, uint8_t> address{m_addr.begin(), m_addr.end()};
- address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);
- address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));
+ return r;
+}
- return EncodeBase32(address) + ".onion";
- }
- default:
- assert(false);
- }
+static std::string OnionToString(Span<const uint8_t> addr)
+{
+ uint8_t checksum[torv3::CHECKSUM_LEN];
+ torv3::Checksum(addr, checksum);
+ // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
+ prevector<torv3::TOTAL_LEN, uint8_t> address{addr.begin(), addr.end()};
+ address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);
+ address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));
+ return EncodeBase32(address) + ".onion";
+}
+
+std::string CNetAddr::ToStringIP() const
+{
+ switch (m_net) {
+ case NET_IPV4:
+ return IPv4ToString(m_addr);
+ case NET_IPV6:
+ return IPv6ToString(m_addr, m_scope_id);
+ case NET_ONION:
+ return OnionToString(m_addr);
case NET_I2P:
return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p";
case NET_CJDNS:
- return IPv6ToString(m_addr);
+ return IPv6ToString(m_addr, 0);
case NET_INTERNAL:
return EncodeBase32(m_addr) + ".internal";
case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
diff --git a/src/netaddress.h b/src/netaddress.h
index 897ce46cda..0d04ab88fb 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -97,9 +97,6 @@ static constexpr size_t ADDR_IPV4_SIZE = 4;
/// Size of IPv6 address (in bytes).
static constexpr size_t ADDR_IPV6_SIZE = 16;
-/// Size of TORv2 address (in bytes).
-static constexpr size_t ADDR_TORV2_SIZE = 10;
-
/// Size of TORv3 address (in bytes). This is the length of just the address
/// as used in BIP155, without the checksum and the version byte.
static constexpr size_t ADDR_TORV3_SIZE = 32;
@@ -260,8 +257,7 @@ class CNetAddr
/**
* 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.
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.
* @returns Whether the operation was successful.
* @see CNetAddr::IsTor()
*/
@@ -303,7 +299,7 @@ class CNetAddr
/**
* Get the BIP155 network id of this address.
* Must not be called for IsInternal() objects.
- * @returns BIP155 network id
+ * @returns BIP155 network id, except TORV2 which is no longer supported.
*/
BIP155Network GetBIP155Network() const;
@@ -334,23 +330,14 @@ class CNetAddr
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
- case NET_ONION:
- if (m_addr.size() == ADDR_TORV3_SIZE) {
- break;
- }
- prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
- assert(prefix_size + m_addr.size() == sizeof(arr));
- memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
- memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
- return;
case NET_INTERNAL:
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
+ case NET_ONION:
case NET_I2P:
- break;
case NET_CJDNS:
break;
case NET_UNROUTABLE:
@@ -358,7 +345,7 @@ class CNetAddr
assert(false);
} // no default case, so the compiler can warn about missing cases
- // Serialize TORv3, I2P and CJDNS as all-zeros.
+ // Serialize ONION, I2P and CJDNS as all-zeros.
memset(arr, 0x0, V1_SERIALIZATION_SIZE);
}
diff --git a/src/netbase.h b/src/netbase.h
index 1f35c29dcb..6a87c338a0 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -172,7 +172,6 @@ CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLoo
* @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.
*/
@@ -235,7 +234,7 @@ void InterruptSocks5(bool interrupt);
* @param port The destination port.
* @param auth The credentials with which to authenticate with the specified
* SOCKS5 proxy.
- * @param sock The SOCKS5 proxy socket.
+ * @param socket The SOCKS5 proxy socket.
*
* @returns Whether or not the operation succeeded.
*
diff --git a/src/node/README.md b/src/node/README.md
index e99a717534..ab5979594d 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -15,8 +15,7 @@ As a rule of thumb, code in one of the [`src/node/`](./),
calling code in the other directories directly, and only invoke it indirectly
through the more limited [`src/interfaces/`](../interfaces/) classes.
-The [`src/node/`](./) directory is a new directory introduced in
-[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is
+This directory is at the moment
sparsely populated. Eventually more substantial files like
[`src/validation.cpp`](../validation.cpp) and
[`src/txmempool.cpp`](../txmempool.cpp) might be moved there.
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
new file mode 100644
index 0000000000..6c66c565ad
--- /dev/null
+++ b/src/node/blockstorage.cpp
@@ -0,0 +1,566 @@
+// Copyright (c) 2011-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 <node/blockstorage.h>
+
+#include <chain.h>
+#include <chainparams.h>
+#include <clientversion.h>
+#include <consensus/validation.h>
+#include <flatfile.h>
+#include <fs.h>
+#include <hash.h>
+#include <pow.h>
+#include <shutdown.h>
+#include <signet.h>
+#include <streams.h>
+#include <undo.h>
+#include <util/system.h>
+#include <validation.h>
+
+std::atomic_bool fImporting(false);
+std::atomic_bool fReindex(false);
+bool fHavePruned = false;
+bool fPruneMode = false;
+uint64_t nPruneTarget = 0;
+
+// TODO make namespace {
+RecursiveMutex cs_LastBlockFile;
+std::vector<CBlockFileInfo> vinfoBlockFile;
+int nLastBlockFile = 0;
+/** Global flag to indicate we should check to see if there are
+* block/undo files that should be deleted. Set on startup
+* or if we allocate more file space when we're in prune mode
+*/
+bool fCheckForPruning = false;
+
+/** Dirty block index entries. */
+std::set<CBlockIndex*> setDirtyBlockIndex;
+
+/** Dirty block file entries. */
+std::set<int> setDirtyFileInfo;
+// } // namespace
+
+static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
+static FlatFileSeq BlockFileSeq();
+static FlatFileSeq UndoFileSeq();
+
+bool IsBlockPruned(const CBlockIndex* pblockindex)
+{
+ return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
+}
+
+// If we're using -prune with -reindex, then delete block files that will be ignored by the
+// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
+// is missing, do the same here to delete any later block files after a gap. Also delete all
+// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile
+// is in sync with what's actually on disk by the time we start downloading, so that pruning
+// works correctly.
+void CleanupBlockRevFiles()
+{
+ std::map<std::string, fs::path> mapBlockFiles;
+
+ // Glob all blk?????.dat and rev?????.dat files from the blocks directory.
+ // Remove the rev files immediately and insert the blk file paths into an
+ // ordered map keyed by block file index.
+ LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
+ fs::path blocksdir = gArgs.GetBlocksDirPath();
+ for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
+ if (fs::is_regular_file(*it) &&
+ it->path().filename().string().length() == 12 &&
+ it->path().filename().string().substr(8,4) == ".dat")
+ {
+ if (it->path().filename().string().substr(0, 3) == "blk") {
+ mapBlockFiles[it->path().filename().string().substr(3, 5)] = it->path();
+ } else if (it->path().filename().string().substr(0, 3) == "rev") {
+ remove(it->path());
+ }
+ }
+ }
+
+ // Remove all block files that aren't part of a contiguous set starting at
+ // zero by walking the ordered map (keys are block file indices) by
+ // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
+ // start removing block files.
+ int nContigCounter = 0;
+ for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
+ if (atoi(item.first) == nContigCounter) {
+ nContigCounter++;
+ continue;
+ }
+ remove(item.second);
+ }
+}
+
+std::string CBlockFileInfo::ToString() const
+{
+ return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
+}
+
+CBlockFileInfo* GetBlockFileInfo(size_t n)
+{
+ LOCK(cs_LastBlockFile);
+
+ return &vinfoBlockFile.at(n);
+}
+
+static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
+{
+ // Open history file to append
+ CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull()) {
+ return error("%s: OpenUndoFile failed", __func__);
+ }
+
+ // Write index header
+ unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion());
+ fileout << messageStart << nSize;
+
+ // Write undo data
+ long fileOutPos = ftell(fileout.Get());
+ if (fileOutPos < 0) {
+ return error("%s: ftell failed", __func__);
+ }
+ pos.nPos = (unsigned int)fileOutPos;
+ fileout << blockundo;
+
+ // calculate & write checksum
+ CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
+ hasher << hashBlock;
+ hasher << blockundo;
+ fileout << hasher.GetHash();
+
+ return true;
+}
+
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
+{
+ FlatFilePos pos = pindex->GetUndoPos();
+ if (pos.IsNull()) {
+ return error("%s: no undo data available", __func__);
+ }
+
+ // Open history file to read
+ CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return error("%s: OpenUndoFile failed", __func__);
+ }
+
+ // Read block
+ uint256 hashChecksum;
+ CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
+ try {
+ verifier << pindex->pprev->GetBlockHash();
+ verifier >> blockundo;
+ filein >> hashChecksum;
+ } catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+
+ // Verify checksum
+ if (hashChecksum != verifier.GetHash()) {
+ return error("%s: Checksum mismatch", __func__);
+ }
+
+ return true;
+}
+
+static void FlushUndoFile(int block_file, bool finalize = false)
+{
+ FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
+ if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
+ AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
+ }
+}
+
+void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
+{
+ LOCK(cs_LastBlockFile);
+ FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
+ if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
+ AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
+ }
+ // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
+ // e.g. during IBD or a sync after a node going offline
+ if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
+}
+
+uint64_t CalculateCurrentUsage()
+{
+ LOCK(cs_LastBlockFile);
+
+ uint64_t retval = 0;
+ for (const CBlockFileInfo& file : vinfoBlockFile) {
+ retval += file.nSize + file.nUndoSize;
+ }
+ return retval;
+}
+
+void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
+{
+ for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
+ FlatFilePos pos(*it, 0);
+ fs::remove(BlockFileSeq().FileName(pos));
+ fs::remove(UndoFileSeq().FileName(pos));
+ LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
+ }
+}
+
+static FlatFileSeq BlockFileSeq()
+{
+ return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
+}
+
+static FlatFileSeq UndoFileSeq()
+{
+ return FlatFileSeq(gArgs.GetBlocksDirPath(), "rev", UNDOFILE_CHUNK_SIZE);
+}
+
+FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly)
+{
+ return BlockFileSeq().Open(pos, fReadOnly);
+}
+
+/** Open an undo file (rev?????.dat) */
+static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly)
+{
+ return UndoFileSeq().Open(pos, fReadOnly);
+}
+
+fs::path GetBlockPosFilename(const FlatFilePos& pos)
+{
+ return BlockFileSeq().FileName(pos);
+}
+
+bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
+{
+ LOCK(cs_LastBlockFile);
+
+ unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
+ if (vinfoBlockFile.size() <= nFile) {
+ vinfoBlockFile.resize(nFile + 1);
+ }
+
+ bool finalize_undo = false;
+ if (!fKnown) {
+ 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
+ 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);
+ }
+ }
+ pos.nFile = nFile;
+ pos.nPos = vinfoBlockFile[nFile].nSize;
+ }
+
+ if ((int)nFile != nLastBlockFile) {
+ if (!fKnown) {
+ LogPrint(BCLog::VALIDATION, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ }
+ FlushBlockFile(!fKnown, finalize_undo);
+ nLastBlockFile = nFile;
+ }
+
+ vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
+ if (fKnown) {
+ vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize);
+ } else {
+ vinfoBlockFile[nFile].nSize += nAddSize;
+ }
+
+ if (!fKnown) {
+ bool out_of_space;
+ size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode("Disk space is too low!", _("Disk space is too low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
+ }
+ }
+
+ setDirtyFileInfo.insert(nFile);
+ return true;
+}
+
+static bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize)
+{
+ pos.nFile = nFile;
+
+ LOCK(cs_LastBlockFile);
+
+ pos.nPos = vinfoBlockFile[nFile].nUndoSize;
+ vinfoBlockFile[nFile].nUndoSize += nAddSize;
+ setDirtyFileInfo.insert(nFile);
+
+ bool out_of_space;
+ size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
+ }
+
+ return true;
+}
+
+static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
+{
+ // Open history file to append
+ CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull()) {
+ return error("WriteBlockToDisk: OpenBlockFile failed");
+ }
+
+ // Write index header
+ unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
+ fileout << messageStart << nSize;
+
+ // Write block
+ long fileOutPos = ftell(fileout.Get());
+ if (fileOutPos < 0) {
+ return error("WriteBlockToDisk: ftell failed");
+ }
+ pos.nPos = (unsigned int)fileOutPos;
+ fileout << block;
+
+ return true;
+}
+
+bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
+{
+ // Write undo information to disk
+ if (pindex->GetUndoPos().IsNull()) {
+ FlatFilePos _pos;
+ if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) {
+ return error("ConnectBlock(): FindUndoPos failed");
+ }
+ if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) {
+ return AbortNode(state, "Failed to write undo data");
+ }
+ // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
+ // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
+ // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
+ // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
+ // the FindBlockPos function
+ if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
+ FlushUndoFile(_pos.nFile, true);
+ }
+
+ // update nUndoPos in block index
+ pindex->nUndoPos = _pos.nPos;
+ pindex->nStatus |= BLOCK_HAVE_UNDO;
+ setDirtyBlockIndex.insert(pindex);
+ }
+
+ return true;
+}
+
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
+{
+ block.SetNull();
+
+ // Open history file to read
+ CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
+ }
+
+ // Read block
+ try {
+ filein >> block;
+ } catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
+ }
+
+ // Check the header
+ if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
+ return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
+ }
+
+ // Signet only: check block solution
+ if (consensusParams.signet_blocks && !CheckSignetBlockSolution(block, consensusParams)) {
+ return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
+ }
+
+ return true;
+}
+
+bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
+{
+ FlatFilePos blockPos;
+ {
+ LOCK(cs_main);
+ blockPos = pindex->GetBlockPos();
+ }
+
+ if (!ReadBlockFromDisk(block, blockPos, consensusParams)) {
+ return false;
+ }
+ if (block.GetHash() != pindex->GetBlockHash()) {
+ return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
+ pindex->ToString(), pindex->GetBlockPos().ToString());
+ }
+ return true;
+}
+
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start)
+{
+ FlatFilePos hpos = pos;
+ hpos.nPos -= 8; // Seek back 8 bytes for meta header
+ CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
+ }
+
+ try {
+ CMessageHeader::MessageStartChars blk_start;
+ unsigned int blk_size;
+
+ filein >> blk_start >> blk_size;
+
+ if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
+ return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
+ HexStr(blk_start),
+ HexStr(message_start));
+ }
+
+ if (blk_size > MAX_SIZE) {
+ return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
+ blk_size, MAX_SIZE);
+ }
+
+ block.resize(blk_size); // Zeroing of memory is intentional here
+ filein.read((char*)block.data(), blk_size);
+ } catch (const std::exception& e) {
+ return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
+ }
+
+ return true;
+}
+
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
+{
+ FlatFilePos block_pos;
+ {
+ LOCK(cs_main);
+ block_pos = pindex->GetBlockPos();
+ }
+
+ return ReadRawBlockFromDisk(block, block_pos, message_start);
+}
+
+/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
+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, active_chain, block.GetBlockTime(), dbp != nullptr)) {
+ error("%s: FindBlockPos failed", __func__);
+ return FlatFilePos();
+ }
+ if (dbp == nullptr) {
+ if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
+ AbortNode("Failed to write block");
+ return FlatFilePos();
+ }
+ }
+ return blockPos;
+}
+
+struct CImportingNow {
+ CImportingNow()
+ {
+ assert(fImporting == false);
+ fImporting = true;
+ }
+
+ ~CImportingNow()
+ {
+ assert(fImporting == true);
+ fImporting = false;
+ }
+};
+
+void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
+{
+ const CChainParams& chainparams = Params();
+ ScheduleBatchPriority();
+
+ {
+ CImportingNow imp;
+
+ // -reindex
+ if (fReindex) {
+ int nFile = 0;
+ while (true) {
+ FlatFilePos pos(nFile, 0);
+ if (!fs::exists(GetBlockPosFilename(pos))) {
+ break; // No block files left to reindex
+ }
+ FILE* file = OpenBlockFile(pos, true);
+ if (!file) {
+ break; // This error is logged in OpenBlockFile
+ }
+ LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
+ chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file, &pos);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
+ nFile++;
+ }
+ pblocktree->WriteReindexing(false);
+ fReindex = false;
+ LogPrintf("Reindexing finished\n");
+ // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
+ chainman.ActiveChainstate().LoadGenesisBlock(chainparams);
+ }
+
+ // -loadblock=
+ for (const fs::path& path : vImportFiles) {
+ FILE* file = fsbridge::fopen(path, "rb");
+ if (file) {
+ LogPrintf("Importing blocks file %s...\n", path.string());
+ chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
+ } else {
+ LogPrintf("Warning: Could not open blocks file %s\n", path.string());
+ }
+ }
+
+ // scan for better chains in the block chain database, that are not yet connected in the active best chain
+
+ // We can't hold cs_main during ActivateBestChain even though we're accessing
+ // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
+ // the relevant pointers before the ABC call.
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
+ BlockValidationState state;
+ if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
+ LogPrintf("Failed to connect best block (%s)\n", state.ToString());
+ StartShutdown();
+ return;
+ }
+ }
+
+ if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
+ LogPrintf("Stopping after block import\n");
+ StartShutdown();
+ return;
+ }
+ } // End scope of CImportingNow
+ chainman.ActiveChainstate().LoadMempool(args);
+}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
new file mode 100644
index 0000000000..faf99dea81
--- /dev/null
+++ b/src/node/blockstorage.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2011-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_NODE_BLOCKSTORAGE_H
+#define BITCOIN_NODE_BLOCKSTORAGE_H
+
+#include <cstdint>
+#include <vector>
+
+#include <fs.h>
+#include <protocol.h> // For CMessageHeader::MessageStartChars
+
+class ArgsManager;
+class BlockValidationState;
+class CBlock;
+class CBlockFileInfo;
+class CBlockIndex;
+class CBlockUndo;
+class CChain;
+class CChainParams;
+class ChainstateManager;
+struct FlatFilePos;
+namespace Consensus {
+struct Params;
+}
+
+static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
+
+/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
+static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
+/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
+static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
+/** The maximum size of a blk?????.dat file (since 0.8) */
+static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
+
+extern std::atomic_bool fImporting;
+extern std::atomic_bool fReindex;
+/** Pruning-related variables and constants */
+/** True if any block files have ever been pruned. */
+extern bool fHavePruned;
+/** True if we're running in -prune mode. */
+extern bool fPruneMode;
+/** Number of MiB of block files that we're trying to stay below. */
+extern uint64_t nPruneTarget;
+
+//! Check whether the block associated with this index entry is pruned or not.
+bool IsBlockPruned(const CBlockIndex* pblockindex);
+
+void CleanupBlockRevFiles();
+
+/** Open a block file (blk?????.dat) */
+FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly = false);
+/** Translation to a filesystem path */
+fs::path GetBlockPosFilename(const FlatFilePos& pos);
+
+/** Get block file info entry for one block file */
+CBlockFileInfo* GetBlockFileInfo(size_t n);
+
+/** Calculate the amount of disk space the block & undo files currently use */
+uint64_t CalculateCurrentUsage();
+
+/**
+ * Actually unlink the specified files
+ */
+void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
+
+/** Functions for disk access for blocks */
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
+bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
+
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
+bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams);
+
+FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);
+
+void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args);
+
+#endif // BITCOIN_NODE_BLOCKSTORAGE_H
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 268580c6e6..38c1d29250 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -8,6 +8,7 @@
#include <coins.h>
#include <crypto/muhash.h>
#include <hash.h>
+#include <index/coinstatsindex.h>
#include <serialize.h>
#include <uint256.h>
#include <util/system.h>
@@ -16,44 +17,22 @@
#include <map>
// Database-independent metric indicating the UTXO set size
-static uint64_t GetBogoSize(const CScript& scriptPubKey)
+uint64_t GetBogoSize(const CScript& script_pub_key)
{
return 32 /* txid */ +
4 /* vout index */ +
4 /* height + coinbase */ +
8 /* amount */ +
2 /* scriptPubKey len */ +
- scriptPubKey.size() /* scriptPubKey */;
+ script_pub_key.size() /* scriptPubKey */;
}
-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)
-{
- 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);
- }
-}
-
-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 TxOutSer(const COutPoint& outpoint, const Coin& coin) {
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));
+ return ss;
}
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
@@ -68,14 +47,40 @@ static void ApplyHash(CCoinsStats& stats, MuHash3072& muhash, const uint256& has
//! 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)
+static void ApplyHash(CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+{
+ for (auto it = outputs.begin(); it != outputs.end(); ++it) {
+ 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);
+ }
+ }
+}
+
+static void ApplyHash(std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs) {}
+
+static void ApplyHash(MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+{
+ for (auto it = outputs.begin(); it != outputs.end(); ++it) {
+ COutPoint outpoint = COutPoint(hash, it->first);
+ Coin coin = it->second;
+ muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ }
+}
+
+static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
stats.nTransactions++;
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
- ApplyHash(stats, hash_obj, hash, outputs, it);
-
stats.nTransactionOutputs++;
stats.nTotalAmount += it->second.out.nValue;
stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
@@ -84,17 +89,25 @@ static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, con
//! Calculate statistics about the unspent transaction output set
template <typename T>
-static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, 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, const CBlockIndex* pindex)
{
- stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
- stats.hashBlock = pcursor->GetBestBlock();
- {
- LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
- stats.nHeight = blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
+ if (!pindex) {
+ {
+ LOCK(cs_main);
+ assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
+ pindex = blockman.LookupBlockIndex(view->GetBestBlock());
+ }
+ }
+ stats.nHeight = Assert(pindex)->nHeight;
+ stats.hashBlock = pindex->GetBlockHash();
+
+ // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
+ if ((stats.m_hash_type == CoinStatsHashType::MUHASH || stats.m_hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && stats.index_requested) {
+ stats.index_used = true;
+ return g_coin_stats_index->LookUpStats(pindex, stats);
}
PrepareHash(hash_obj, stats);
@@ -107,7 +120,8 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
Coin coin;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
if (!outputs.empty() && key.hash != prevkey) {
- ApplyStats(stats, hash_obj, prevkey, outputs);
+ ApplyStats(stats, prevkey, outputs);
+ ApplyHash(hash_obj, prevkey, outputs);
outputs.clear();
}
prevkey = key.hash;
@@ -119,7 +133,8 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
pcursor->Next();
}
if (!outputs.empty()) {
- ApplyStats(stats, hash_obj, prevkey, outputs);
+ ApplyStats(stats, prevkey, outputs);
+ ApplyHash(hash_obj, prevkey, outputs);
}
FinalizeHash(hash_obj, stats);
@@ -128,19 +143,19 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
return true;
}
-bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, CoinStatsHashType hash_type, const std::function<void()>& interruption_point)
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point, const CBlockIndex* pindex)
{
- switch (hash_type) {
+ switch (stats.m_hash_type) {
case(CoinStatsHashType::HASH_SERIALIZED): {
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
- return GetUTXOStats(view, blockman, stats, ss, interruption_point);
+ return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex);
}
case(CoinStatsHashType::MUHASH): {
MuHash3072 muhash;
- return GetUTXOStats(view, blockman, stats, muhash, interruption_point);
+ return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex);
}
case(CoinStatsHashType::NONE): {
- return GetUTXOStats(view, blockman, stats, nullptr, interruption_point);
+ return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex);
}
} // no default case, so the compiler can warn about missing cases
assert(false);
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 975651dcc4..8be256edc9 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -7,6 +7,9 @@
#define BITCOIN_NODE_COINSTATS_H
#include <amount.h>
+#include <chain.h>
+#include <coins.h>
+#include <streams.h>
#include <uint256.h>
#include <cstdint>
@@ -23,6 +26,7 @@ enum class CoinStatsHashType {
struct CCoinsStats
{
+ CoinStatsHashType m_hash_type;
int nHeight{0};
uint256 hashBlock{};
uint64_t nTransactions{0};
@@ -34,9 +38,31 @@ struct CCoinsStats
//! The number of coins contained.
uint64_t coins_count{0};
+
+ //! Signals if the coinstatsindex should be used (when available).
+ bool index_requested{true};
+ //! Signals if the coinstatsindex was used to retrieve the statistics.
+ bool index_used{false};
+
+ // Following values are only available from coinstats index
+ CAmount total_subsidy{0};
+ CAmount block_unspendable_amount{0};
+ CAmount block_prevout_spent_amount{0};
+ CAmount block_new_outputs_ex_coinbase_amount{0};
+ CAmount block_coinbase_amount{0};
+ CAmount unspendables_genesis_block{0};
+ CAmount unspendables_bip30{0};
+ CAmount unspendables_scripts{0};
+ CAmount unspendables_unclaimed_rewards{0};
+
+ CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {}
};
//! Calculate statistics about the unspent transaction output set
-bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const CoinStatsHashType hash_type, const std::function<void()>& interruption_point = {});
+bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function<void()>& interruption_point = {}, const CBlockIndex* pindex = nullptr);
+
+uint64_t GetBogoSize(const CScript& script_pub_key);
+
+CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
#endif // BITCOIN_NODE_COINSTATS_H
diff --git a/src/node/context.h b/src/node/context.h
index 2be9a584e6..06adb33a80 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -22,6 +22,7 @@ class PeerManager;
namespace interfaces {
class Chain;
class ChainClient;
+class Init;
class WalletClient;
} // namespace interfaces
@@ -36,6 +37,8 @@ class WalletClient;
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
+ //! Init interface for initializing current process and connecting to other processes.
+ interfaces::Init* init{nullptr};
std::unique_ptr<CAddrMan> addrman;
std::unique_ptr<CConnman> connman;
std::unique_ptr<CTxMemPool> mempool;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 7ad02f81dc..8befbf5e30 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -4,7 +4,6 @@
#include <addrdb.h>
#include <banman.h>
-#include <boost/signals2/signal.hpp>
#include <chain.h>
#include <chainparams.h>
#include <init.h>
@@ -17,6 +16,7 @@
#include <net_processing.h>
#include <netaddress.h>
#include <netbase.h>
+#include <node/blockstorage.h>
#include <node/coin.h>
#include <node/context.h>
#include <node/transaction.h>
@@ -53,6 +53,8 @@
#include <optional>
#include <utility>
+#include <boost/signals2/signal.hpp>
+
using interfaces::BlockTip;
using interfaces::Chain;
using interfaces::FoundBlock;
@@ -80,7 +82,7 @@ public:
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
- return AppInitMain(m_context_ref, *m_context, tip_info);
+ return AppInitMain(*m_context, tip_info);
}
void appShutdown() override
{
@@ -244,7 +246,8 @@ public:
CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
{
- JSONRPCRequest req(m_context_ref);
+ JSONRPCRequest req;
+ req.context = m_context;
req.params = params;
req.strMethod = command;
req.URI = uri;
@@ -314,14 +317,8 @@ public:
void setContext(NodeContext* context) override
{
m_context = context;
- if (context) {
- m_context_ref = context;
- } else {
- m_context_ref.reset();
- }
}
NodeContext* m_context{nullptr};
- std::any m_context_ref;
};
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active)
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 691b2791d7..a1e7a71e2c 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -100,8 +100,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
// the mempool tracks locally submitted transactions to make a
// best-effort of initial broadcast
node.mempool->AddUnbroadcastTx(hashTx);
-
- LOCK(cs_main);
node.peerman->RelayTransaction(hashTx, tx->GetWitnessHash());
}
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
index fe78cb46bd..61292cdcc5 100644
--- a/src/node/utxo_snapshot.h
+++ b/src/node/utxo_snapshot.h
@@ -22,20 +22,15 @@ public:
//! during snapshot load to estimate progress of UTXO set reconstruction.
uint64_t m_coins_count = 0;
- //! Necessary to "fake" the base nChainTx so that we can estimate progress during
- //! initial block download for the assumeutxo chainstate.
- unsigned int m_nchaintx = 0;
-
SnapshotMetadata() { }
SnapshotMetadata(
const uint256& base_blockhash,
uint64_t coins_count,
unsigned int nchaintx) :
m_base_blockhash(base_blockhash),
- m_coins_count(coins_count),
- m_nchaintx(nchaintx) { }
+ m_coins_count(coins_count) { }
- SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count, obj.m_nchaintx); }
+ SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); }
};
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index 3da85fedf9..25b9282b4e 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -7,29 +7,26 @@
#include <tinyformat.h>
-CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_)
+CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes)
{
- assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
- int64_t nSize = int64_t(nBytes_);
+ const int64_t nSize{num_bytes};
- if (nSize > 0)
+ if (nSize > 0) {
nSatoshisPerK = nFeePaid * 1000 / nSize;
- else
+ } else {
nSatoshisPerK = 0;
+ }
}
-CAmount CFeeRate::GetFee(size_t nBytes_) const
+CAmount CFeeRate::GetFee(uint32_t num_bytes) const
{
- assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
- int64_t nSize = int64_t(nBytes_);
+ const int64_t nSize{num_bytes};
CAmount nFee = nSatoshisPerK * nSize / 1000;
if (nFee == 0 && nSize != 0) {
- if (nSatoshisPerK > 0)
- nFee = CAmount(1);
- if (nSatoshisPerK < 0)
- nFee = CAmount(-1);
+ if (nSatoshisPerK > 0) nFee = CAmount(1);
+ if (nSatoshisPerK < 0) nFee = CAmount(-1);
}
return nFee;
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 86ae507957..d296d32774 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -39,21 +39,17 @@ public:
// We've previously had bugs creep in from silent double->int conversion...
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
}
- /** Constructor for a fee rate in satoshis per kvB (sat/kvB). The size in bytes must not exceed (2^63 - 1).
+ /** Constructor for a fee rate in satoshis per kvB (sat/kvB).
*
- * Passing an nBytes value of COIN (1e8) returns a fee rate in satoshis per vB (sat/vB),
+ * Passing a num_bytes value of COIN (1e8) returns a fee rate in satoshis per vB (sat/vB),
* e.g. (nFeePaid * 1e8 / 1e3) == (nFeePaid / 1e5),
* where 1e5 is the ratio to convert from BTC/kvB to sat/vB.
- *
- * @param[in] nFeePaid CAmount fee rate to construct with
- * @param[in] nBytes size_t bytes (units) to construct with
- * @returns fee rate
*/
- CFeeRate(const CAmount& nFeePaid, size_t nBytes);
+ CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes);
/**
* Return the fee in satoshis for the given size in bytes.
*/
- CAmount GetFee(size_t nBytes) const;
+ CAmount GetFee(uint32_t num_bytes) const;
/**
* Return the fee in satoshis for a size of 1000 bytes
*/
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 7da171d2e1..2ae5798ebe 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -10,6 +10,7 @@
#include <logging.h>
#include <streams.h>
#include <txmempool.h>
+#include <util/serfloat.h>
#include <util/system.h>
static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
@@ -26,6 +27,25 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
assert(false);
}
+namespace {
+
+struct EncodedDoubleFormatter
+{
+ template<typename Stream> void Ser(Stream &s, double v)
+ {
+ s << EncodeDouble(v);
+ }
+
+ template<typename Stream> void Unser(Stream& s, double& v)
+ {
+ uint64_t encoded;
+ s >> encoded;
+ v = DecodeDouble(encoded);
+ }
+};
+
+} // namespace
+
/**
* We will instantiate an instance of this class to track transactions that were
* included in a block. We will lump transactions into a bucket according to their
@@ -356,12 +376,12 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
void TxConfirmStats::Write(CAutoFile& fileout) const
{
- fileout << decay;
+ fileout << Using<EncodedDoubleFormatter>(decay);
fileout << scale;
- fileout << m_feerate_avg;
- fileout << txCtAvg;
- fileout << confAvg;
- fileout << failAvg;
+ fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
+ fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
+ fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
+ fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
}
void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets)
@@ -372,7 +392,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
size_t maxConfirms, maxPeriods;
// The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
- filein >> decay;
+ filein >> Using<EncodedDoubleFormatter>(decay);
if (decay <= 0 || decay >= 1) {
throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
}
@@ -381,15 +401,15 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
}
- filein >> m_feerate_avg;
+ filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
if (m_feerate_avg.size() != numBuckets) {
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
}
- filein >> txCtAvg;
+ filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
if (txCtAvg.size() != numBuckets) {
throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
}
- filein >> confAvg;
+ filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
maxPeriods = confAvg.size();
maxConfirms = scale * maxPeriods;
@@ -402,7 +422,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
}
}
- filein >> failAvg;
+ filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
if (maxPeriods != failAvg.size()) {
throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");
}
@@ -504,7 +524,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
// If the fee estimation file is present, read recorded estimations
- fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION);
if (est_file.IsNull() || !Read(est_file)) {
LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", est_filepath.string());
@@ -864,7 +884,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
void CBlockPolicyEstimator::Flush() {
FlushUnconfirmed();
- fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION);
if (est_file.IsNull() || !Write(est_file)) {
LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", est_filepath.string());
@@ -884,7 +904,7 @@ bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
else {
fileout << historicalFirst << historicalBest;
}
- fileout << buckets;
+ fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets);
feeStats->Write(fileout);
shortStats->Write(fileout);
longStats->Write(fileout);
@@ -920,7 +940,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
}
std::vector<double> fileBuckets;
- filein >> fileBuckets;
+ filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
size_t numBuckets = fileBuckets.size();
if (numBuckets <= 1 || numBuckets > 1000) {
throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
diff --git a/src/policy/packages.h b/src/policy/packages.h
new file mode 100644
index 0000000000..4b1463dcb3
--- /dev/null
+++ b/src/policy/packages.h
@@ -0,0 +1,34 @@
+// 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_POLICY_PACKAGES_H
+#define BITCOIN_POLICY_PACKAGES_H
+
+#include <consensus/validation.h>
+#include <primitives/transaction.h>
+
+#include <vector>
+
+/** Default maximum number of transactions in a package. */
+static constexpr uint32_t MAX_PACKAGE_COUNT{25};
+/** Default maximum total virtual size of transactions in a package in KvB. */
+static constexpr uint32_t MAX_PACKAGE_SIZE{101};
+
+/** A "reason" why a package was invalid. It may be that one or more of the included
+ * transactions is invalid or the package itself violates our rules.
+ * We don't distinguish between consensus and policy violations right now.
+ */
+enum class PackageValidationResult {
+ PCKG_RESULT_UNSET = 0, //!< Initial value. The package has not yet been rejected.
+ PCKG_POLICY, //!< The package itself is invalid (e.g. too many transactions).
+ PCKG_TX, //!< At least one tx is invalid.
+};
+
+/** A package is an ordered list of transactions. The transactions cannot conflict with (spend the
+ * same inputs as) one another. */
+using Package = std::vector<CTransactionRef>;
+
+class PackageValidationState : public ValidationState<PackageValidationResult> {};
+
+#endif // BITCOIN_POLICY_PACKAGES_H
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 2b5fd4179d..9e433584e7 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -21,12 +21,12 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
// need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than
// 182*dustRelayFee/1000 (in satoshis).
- // 546 satoshis at the default rate of 3000 sat/kB.
+ // 546 satoshis at the default rate of 3000 sat/kvB.
// A typical spendable segwit txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
// 98*dustRelayFee/1000 (in satoshis).
- // 294 satoshis at the default rate of 3000 sat/kB.
+ // 294 satoshis at the default rate of 3000 sat/kvB.
if (txout.scriptPubKey.IsUnspendable())
return 0;
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 0b893b9272..2e70b41e4c 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -87,26 +87,16 @@ const static std::string allNetMessageTypes[] = {
};
const static std::vector<std::string> allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes));
-CMessageHeader::CMessageHeader()
-{
- memset(pchMessageStart, 0, MESSAGE_START_SIZE);
- memset(pchCommand, 0, sizeof(pchCommand));
- nMessageSize = -1;
- memset(pchChecksum, 0, CHECKSUM_SIZE);
-}
-
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn)
{
memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
- // Copy the command name, zero-padding to COMMAND_SIZE bytes
+ // Copy the command name
size_t i = 0;
for (; i < COMMAND_SIZE && pszCommand[i] != 0; ++i) pchCommand[i] = pszCommand[i];
assert(pszCommand[i] == 0); // Assert that the command name passed in is not longer than COMMAND_SIZE
- for (; i < COMMAND_SIZE; ++i) pchCommand[i] = 0;
nMessageSize = nMessageSizeIn;
- memset(pchChecksum, 0, CHECKSUM_SIZE);
}
std::string CMessageHeader::GetCommand() const
diff --git a/src/protocol.h b/src/protocol.h
index f183db0501..aaa9f1df40 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -16,6 +16,7 @@
#include <uint256.h>
#include <version.h>
+#include <limits>
#include <stdint.h>
#include <string>
@@ -37,7 +38,7 @@ public:
static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE;
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
- explicit CMessageHeader();
+ explicit CMessageHeader() = default;
/** Construct a P2P message header from message-start characters, a command and the size of the message.
* @note Passing in a `pszCommand` longer than COMMAND_SIZE will result in a run-time assertion error.
@@ -49,10 +50,10 @@ public:
SERIALIZE_METHODS(CMessageHeader, obj) { READWRITE(obj.pchMessageStart, obj.pchCommand, obj.nMessageSize, obj.pchChecksum); }
- char pchMessageStart[MESSAGE_START_SIZE];
- char pchCommand[COMMAND_SIZE];
- uint32_t nMessageSize;
- uint8_t pchChecksum[CHECKSUM_SIZE];
+ char pchMessageStart[MESSAGE_START_SIZE]{};
+ char pchCommand[COMMAND_SIZE]{};
+ uint32_t nMessageSize{std::numeric_limits<uint32_t>::max()};
+ uint8_t pchChecksum[CHECKSUM_SIZE]{};
};
/**
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index fc1624af25..51cc826b00 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -5,9 +5,16 @@
#include <pubkey.h>
+#include <hash.h>
#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
#include <secp256k1_recovery.h>
#include <secp256k1_schnorrsig.h>
+#include <span.h>
+#include <uint256.h>
+
+#include <algorithm>
+#include <cassert>
namespace
{
@@ -173,6 +180,12 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
}
+bool XOnlyPubKey::IsFullyValid() const
+{
+ secp256k1_xonly_pubkey pubkey;
+ return secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data());
+}
+
bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
{
assert(sigbytes.size() == 64);
@@ -181,13 +194,45 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
}
-bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
+static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+
+uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
+{
+ if (merkle_root == nullptr) {
+ // We have no scripts. The actual tweak does not matter, but follow BIP341 here to
+ // allow for reproducible tweaking.
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
+ } else {
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
+ }
+}
+
+bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
+{
+ secp256k1_xonly_pubkey internal_key;
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
+ uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
+ return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
+}
+
+std::optional<std::pair<XOnlyPubKey, bool>> XOnlyPubKey::CreateTapTweak(const uint256* merkle_root) const
{
secp256k1_xonly_pubkey base_point;
- if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
- return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, data())) return std::nullopt;
+ secp256k1_pubkey out;
+ uint256 tweak = ComputeTapTweakHash(merkle_root);
+ if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_verify, &out, &base_point, tweak.data())) return std::nullopt;
+ int parity = -1;
+ std::pair<XOnlyPubKey, bool> ret;
+ secp256k1_xonly_pubkey out_xonly;
+ if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_verify, &out_xonly, &parity, &out)) return std::nullopt;
+ secp256k1_xonly_pubkey_serialize(secp256k1_context_verify, ret.first.begin(), &out_xonly);
+ assert(parity == 0 || parity == 1);
+ ret.second = parity;
+ return ret;
}
+
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
@@ -293,7 +338,7 @@ void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = pubkey.GetID();
- memcpy(&out.vchFingerprint[0], &id, 4);
+ memcpy(out.vchFingerprint, &id, 4);
out.nChild = _nChild;
return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode);
}
diff --git a/src/pubkey.h b/src/pubkey.h
index 12514fc3c9..152a48dd18 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -12,7 +12,8 @@
#include <span.h>
#include <uint256.h>
-#include <stdexcept>
+#include <cstring>
+#include <optional>
#include <vector>
const unsigned int BIP32_EXTKEY_SIZE = 74;
@@ -101,7 +102,7 @@ public:
}
//! Construct a public key from a byte vector.
- explicit CPubKey(const std::vector<unsigned char>& _vch)
+ explicit CPubKey(Span<const uint8_t> _vch)
{
Set(_vch.begin(), _vch.end());
}
@@ -222,19 +223,56 @@ private:
uint256 m_keydata;
public:
+ /** Construct an empty x-only pubkey. */
+ XOnlyPubKey() = default;
+
+ XOnlyPubKey(const XOnlyPubKey&) = default;
+ XOnlyPubKey& operator=(const XOnlyPubKey&) = default;
+
+ /** Determine if this pubkey is fully valid. This is true for approximately 50% of all
+ * possible 32-byte arrays. If false, VerifySchnorr and CreatePayToContract will always
+ * fail. */
+ bool IsFullyValid() const;
+
/** Construct an x-only pubkey from exactly 32 bytes. */
explicit XOnlyPubKey(Span<const unsigned char> bytes);
+ /** Construct an x-only pubkey from a normal pubkey. */
+ explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span<const unsigned char>(pubkey.begin() + 1, pubkey.begin() + 33)) {}
+
/** Verify a Schnorr signature against this public key.
*
* sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
- bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
+
+ /** Compute the Taproot tweak as specified in BIP341, with *this as internal
+ * key:
+ * - if merkle_root == nullptr: H_TapTweak(xonly_pubkey)
+ * - otherwise: H_TapTweak(xonly_pubkey || *merkle_root)
+ *
+ * Note that the behavior of this function with merkle_root != nullptr is
+ * consensus critical.
+ */
+ uint256 ComputeTapTweakHash(const uint256* merkle_root) const;
+
+ /** Verify that this is a Taproot tweaked output point, against a specified internal key,
+ * Merkle root, and parity. */
+ bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const;
+
+ /** Construct a Taproot tweaked output point with this point as internal key. */
+ std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const;
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
const unsigned char* data() const { return m_keydata.begin(); }
- size_t size() const { return m_keydata.size(); }
+ static constexpr size_t size() { return decltype(m_keydata)::size(); }
+ const unsigned char* begin() const { return m_keydata.begin(); }
+ const unsigned char* end() const { return m_keydata.end(); }
+ unsigned char* begin() { return m_keydata.begin(); }
+ unsigned char* end() { return m_keydata.end(); }
+ bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; }
+ bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; }
+ bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; }
};
struct CExtPubKey {
@@ -247,7 +285,7 @@ struct CExtPubKey {
friend bool operator==(const CExtPubKey &a, const CExtPubKey &b)
{
return a.nDepth == b.nDepth &&
- memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ memcmp(a.vchFingerprint, b.vchFingerprint, sizeof(vchFingerprint)) == 0 &&
a.nChild == b.nChild &&
a.chaincode == b.chaincode &&
a.pubkey == b.pubkey;
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 9927e925ac..7024fc7654 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -112,29 +112,17 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
break;
}
- // Context menu actions
- QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
- QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
- QAction *editAction = new QAction(tr("&Edit"), this);
- deleteAction = new QAction(ui->deleteAddress->text(), this);
-
// Build context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(copyAddressAction);
- contextMenu->addAction(copyLabelAction);
- contextMenu->addAction(editAction);
- if(tab == SendingTab)
- contextMenu->addAction(deleteAction);
- contextMenu->addSeparator();
-
- // Connect signals for context menu actions
- connect(copyAddressAction, &QAction::triggered, this, &AddressBookPage::on_copyAddress_clicked);
- connect(copyLabelAction, &QAction::triggered, this, &AddressBookPage::onCopyLabelAction);
- connect(editAction, &QAction::triggered, this, &AddressBookPage::onEditAction);
- connect(deleteAction, &QAction::triggered, this, &AddressBookPage::on_deleteAddress_clicked);
+ contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
+ contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
+ contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
- connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
+ if (tab == SendingTab) {
+ contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
+ }
+ connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
GUIUtil::handleCloseWindowShortcut(this);
@@ -249,13 +237,11 @@ void AddressBookPage::selectionChanged()
// In sending tab, allow deletion of selection
ui->deleteAddress->setEnabled(true);
ui->deleteAddress->setVisible(true);
- deleteAction->setEnabled(true);
break;
case ReceivingTab:
// Deleting receiving addresses, however, is not allowed
ui->deleteAddress->setEnabled(false);
ui->deleteAddress->setVisible(false);
- deleteAction->setEnabled(false);
break;
}
ui->copyAddress->setEnabled(true);
@@ -295,7 +281,9 @@ 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", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr);
+ /*: Expanded name of the CSV file format.
+ See https://en.wikipedia.org/wiki/Comma-separated_values */
+ tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
return;
@@ -309,6 +297,8 @@ void AddressBookPage::on_exportButton_clicked()
if(!writer.write()) {
QMessageBox::critical(this, tr("Exporting Failed"),
+ /*: An error message. %1 is a stand-in argument for the name
+ of the file we attempted to save to. */
tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
}
}
diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h
index c6a364ccbd..93feac9e23 100644
--- a/src/qt/addressbookpage.h
+++ b/src/qt/addressbookpage.h
@@ -55,7 +55,6 @@ private:
QString returnValue;
AddressBookSortFilterProxyModel *proxyModel;
QMenu *contextMenu;
- QAction *deleteAction; // to be able to explicitly disable it
QString newAddressToSelect;
private Q_SLOTS:
diff --git a/src/qt/android/.gitignore b/src/qt/android/.gitignore
new file mode 100644
index 0000000000..74cf42f934
--- /dev/null
+++ b/src/qt/android/.gitignore
@@ -0,0 +1,9 @@
+/.gradle
+/build
+/gradle/wrapper
+/gradlew*
+/libs
+/res/layout
+/res/values*
+/src/org/kde
+/src/org/qtproject
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index fc6d0febc2..9e6cf56d31 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -45,6 +45,7 @@
#include <QApplication>
#include <QDebug>
#include <QFontDatabase>
+#include <QLatin1String>
#include <QLibraryInfo>
#include <QLocale>
#include <QMessageBox>
@@ -59,6 +60,7 @@
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
+Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
Q_IMPORT_PLUGIN(QMacStylePlugin);
@@ -317,11 +319,9 @@ void BitcoinApplication::parameterSetup()
InitParameterInteraction(gArgs);
}
-void BitcoinApplication::InitializePruneSetting(bool prune)
+void BitcoinApplication::InitPruneSetting(int64_t prune_MiB)
{
- // If prune is set, intentionally override existing prune size with
- // the default size since this is called when choosing a new datadir.
- optionsModel->SetPruneTargetGB(prune ? DEFAULT_PRUNE_TARGET_GB : 0, true);
+ optionsModel->SetPruneTargetGB(PruneMiBtoGB(prune_MiB), true);
}
void BitcoinApplication::requestInitialize()
@@ -417,10 +417,23 @@ void BitcoinApplication::shutdownResult()
void BitcoinApplication::handleRunawayException(const QString &message)
{
- QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("<br><br>") + message);
+ QMessageBox::critical(
+ nullptr, tr("Runaway exception"),
+ tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) +
+ QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
::exit(EXIT_FAILURE);
}
+void BitcoinApplication::handleNonFatalException(const QString& message)
+{
+ assert(QThread::currentThread() == thread());
+ QMessageBox::warning(
+ nullptr, tr("Internal error"),
+ tr("An internal error occurred. %1 will attempt to continue safely. This is "
+ "an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) +
+ QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
+}
+
WId BitcoinApplication::getMainWinId() const
{
if (!window)
@@ -518,12 +531,12 @@ int GuiMain(int argc, char* argv[])
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
bool did_show_intro = false;
- bool prune = false; // Intro dialog prune check box
+ int64_t prune_MiB = 0; // Intro dialog prune configuration
// Gracefully exit if the user cancels
- if (!Intro::showIfNeeded(did_show_intro, prune)) return EXIT_SUCCESS;
+ if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
- /// - Do not call GetDataDir(true) before this step finishes
+ /// - Do not call gArgs.GetDataDirNet() before this step finishes
if (!CheckDataDirOption()) {
InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
@@ -602,7 +615,7 @@ int GuiMain(int argc, char* argv[])
if (did_show_intro) {
// Store intro dialog settings other than datadir (network specific)
- app.InitializePruneSetting(prune);
+ app.InitPruneSetting(prune_MiB);
}
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
@@ -620,7 +633,7 @@ int GuiMain(int argc, char* argv[])
if (app.baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN)
- WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
+ WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
app.requestShutdown();
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 69e0a5921e..f9fab0534b 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -68,7 +68,7 @@ public:
/// Create options model
void createOptionsModel(bool resetSettings);
/// Initialize prune setting
- void InitializePruneSetting(bool prune);
+ void InitPruneSetting(int64_t prune_MiB);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
@@ -99,6 +99,12 @@ public Q_SLOTS:
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
+ /**
+ * A helper function that shows a message box
+ * with details about a non-fatal exception.
+ */
+ void handleNonFatalException(const QString& message);
+
Q_SIGNALS:
void requestedInitialize();
void requestedShutdown();
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 2af3502be3..ba59bcc660 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -239,7 +239,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
// If one if the widgets changes, the combined content changes as well
connect(amount, &AmountSpinBox::valueChanged, this, &BitcoinAmountField::valueChanged);
- connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);
+ connect(unit, qOverload<int>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);
// Set default based on configuration
unitChanged(unit->currentIndex());
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6677c9e3b5..50f6e739e8 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -43,6 +43,7 @@
#include <QAction>
#include <QApplication>
#include <QComboBox>
+#include <QCursor>
#include <QDateTime>
#include <QDragEnterEvent>
#include <QListWidget>
@@ -104,6 +105,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
{
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(_platformStyle, this);
+ connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] {
+ auto activity = new CreateWalletActivity(getWalletController(), this);
+ connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
+ activity->create();
+ });
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -150,11 +156,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
frameBlocksLayout->setContentsMargins(3,0,3,0);
frameBlocksLayout->setSpacing(3);
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
- labelWalletEncryptionIcon = new QLabel();
- labelWalletHDStatusIcon = new QLabel();
- labelProxyIcon = new GUIUtil::ClickableLabel();
- connectionsControl = new GUIUtil::ClickableLabel();
- labelBlocksIcon = new GUIUtil::ClickableLabel();
+ labelWalletEncryptionIcon = new GUIUtil::ThemedLabel(platformStyle);
+ labelWalletHDStatusIcon = new GUIUtil::ThemedLabel(platformStyle);
+ labelProxyIcon = new GUIUtil::ClickableLabel(platformStyle);
+ connectionsControl = new GUIUtil::ClickableLabel(platformStyle);
+ labelBlocksIcon = new GUIUtil::ClickableLabel(platformStyle);
if(enableWallet)
{
frameBlocksLayout->addStretch();
@@ -199,9 +205,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
// Subscribe to notifications from core
subscribeToCoreSignals();
- connect(connectionsControl, &GUIUtil::ClickableLabel::clicked, [this] {
- m_node.setNetworkActive(!m_node.getNetworkActive());
- });
connect(labelProxyIcon, &GUIUtil::ClickableLabel::clicked, [this] {
openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK);
});
@@ -308,27 +311,27 @@ void BitcoinGUI::createActions()
aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
- optionsAction = new QAction(tr("&Options..."), this);
+ optionsAction = new QAction(tr("&Options…"), this);
optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(PACKAGE_NAME));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
toggleHideAction = new QAction(tr("&Show / Hide"), this);
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
- encryptWalletAction = new QAction(tr("&Encrypt Wallet..."), this);
+ encryptWalletAction = new QAction(tr("&Encrypt Wallet…"), this);
encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
- backupWalletAction = new QAction(tr("&Backup Wallet..."), this);
+ backupWalletAction = new QAction(tr("&Backup Wallet…"), this);
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
- changePassphraseAction = new QAction(tr("&Change Passphrase..."), this);
+ changePassphraseAction = new QAction(tr("&Change Passphrase…"), this);
changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
- signMessageAction = new QAction(tr("Sign &message..."), this);
+ signMessageAction = new QAction(tr("Sign &message…"), this);
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
- verifyMessageAction = new QAction(tr("&Verify message..."), this);
+ verifyMessageAction = new QAction(tr("&Verify message…"), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
+ m_load_psbt_action = new QAction(tr("&Load PSBT from file…"), this);
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
- m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
+ m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard…"), this);
m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
openRPCConsoleAction = new QAction(tr("Node window"), this);
@@ -342,7 +345,7 @@ void BitcoinGUI::createActions()
usedReceivingAddressesAction = new QAction(tr("&Receiving addresses"), this);
usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
- openAction = new QAction(tr("Open &URI..."), this);
+ openAction = new QAction(tr("Open &URI…"), this);
openAction->setStatusTip(tr("Open a bitcoin: URI"));
m_open_wallet_action = new QAction(tr("Open Wallet"), this);
@@ -350,14 +353,14 @@ void BitcoinGUI::createActions()
m_open_wallet_action->setStatusTip(tr("Open a wallet"));
m_open_wallet_menu = new QMenu(this);
- m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
+ m_close_wallet_action = new QAction(tr("Close Wallet…"), this);
m_close_wallet_action->setStatusTip(tr("Close wallet"));
- m_create_wallet_action = new QAction(tr("Create Wallet..."), this);
+ m_create_wallet_action = new QAction(tr("Create Wallet…"), this);
m_create_wallet_action->setEnabled(false);
m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
- m_close_all_wallets_action = new QAction(tr("Close All Wallets..."), this);
+ m_close_all_wallets_action = new QAction(tr("Close All Wallets…"), this);
m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
showHelpMessageAction = new QAction(tr("&Command-line options"), this);
@@ -561,7 +564,7 @@ void BitcoinGUI::createToolBars()
m_wallet_selector = new QComboBox();
m_wallet_selector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
- connect(m_wallet_selector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinGUI::setCurrentWalletBySelectorIndex);
+ connect(m_wallet_selector, qOverload<int>(&QComboBox::currentIndexChanged), this, &BitcoinGUI::setCurrentWalletBySelectorIndex);
m_wallet_selector_label = new QLabel();
m_wallet_selector_label->setText(tr("Wallet:") + " ");
@@ -586,7 +589,10 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH
createTrayIconMenu();
// Keep up to date with client
- updateNetworkState();
+ setNetworkActive(m_node.getNetworkActive());
+ connect(connectionsControl, &GUIUtil::ClickableLabel::clicked, [this] {
+ GUIUtil::PopupMenu(m_network_context_menu, QCursor::pos());
+ });
connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
@@ -654,7 +660,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
m_open_wallet_action->setEnabled(true);
m_open_wallet_action->setMenu(m_open_wallet_menu);
- connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
+ GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) {
@@ -670,7 +676,10 @@ WalletController* BitcoinGUI::getWalletController()
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
- if (!walletFrame->addWallet(walletModel)) return;
+
+ WalletView* wallet_view = new WalletView(platformStyle, walletFrame);
+ if (!walletFrame->addWallet(walletModel, wallet_view)) return;
+
rpcConsole->addWallet(walletModel);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(true);
@@ -680,6 +689,18 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
}
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
+
+ connect(wallet_view, &WalletView::outOfSyncWarningClicked, walletFrame, &WalletFrame::outOfSyncWarningClicked);
+ connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
+ this->message(title, message, style);
+ });
+ connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction);
+ connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
+ wallet_view->setPrivacy(isPrivacyModeActivated());
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
@@ -915,17 +936,21 @@ void BitcoinGUI::updateNetworkState()
QString tooltip;
if (m_node.getNetworkActive()) {
- tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".<br>") + tr("Click to disable network activity.");
+ //: A substring of the tooltip.
+ tooltip = tr("%n active connection(s) to Bitcoin network.", "", count);
} else {
- tooltip = tr("Network activity disabled.") + QString("<br>") + tr("Click to enable network activity again.");
+ //: A substring of the tooltip.
+ tooltip = tr("Network activity disabled.");
icon = ":/icons/network_disabled";
}
// Don't word-wrap this (fixed-width) tooltip
- tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
+ tooltip = QLatin1String("<nobr>") + tooltip + QLatin1String("<br>") +
+ //: A substring of the tooltip. "More actions" are available via the context menu.
+ tr("Click for more actions.") + QLatin1String("</nobr>");
connectionsControl->setToolTip(tooltip);
- connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ connectionsControl->setThemedPixmap(icon, STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
}
void BitcoinGUI::setNumConnections(int count)
@@ -933,9 +958,24 @@ void BitcoinGUI::setNumConnections(int count)
updateNetworkState();
}
-void BitcoinGUI::setNetworkActive(bool networkActive)
+void BitcoinGUI::setNetworkActive(bool network_active)
{
updateNetworkState();
+ m_network_context_menu->clear();
+ m_network_context_menu->addAction(
+ //: A context menu item. The "Peers tab" is an element of the "Node window".
+ tr("Show Peers tab"),
+ [this] {
+ rpcConsole->setTabFocus(RPCConsole::TabTypes::PEERS);
+ showDebugWindow();
+ });
+ m_network_context_menu->addAction(
+ network_active ?
+ //: A context menu item.
+ tr("Disable network activity") :
+ //: A context menu item. The network activity was disabled previously.
+ tr("Enable network activity"),
+ [this, new_state = !network_active] { m_node.setNetworkActive(new_state); });
}
void BitcoinGUI::updateHeadersSyncProgressLabel()
@@ -944,7 +984,7 @@ void BitcoinGUI::updateHeadersSyncProgressLabel()
int headersTipHeight = clientModel->getHeaderTipHeight();
int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing;
if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
- progressBarLabel->setText(tr("Syncing Headers (%1%)...").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1)));
+ progressBarLabel->setText(tr("Syncing Headers (%1%)…").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1)));
}
void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
@@ -990,24 +1030,24 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
updateHeadersSyncProgressLabel();
return;
}
- progressBarLabel->setText(tr("Synchronizing with network..."));
+ progressBarLabel->setText(tr("Synchronizing with network…"));
updateHeadersSyncProgressLabel();
break;
case BlockSource::DISK:
if (header) {
- progressBarLabel->setText(tr("Indexing blocks on disk..."));
+ progressBarLabel->setText(tr("Indexing blocks on disk…"));
} else {
- progressBarLabel->setText(tr("Processing blocks on disk..."));
+ progressBarLabel->setText(tr("Processing blocks on disk…"));
}
break;
case BlockSource::REINDEX:
- progressBarLabel->setText(tr("Reindexing blocks on disk..."));
+ progressBarLabel->setText(tr("Reindexing blocks on disk…"));
break;
case BlockSource::NONE:
if (header) {
return;
}
- progressBarLabel->setText(tr("Connecting to peers..."));
+ progressBarLabel->setText(tr("Connecting to peers…"));
break;
}
@@ -1021,7 +1061,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
// Set icon state: spinning if catching up, tick otherwise
if (secs < MAX_BLOCK_TIME_GAP) {
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
- labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
+ labelBlocksIcon->setThemedPixmap(QStringLiteral(":/icons/synced"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
#ifdef ENABLE_WALLET
if(walletFrame)
@@ -1044,12 +1084,12 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
progressBar->setVisible(true);
- tooltip = tr("Catching up...") + QString("<br>") + tooltip;
+ tooltip = tr("Catching up…") + QString("<br>") + tooltip;
if(count != prevBlocks)
{
- labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString(
- ":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
- .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
+ labelBlocksIcon->setThemedPixmap(
+ QString(":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')),
+ STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
}
prevBlocks = count;
@@ -1138,7 +1178,17 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
void BitcoinGUI::changeEvent(QEvent *e)
{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview")));
+ sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send")));
+ receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses")));
+ historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history")));
+ }
+#endif
+
QMainWindow::changeEvent(e);
+
#ifndef Q_OS_MAC // Ignored on Mac
if(e->type() == QEvent::WindowStateChange)
{
@@ -1256,7 +1306,7 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
{
- labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
labelWalletHDStatusIcon->show();
// eventually disable the QLabel to set its opacity to 50%
@@ -1275,7 +1325,7 @@ void BitcoinGUI::setEncryptionStatus(int status)
break;
case WalletModel::Unlocked:
labelWalletEncryptionIcon->show();
- labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_open"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
@@ -1283,7 +1333,7 @@ void BitcoinGUI::setEncryptionStatus(int status)
break;
case WalletModel::Locked:
labelWalletEncryptionIcon->show();
- labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+ labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_closed"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
@@ -1315,7 +1365,7 @@ void BitcoinGUI::updateProxyIcon()
if (proxy_enabled) {
if (!GUIUtil::HasPixmap(labelProxyIcon)) {
QString ip_port_q = QString::fromStdString(ip_port);
- labelProxyIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/proxy").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
+ labelProxyIcon->setThemedPixmap((":/icons/proxy"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q));
} else {
labelProxyIcon->show();
@@ -1375,7 +1425,6 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress)
progressDialog = new QProgressDialog(title, QString(), 0, 100);
GUIUtil::PolishProgressDialog(progressDialog);
progressDialog->setWindowModality(Qt::ApplicationModal);
- progressDialog->setMinimumDuration(0);
progressDialog->setAutoClose(false);
progressDialog->setValue(0);
} else if (nProgress == 100) {
@@ -1440,9 +1489,10 @@ bool BitcoinGUI::isPrivacyModeActivated() const
return m_mask_values_action->isChecked();
}
-UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
- optionsModel(nullptr),
- menu(nullptr)
+UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle)
+ : optionsModel(nullptr),
+ menu(nullptr),
+ m_platform_style{platformStyle}
{
createContextMenu();
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
@@ -1455,7 +1505,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl
}
setMinimumSize(max_width, 0);
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- setStyleSheet(QString("QLabel { color : %1 }").arg(platformStyle->SingleColor().name()));
+ setStyleSheet(QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name()));
}
/** So that it responds to button clicks */
@@ -1464,15 +1514,24 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
onDisplayUnitsClicked(event->pos());
}
+void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name());
+ if (style != styleSheet()) {
+ setStyleSheet(style);
+ }
+ }
+#endif
+}
+
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void UnitDisplayStatusBarControl::createContextMenu()
{
menu = new QMenu(this);
- for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
- {
- QAction *menuAction = new QAction(QString(BitcoinUnits::longName(u)), this);
- menuAction->setData(QVariant(u));
- menu->addAction(menuAction);
+ for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
+ menu->addAction(BitcoinUnits::longName(u))->setData(QVariant(u));
}
connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection);
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 147f19e68d..c83cd446a0 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -9,6 +9,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <qt/guiutil.h>
#include <qt/optionsdialog.h>
#include <amount.h>
@@ -16,6 +17,7 @@
#include <QLabel>
#include <QMainWindow>
#include <QMap>
+#include <QMenu>
#include <QPoint>
#include <QSystemTrayIcon>
@@ -50,7 +52,6 @@ QT_BEGIN_NAMESPACE
class QAction;
class QComboBox;
class QDateTime;
-class QMenu;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
@@ -121,8 +122,8 @@ private:
WalletFrame* walletFrame = nullptr;
UnitDisplayStatusBarControl* unitDisplayControl = nullptr;
- QLabel* labelWalletEncryptionIcon = nullptr;
- QLabel* labelWalletHDStatusIcon = nullptr;
+ GUIUtil::ThemedLabel* labelWalletEncryptionIcon = nullptr;
+ GUIUtil::ThemedLabel* labelWalletHDStatusIcon = nullptr;
GUIUtil::ClickableLabel* labelProxyIcon = nullptr;
GUIUtil::ClickableLabel* connectionsControl = nullptr;
GUIUtil::ClickableLabel* labelBlocksIcon = nullptr;
@@ -174,6 +175,8 @@ private:
HelpMessageDialog* helpMessageDialog = nullptr;
ModalOverlay* modalOverlay = nullptr;
+ QMenu* m_network_context_menu = new QMenu(this);
+
#ifdef Q_OS_MAC
CAppNapInhibitor* m_app_nap_inhibitor = nullptr;
#endif
@@ -221,7 +224,7 @@ public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set network state shown in the UI */
- void setNetworkActive(bool networkActive);
+ void setNetworkActive(bool network_active);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
@@ -333,10 +336,12 @@ public:
protected:
/** So that it responds to left-button clicks */
void mousePressEvent(QMouseEvent *event) override;
+ void changeEvent(QEvent* e) override;
private:
OptionsModel *optionsModel;
QMenu* menu;
+ const PlatformStyle* m_platform_style;
/** Shows context menu with Display Unit options by the mouse coordinates */
void onDisplayUnitsClicked(const QPoint& point);
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 27e512d075..bff253e58a 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -17,13 +17,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"-maxtxfee is set very high! Fees this large could be paid on a single "
"transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Cannot downgrade wallet from version %i to version %i. Wallet version "
+"unchanged."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. %s is probably already running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot provide specific connections and have addrman find outgoing "
"connections at the same."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Cannot upgrade a non HD split wallet without upgrading to support pre split "
-"keypool. Please use version 169900 or no version specified."),
+"Cannot upgrade a non HD split wallet from version %i to version %i without "
+"upgrading to support pre-split keypool. Please use version %i or no version "
+"specified."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Distributed under the MIT software license, see the accompanying file %s or "
"%s"),
@@ -31,17 +35,35 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error reading %s! All keys read correctly, but transaction data or address "
"book entries might be missing or incorrect."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Error: Dumpfile version is not supported. This version of bitcoin-wallet "
+"only supports version 1 dumpfiles. Got dumpfile with version %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -"
"fallbackfee."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"File %s already exists. If you are sure this is what you want, move it out "
+"of the way first."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay "
"fee of %s to prevent stuck transactions)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"More than one onion bind address is provided. Using %s for the automatically "
"created Tor onion service."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"No dump file provided. To use createfromdump, -dumpfile=<filename> must be "
+"provided."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"No dump file provided. To use dump, -dumpfile=<filename> must be provided."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
+"No wallet file format provided. To use createfromdump, -format=<format> must "
+"be provided."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Please check that your computer's date and time are correct! If your clock "
"is wrong, %s will not work properly."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -53,12 +75,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Prune: last wallet synchronisation goes beyond pruned data. You need to -"
"reindex (download the whole blockchain again in case of pruned node)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet "
-"schema version: %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
-"SQLiteDatabase: Failed to prepare the statement to fetch the application id: "
-"%s"),
-QT_TRANSLATE_NOOP("bitcoin-core", ""
"SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is "
"supported"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -93,23 +109,25 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to replay blocks. You will need to rebuild the database using -"
"reindex-chainstate."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Unable to rewind the database to a pre-fork state. You will need to "
-"redownload the blockchain"),
+"Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or "
+"\"sqlite\"."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Warning: Private keys detected in wallet {%s} with disabled private keys"),
+"Warning: Dumpfile wallet format \"%s\" does not match command line specified "
+"format \"%s\"."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Warning: The network does not appear to fully agree! Some miners appear to "
-"be experiencing issues."),
+"Warning: Private keys detected in wallet {%s} with disabled private keys"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: We do not appear to fully agree with our peers! You may need to "
"upgrade, or other nodes may need to upgrade."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Witness data for blocks after height %d requires validation. Please restart "
+"with -reindex."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"You need to rebuild the database using -reindex to go back to unpruned "
"mode. This will redownload the entire blockchain"),
QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"),
QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"),
QT_TRANSLATE_NOOP("bitcoin-core", "A fatal internal error occurred, see debug.log for details"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -%s address: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot set -peerblockfilters without -blockfilterindex."),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write to data directory '%s'; check permissions."),
@@ -122,6 +140,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Could not parse asmap file %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Disk space is too low!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Do you want to rebuild the block database now?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Dump file %s does not exist."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error creating %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environment %s!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"),
@@ -131,18 +151,27 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer versi
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error reading next record from wallet database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error upgrading chainstate database"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Couldn't create cursor into database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low for %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Dumpfile checksum does not match. Computed %s, expected %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got key that was not hex: %s"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Got value that was not hex: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Keypool ran out, please call keypoolrefill first"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Missing checksum"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to parse version %u as a uint32_t"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unable to write record to new wallet"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to rescan the wallet during initialization"),
QT_TRANSLATE_NOOP("bitcoin-core", "Failed to verify database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Fee rate (%s) is lower than the minimum fee rate setting (%s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Ignoring duplicate -wallet %s."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Importing…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -i2psam address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address or hostname: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid P2P permission: '%s'"),
@@ -151,24 +180,21 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -discardfee=<amount>: '%s'
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Loading P2P addresses…"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist…"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index…"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Need to specify a port with -whitebind: '%s'"),
QT_TRANSLATE_NOOP("bitcoin-core", "No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."),
QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune cannot be configured with a negative value."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -blockfilterindex."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -coinstatsindex."),
QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -txindex."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Pruning blockstore..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Pruning blockstore…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Rewinding blocks..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks…"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning…"),
QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to execute statement to verify database: %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to fetch the application id: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to prepare statement to verify database: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to read database verification error: %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Unexpected application id. Expected %u, got %u"),
@@ -178,9 +204,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" does not exist"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is a relative path"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is not a directory"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specified blocks directory \"%s\" does not exist."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Starting network threads..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Starting network threads…"),
QT_TRANSLATE_NOOP("bitcoin-core", "The source code is available from %s."),
-QT_TRANSLATE_NOOP("bitcoin-core", "The specified config file %s does not exist\n"),
+QT_TRANSLATE_NOOP("bitcoin-core", "The specified config file %s does not exist"),
QT_TRANSLATE_NOOP("bitcoin-core", "The transaction amount is too small to pay the fee"),
QT_TRANSLATE_NOOP("bitcoin-core", "The wallet will avoid paying less than the minimum relay fee."),
QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."),
@@ -188,7 +214,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "This is the minimum transaction fee you pay o
QT_TRANSLATE_NOOP("bitcoin-core", "This is the transaction fee you will pay if you send a transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee and change calculation failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction has too long of a mempool chain"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction must have at least one recipient"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"),
@@ -197,6 +222,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to create the PID file '%s': %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Unable to open %s for writing"),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unknown address type '%s'"),
@@ -206,8 +232,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading txindex database"),
QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
-QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks…"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)…"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: unknown new rules activated (versionbit %i)"),
};
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index b244bc94f2..bb2073b9fe 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -8,6 +8,7 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
+#include <qt/peertablesortproxy.h>
#include <clientversion.h>
#include <interfaces/handler.h>
@@ -38,7 +39,11 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
{
cachedBestHeaderHeight = -1;
cachedBestHeaderTime = -1;
+
peerTableModel = new PeerTableModel(m_node, this);
+ m_peer_table_sort_proxy = new PeerTableSortProxy(this);
+ m_peer_table_sort_proxy->setSourceModel(peerTableModel);
+
banTableModel = new BanTableModel(m_node, this);
QTimer* timer = new QTimer;
@@ -184,6 +189,11 @@ PeerTableModel *ClientModel::getPeerTableModel()
return peerTableModel;
}
+PeerTableSortProxy* ClientModel::peerTableSortProxy()
+{
+ return m_peer_table_sort_proxy;
+}
+
BanTableModel *ClientModel::getBanTableModel()
{
return banTableModel;
@@ -211,12 +221,12 @@ QString ClientModel::formatClientStartupTime() const
QString ClientModel::dataDir() const
{
- return GUIUtil::boostPathToQString(GetDataDir());
+ return GUIUtil::boostPathToQString(gArgs.GetDataDirNet());
}
QString ClientModel::blocksDir() const
{
- return GUIUtil::boostPathToQString(GetBlocksDir());
+ return GUIUtil::boostPathToQString(gArgs.GetBlocksDirPath());
}
void ClientModel::updateBanlist()
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 7ac4cc040b..7a199ef19c 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -17,6 +17,7 @@ class BanTableModel;
class CBlockIndex;
class OptionsModel;
class PeerTableModel;
+class PeerTableSortProxy;
enum class SynchronizationState;
namespace interfaces {
@@ -54,6 +55,7 @@ public:
interfaces::Node& node() const { return m_node; }
OptionsModel *getOptionsModel();
PeerTableModel *getPeerTableModel();
+ PeerTableSortProxy* peerTableSortProxy();
BanTableModel *getBanTableModel();
//! Return number of connections, default is in- and outbound (total)
@@ -96,6 +98,7 @@ private:
std::unique_ptr<interfaces::Handler> m_handler_notify_header_tip;
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
+ PeerTableSortProxy* m_peer_table_sort_proxy{nullptr};
BanTableModel *banTableModel;
//! A thread to interact with m_node asynchronously
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index ca78c96d70..8ae0648141 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -50,32 +50,16 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
{
ui->setupUi(this);
- // context menu actions
- QAction *copyAddressAction = new QAction(tr("Copy address"), this);
- QAction *copyLabelAction = new QAction(tr("Copy label"), this);
- QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
- copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
- lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
- unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
-
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(copyAddressAction);
- contextMenu->addAction(copyLabelAction);
- contextMenu->addAction(copyAmountAction);
- contextMenu->addAction(copyTransactionHashAction);
+ contextMenu->addAction(tr("Copy address"), this, &CoinControlDialog::copyAddress);
+ contextMenu->addAction(tr("Copy label"), this, &CoinControlDialog::copyLabel);
+ contextMenu->addAction(tr("Copy amount"), this, &CoinControlDialog::copyAmount);
+ copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
- contextMenu->addAction(lockAction);
- contextMenu->addAction(unlockAction);
-
- // context menu signals
+ lockAction = contextMenu->addAction(tr("Lock unspent"), this, &CoinControlDialog::lockCoin);
+ unlockAction = contextMenu->addAction(tr("Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
- connect(copyAddressAction, &QAction::triggered, this, &CoinControlDialog::copyAddress);
- connect(copyLabelAction, &QAction::triggered, this, &CoinControlDialog::copyLabel);
- connect(copyAmountAction, &QAction::triggered, this, &CoinControlDialog::copyAmount);
- connect(copyTransactionHashAction, &QAction::triggered, this, &CoinControlDialog::copyTransactionHash);
- connect(lockAction, &QAction::triggered, this, &CoinControlDialog::lockCoin);
- connect(unlockAction, &QAction::triggered, this, &CoinControlDialog::unlockCoin);
// clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
@@ -578,6 +562,15 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
label->setVisible(nChange < 0);
}
+void CoinControlDialog::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ updateView();
+ }
+#endif
+}
+
void CoinControlDialog::updateView()
{
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 6ceb3de61d..3a03341c9e 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -51,6 +51,9 @@ public:
static QList<CAmount> payAmounts;
static bool fSubtractFeeFromAmount;
+protected:
+ void changeEvent(QEvent* e) override;
+
private:
Ui::CoinControlDialog *ui;
CCoinControl& m_coin_control;
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index e45cafe48a..15e0d3fad9 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -470,13 +470,7 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="fontSmallerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontSmallerButton">
<property name="toolTip">
<string>Decrease font size</string>
</property>
@@ -489,26 +483,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="fontBiggerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontBiggerButton">
<property name="toolTip">
<string>Increase font size</string>
</property>
@@ -521,26 +503,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="clearButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="clearButton">
<property name="toolTip">
<string>Clear console</string>
</property>
@@ -554,15 +524,15 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
<property name="shortcut">
<string notr="true">Ctrl+L</string>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
</layout>
@@ -923,9 +893,18 @@
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideMiddle</enum>
+ </property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
@@ -978,6 +957,9 @@
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
@@ -1192,6 +1174,9 @@
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
index f27a4ebe44..a1e94f99e6 100644
--- a/src/qt/forms/intro.ui
+++ b/src/qt/forms/intro.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>674</width>
- <height>415</height>
+ <height>447</height>
</rect>
</property>
<property name="windowTitle">
@@ -211,16 +211,6 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="prune">
- <property name="toolTip">
- <string>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</string>
- </property>
- <property name="text">
- <string></string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QLabel" name="lblExplanation2">
<property name="text">
<string>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</string>
@@ -241,6 +231,47 @@
</widget>
</item>
<item>
+ <layout class="QHBoxLayout" name="pruneOptLayout">
+ <item>
+ <widget class="QCheckBox" name="prune">
+ <property name="text">
+ <string>Limit block chain storage to</string>
+ </property>
+ <property name="toolTip">
+ <string>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="pruneGB">
+ <property name="suffix">
+ <string> GB</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblPruneSuffix">
+ <property name="buddy">
+ <cstring>pruneGB</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
index d2e7ca8f06..dbdeadfc00 100644
--- a/src/qt/forms/modaloverlay.ui
+++ b/src/qt/forms/modaloverlay.ui
@@ -219,7 +219,7 @@ QLabel { color: rgb(40,40,40); }</string>
<item row="0" column="1">
<widget class="QLabel" name="numberOfBlocksLeft">
<property name="text">
- <string>Unknown...</string>
+ <string>Unknown…</string>
</property>
</widget>
</item>
@@ -245,7 +245,7 @@ QLabel { color: rgb(40,40,40); }</string>
</sizepolicy>
</property>
<property name="text">
- <string>Unknown...</string>
+ <string>Unknown…</string>
</property>
</widget>
</item>
@@ -271,16 +271,6 @@ QLabel { color: rgb(40,40,40); }</string>
</property>
</widget>
</item>
- <item>
- <widget class="QProgressBar" name="progressBar">
- <property name="value">
- <number>24</number>
- </property>
- <property name="format">
- <string/>
- </property>
- </widget>
- </item>
</layout>
</item>
<item row="4" column="0">
@@ -299,7 +289,7 @@ QLabel { color: rgb(40,40,40); }</string>
<item row="4" column="1">
<widget class="QLabel" name="progressIncreasePerH">
<property name="text">
- <string>calculating...</string>
+ <string>calculating…</string>
</property>
</widget>
</item>
@@ -319,7 +309,7 @@ QLabel { color: rgb(40,40,40); }</string>
<item row="5" column="1">
<widget class="QLabel" name="expectedTimeLeft">
<property name="text">
- <string>calculating...</string>
+ <string>calculating…</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 6d279540e9..f199e8c1a1 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -738,14 +738,14 @@
<item>
<widget class="QLabel" name="embeddedFont_label_1">
<property name="text">
- <string>111.11111111 BTC</string>
+ <string notr="true">111.11111111 BTC</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="embeddedFont_label_9">
<property name="text">
- <string>909.09090909 BTC</string>
+ <string notr="true">909.09090909 BTC</string>
</property>
</widget>
</item>
@@ -787,14 +787,14 @@
<item>
<widget class="QLabel" name="systemFont_label_1">
<property name="text">
- <string>111.11111111 BTC</string>
+ <string notr="true">111.11111111 BTC</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="systemFont_label_9">
<property name="text">
- <string>909.09090909 BTC</string>
+ <string notr="true">909.09090909 BTC</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/psbtoperationsdialog.ui b/src/qt/forms/psbtoperationsdialog.ui
index c2e2f5035b..caae0dab2a 100644
--- a/src/qt/forms/psbtoperationsdialog.ui
+++ b/src/qt/forms/psbtoperationsdialog.ui
@@ -126,7 +126,7 @@
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
- <string>Save...</string>
+ <string>Save…</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index f6d4723465..7d95a8bc90 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>Request payment to ...</string>
+ <string>Request payment to …</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<property name="sizeConstraint">
@@ -65,7 +65,7 @@
<item row="2" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="uri_content">
<property name="text">
- <string notr="true">bitcoin:BC1...</string>
+ <string notr="true">bitcoin:BC1…</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -97,7 +97,7 @@
<item row="3" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="address_content">
<property name="text">
- <string notr="true">bc1...</string>
+ <string notr="true">bc1…</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
@@ -257,7 +257,7 @@
<item>
<widget class="QPushButton" name="btnSaveAs">
<property name="text">
- <string>&amp;Save Image...</string>
+ <string>&amp;Save Image…</string>
</property>
<property name="autoDefault">
<bool>false</bool>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index cfd4bf33d4..37da6fedda 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -107,7 +107,7 @@
<string notr="true"/>
</property>
<property name="text">
- <string>Inputs...</string>
+ <string>Inputs…</string>
</property>
<property name="autoDefault">
<bool>false</bool>
@@ -738,7 +738,7 @@
<item>
<widget class="QPushButton" name="buttonChooseFee">
<property name="text">
- <string>Choose...</string>
+ <string>Choose…</string>
</property>
</widget>
</item>
@@ -850,7 +850,7 @@
<property name="toolTip">
<string>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
-Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</string>
+Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satoshis per kvB" for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</string>
</property>
<property name="text">
<string>per kilobyte</string>
@@ -991,7 +991,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<item>
<widget class="QLabel" name="labelSmartFee2">
<property name="text">
- <string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
+ <string>(Smart fee not initialized yet. This usually takes a few blocks…)</string>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index b4afdbcc22..393dca8ccd 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -6,6 +6,7 @@
#include <qt/bitcoinaddressvalidator.h>
#include <qt/bitcoinunits.h>
+#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/sendcoinsrecipient.h>
@@ -29,6 +30,7 @@
#include <shlwapi.h>
#endif
+#include <QAbstractButton>
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
@@ -42,6 +44,7 @@
#include <QGuiApplication>
#include <QJsonObject>
#include <QKeyEvent>
+#include <QLatin1String>
#include <QLineEdit>
#include <QList>
#include <QLocale>
@@ -59,6 +62,7 @@
#include <QUrlQuery>
#include <QtGlobal>
+#include <cassert>
#include <chrono>
#if defined(Q_OS_MAC)
@@ -119,6 +123,11 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
}
+void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut)
+{
+ QObject::connect(new QShortcut(shortcut, button), &QShortcut::activated, [button]() { button->animateClick(); });
+}
+
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
// return if URI is not valid or is no bitcoin: URI
@@ -397,7 +406,7 @@ void handleCloseWindowShortcut(QWidget* w)
void openDebugLogfile()
{
- fs::path pathDebug = GetDataDir() / "debug.log";
+ fs::path pathDebug = gArgs.GetDataDirNet() / "debug.log";
/* Open debug.log with the associated application */
if (fs::exists(pathDebug))
@@ -628,8 +637,11 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
void setClipboard(const QString& str)
{
- QApplication::clipboard()->setText(str, QClipboard::Clipboard);
- QApplication::clipboard()->setText(str, QClipboard::Selection);
+ QClipboard* clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Clipboard);
+ if (clipboard->supportsSelection()) {
+ clipboard->setText(str, QClipboard::Selection);
+ }
}
fs::path qstringToBoostPath(const QString &path)
@@ -703,7 +715,7 @@ QString formatServicesStr(quint64 mask)
}
if (strList.size())
- return strList.join(" & ");
+ return strList.join(", ");
else
return QObject::tr("None");
}
@@ -781,6 +793,40 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m
return font_size;
}
+ThemedLabel::ThemedLabel(const PlatformStyle* platform_style, QWidget* parent)
+ : QLabel{parent}, m_platform_style{platform_style}
+{
+ assert(m_platform_style);
+}
+
+void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int height)
+{
+ m_image_filename = image_filename;
+ m_pixmap_width = width;
+ m_pixmap_height = height;
+ updateThemedPixmap();
+}
+
+void ThemedLabel::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ updateThemedPixmap();
+ }
+#endif
+ QLabel::changeEvent(e);
+}
+
+void ThemedLabel::updateThemedPixmap()
+{
+ setPixmap(m_platform_style->SingleColorIcon(m_image_filename).pixmap(m_pixmap_width, m_pixmap_height));
+}
+
+ClickableLabel::ClickableLabel(const PlatformStyle* platform_style, QWidget* parent)
+ : ThemedLabel{platform_style, parent}
+{
+}
+
void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
@@ -807,10 +853,12 @@ void PolishProgressDialog(QProgressDialog* dialog)
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
const int margin = TextWidth(dialog->fontMetrics(), ("X"));
dialog->resize(dialog->width() + 2 * margin, dialog->height());
- dialog->show();
-#else
- Q_UNUSED(dialog);
#endif
+ // QProgressDialog estimates the time the operation will take (based on time
+ // for steps), and only shows itself if that estimate is beyond minimumDuration.
+ // The default minimumDuration value is 4 seconds, and it could make users
+ // think that the GUI is frozen.
+ dialog->setMinimumDuration(0);
}
int TextWidth(const QFontMetrics& fm, const QString& text)
@@ -893,4 +941,22 @@ QImage GetImage(const QLabel* label)
#endif
}
+QString MakeHtmlLink(const QString& source, const QString& link)
+{
+ return QString(source).replace(
+ link,
+ QLatin1String("<a href=\"") + link + QLatin1String("\">") + link + QLatin1String("</a>"));
+}
+
+void PrintSlotException(
+ const std::exception* exception,
+ const QObject* sender,
+ const QObject* receiver)
+{
+ std::string description = sender->metaObject()->className();
+ description += "->";
+ description += receiver->metaObject()->className();
+ PrintExceptionContinue(exception, description.c_str());
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 6395ec6abd..06a3b63668 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -9,19 +9,25 @@
#include <fs.h>
#include <net.h>
#include <netaddress.h>
+#include <util/check.h>
+#include <QApplication>
#include <QEvent>
#include <QHeaderView>
#include <QItemDelegate>
#include <QLabel>
#include <QMessageBox>
+#include <QMetaObject>
#include <QObject>
#include <QProgressBar>
#include <QString>
#include <QTableView>
+#include <cassert>
#include <chrono>
+#include <utility>
+class PlatformStyle;
class QValidatedLineEdit;
class SendCoinsRecipient;
@@ -31,10 +37,12 @@ namespace interfaces
}
QT_BEGIN_NAMESPACE
+class QAbstractButton;
class QAbstractItemView;
class QAction;
class QDateTime;
class QFont;
+class QKeySequence;
class QLineEdit;
class QMenu;
class QPoint;
@@ -60,6 +68,14 @@ namespace GUIUtil
// Set up widget for address
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
+ /**
+ * Connects an additional shortcut to a QAbstractButton. Works around the
+ * one shortcut limitation of the button's shortcut property.
+ * @param[in] button QAbstractButton to assign shortcut to
+ * @param[in] shortcut QKeySequence to use as shortcut
+ */
+ void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut);
+
// Parse "bitcoin:" URI into recipient object, return true on successful parsing
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out);
bool parseBitcoinURI(QString uri, SendCoinsRecipient *out);
@@ -216,10 +232,32 @@ namespace GUIUtil
qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);
- class ClickableLabel : public QLabel
+ class ThemedLabel : public QLabel
+ {
+ Q_OBJECT
+
+ public:
+ explicit ThemedLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
+ void setThemedPixmap(const QString& image_filename, int width, int height);
+
+ protected:
+ void changeEvent(QEvent* e) override;
+
+ private:
+ const PlatformStyle* m_platform_style;
+ QString m_image_filename;
+ int m_pixmap_width;
+ int m_pixmap_height;
+ void updateThemedPixmap();
+ };
+
+ class ClickableLabel : public ThemedLabel
{
Q_OBJECT
+ public:
+ explicit ClickableLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
+
Q_SIGNALS:
/** Emitted when the label is clicked. The relative mouse coordinates of the click are
* passed to the signal.
@@ -327,6 +365,58 @@ namespace GUIUtil
QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection);
}
+ /**
+ * Replaces a plain text link with an HTML tagged one.
+ */
+ QString MakeHtmlLink(const QString& source, const QString& link);
+
+ void PrintSlotException(
+ const std::exception* exception,
+ const QObject* sender,
+ const QObject* receiver);
+
+ /**
+ * A drop-in replacement of QObject::connect function
+ * (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that
+ * guaranties that all exceptions are handled within the slot.
+ *
+ * NOTE: This function is incompatible with Qt private signals.
+ */
+ template <typename Sender, typename Signal, typename Receiver, typename Slot>
+ auto ExceptionSafeConnect(
+ Sender sender, Signal signal, Receiver receiver, Slot method,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ return QObject::connect(
+ sender, signal, receiver,
+ [sender, receiver, method](auto&&... args) {
+ bool ok{true};
+ try {
+ (receiver->*method)(std::forward<decltype(args)>(args)...);
+ } catch (const NonFatalCheckError& e) {
+ PrintSlotException(&e, sender, receiver);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleNonFatalException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, QString::fromStdString(e.what())));
+ } catch (const std::exception& e) {
+ PrintSlotException(&e, sender, receiver);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleRunawayException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, QString::fromStdString(e.what())));
+ } catch (...) {
+ PrintSlotException(nullptr, sender, receiver);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleRunawayException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, "Unknown failure occurred."));
+ }
+ assert(ok);
+ },
+ type);
+ }
+
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index aa6b2665fa..a698a96857 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -17,6 +17,7 @@
#include <interfaces/node.h>
#include <util/system.h>
+#include <validation.h>
#include <QFileDialog>
#include <QSettings>
@@ -139,17 +140,26 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si
);
ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
+ const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9);
+ ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits<int>::max());
if (gArgs.GetArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
ui->prune->setChecked(true);
ui->prune->setEnabled(false);
}
- ui->prune->setText(tr("Discard blocks after verification, except most recent %1 GB (prune)").arg(m_prune_target_gb));
+ ui->pruneGB->setValue(m_prune_target_gb);
+ ui->pruneGB->setToolTip(ui->prune->toolTip());
+ ui->lblPruneSuffix->setToolTip(ui->prune->toolTip());
UpdatePruneLabels(ui->prune->isChecked());
connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) {
UpdatePruneLabels(prune_checked);
UpdateFreeSpaceLabel();
});
+ connect(ui->pruneGB, qOverload<int>(&QSpinBox::valueChanged), [this](int prune_GB) {
+ m_prune_target_gb = prune_GB;
+ UpdatePruneLabels(ui->prune->isChecked());
+ UpdateFreeSpaceLabel();
+ });
startThread();
}
@@ -182,7 +192,17 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::showIfNeeded(bool& did_show_intro, bool& prune)
+int64_t Intro::getPruneMiB() const
+{
+ switch (ui->prune->checkState()) {
+ case Qt::Checked:
+ return PruneGBtoMiB(m_prune_target_gb);
+ case Qt::Unchecked: default:
+ return 0;
+ }
+}
+
+bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
{
did_show_intro = false;
@@ -233,7 +253,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, bool& prune)
}
// Additional preferences:
- prune = intro.ui->prune->isChecked();
+ prune_MiB = intro.getPruneMiB();
settings.setValue("strDataDir", dataDir);
settings.setValue("fReset", false);
@@ -278,12 +298,12 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable
void Intro::UpdateFreeSpaceLabel()
{
- QString freeString = tr("%n GB of free space available", "", m_bytes_available / GB_BYTES);
+ QString freeString = tr("%1 GB of free space available").arg(m_bytes_available / GB_BYTES);
if (m_bytes_available < m_required_space_gb * GB_BYTES) {
- freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb);
+ freeString += " " + tr("(of %1 GB needed)").arg(m_required_space_gb);
ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
} else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) {
- freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb);
+ freeString += " " + tr("(%1 GB needed for full chain)").arg(m_required_space_gb);
ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
} else {
ui->freeSpace->setStyleSheet("");
@@ -361,6 +381,13 @@ void Intro::UpdatePruneLabels(bool prune_checked)
storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory.");
}
ui->lblExplanation3->setVisible(prune_checked);
+ ui->pruneGB->setEnabled(prune_checked);
+ static constexpr uint64_t nPowTargetSpacing = 10 * 60; // from chainparams, which we don't have at this stage
+ static constexpr uint32_t expected_block_data_size = 2250000; // includes undo data
+ const uint64_t expected_backup_days = m_prune_target_gb * 1e9 / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing);
+ ui->lblPruneSuffix->setText(
+ //: Explanatory text on the capability of the current prune target.
+ tr("(sufficient to restore backups %n day(s) old)", "", expected_backup_days));
ui->sizeWarningLabel->setText(
tr("%1 will download and store a copy of the Bitcoin block chain.").arg(PACKAGE_NAME) + " " +
storageRequiresMsg.arg(m_required_space_gb) + " " +
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 51f42de7ac..91d57690b7 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -36,6 +36,7 @@ public:
QString getDataDirectory();
void setDataDirectory(const QString &dataDir);
+ int64_t getPruneMiB() const;
/**
* Determine data directory. Let the user choose if the current one doesn't exist.
@@ -44,10 +45,10 @@ public:
* @returns true if a data directory was selected, false if the user cancelled the selection
* dialog.
*
- * @note do NOT call global GetDataDir() before calling this function, this
+ * @note do NOT call global gArgs.GetDataDirNet() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool showIfNeeded(bool& did_show_intro, bool& prune);
+ static bool showIfNeeded(bool& did_show_intro, int64_t& prune_MiB);
Q_SIGNALS:
void requestCheck();
@@ -72,7 +73,7 @@ private:
//! Total required space (in GB) depending on user choice (prune or not prune).
int64_t m_required_space_gb{0};
uint64_t m_bytes_available{0};
- const int64_t m_prune_target_gb;
+ int64_t m_prune_target_gb;
void startThread();
void checkPath(const QString &dataDir);
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index c55cc65b63..b146489ba8 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -95,38 +95,45 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>&amp;Copy Address</source>
+ <location line="+8"/>
+ <source>Copy Address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Copy &amp;Label</source>
+ <source>Copy Label</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>&amp;Edit</source>
+ <source>Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+179"/>
- <source>Export Address List</source>
+ <location line="+3"/>
+ <source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Comma separated file (*.csv)</source>
+ <location line="+161"/>
+ <source>Export Address List</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
- <source>Exporting Failed</source>
+ <location line="+3"/>
+ <source>Comma separated file</source>
+ <extracomment>Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+16"/>
<source>There was an error trying to save the address list to %1. Please try again.</source>
+ <extracomment>An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-3"/>
+ <source>Exporting Failed</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -143,7 +150,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
+ <location line="+38"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -192,16 +199,6 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+3"/>
- <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
- <source>Decrypt wallet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
<source>Change passphrase</source>
<translation type="unfinished"></translation>
</message>
@@ -221,18 +218,18 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
- <location line="+57"/>
+ <location line="+18"/>
+ <location line="+44"/>
<source>Wallet encrypted</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-147"/>
+ <location line="-125"/>
<source>Enter the new passphrase for the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+15"/>
<source>Enter the old passphrase and new passphrase for the wallet.</source>
<translation type="unfinished"></translation>
</message>
@@ -252,7 +249,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+7"/>
<source>Your wallet is now encrypted. </source>
<translation type="unfinished"></translation>
</message>
@@ -262,49 +259,43 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+6"/>
<location line="+8"/>
- <location line="+8"/>
- <location line="+43"/>
+ <location line="+32"/>
<location line="+6"/>
<source>Wallet encryption failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-56"/>
+ <location line="-45"/>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+8"/>
- <location line="+49"/>
+ <location line="+38"/>
<source>The supplied passphrases do not match.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-38"/>
+ <location line="-27"/>
<location line="+6"/>
<source>Wallet unlock failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-5"/>
- <location line="+12"/>
- <location line="+19"/>
+ <location line="+20"/>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-20"/>
- <source>Wallet decryption failed</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+14"/>
+ <location line="-6"/>
<source>Wallet passphrase was successfully changed.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+47"/>
+ <location line="+46"/>
<location line="+33"/>
<source>Warning: The Caps Lock key is on!</source>
<translation type="unfinished"></translation>
@@ -313,7 +304,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>BanTableModel</name>
<message>
- <location filename="../bantablemodel.cpp" line="+86"/>
+ <location filename="../bantablemodel.cpp" line="+85"/>
<source>IP/Netmask</source>
<translation type="unfinished"></translation>
</message>
@@ -324,19 +315,32 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
</context>
<context>
- <name>BitcoinGUI</name>
+ <name>BitcoinApplication</name>
+ <message>
+ <location filename="../bitcoin.cpp" line="+420"/>
+ <source>Runaway exception</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>A fatal error occurred. %1 can no longer continue safely and will quit.</source>
+ <translation type="unfinished"></translation>
+ </message>
<message>
- <location filename="../bitcoingui.cpp" line="+322"/>
- <source>Sign &amp;message...</source>
- <translation>Sign &amp;message...</translation>
+ <location line="+9"/>
+ <source>Internal error</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+669"/>
- <source>Synchronizing with network...</source>
- <translation>Synchronizing with network...</translation>
+ <location line="+1"/>
+ <source>An internal error occurred. %1 will attempt to continue safely. This is an unexpected bug which can be reported as described below.</source>
+ <translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>BitcoinGUI</name>
<message>
- <location line="-747"/>
+ <location filename="../bitcoingui.cpp" line="+245"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -386,82 +390,33 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Show information about Qt</translation>
</message>
<message>
- <location line="+2"/>
- <source>&amp;Options...</source>
- <translation>&amp;Options...</translation>
- </message>
- <message>
- <location line="+1"/>
- <source>Modify configuration options for %1</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+6"/>
- <source>&amp;Encrypt Wallet...</source>
- <translation>&amp;Encrypt Wallet...</translation>
- </message>
- <message>
<location line="+3"/>
- <source>&amp;Backup Wallet...</source>
- <translation>&amp;Backup Wallet...</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>&amp;Change Passphrase...</source>
- <translation>&amp;Change Passphrase...</translation>
- </message>
- <message>
- <location line="+22"/>
- <source>Open &amp;URI...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+11"/>
- <source>Create Wallet...</source>
+ <source>Modify configuration options for %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+46"/>
<source>Create a new wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+210"/>
+ <location line="+209"/>
<source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+351"/>
- <source>Click to disable network activity.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="+358"/>
<source>Network activity disabled.</source>
+ <extracomment>A substring of the tooltip.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Click to enable network activity again.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+27"/>
- <source>Syncing Headers (%1%)...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+57"/>
- <source>Reindexing blocks on disk...</source>
- <translation>Reindexing blocks on disk...</translation>
- </message>
- <message>
- <location line="+315"/>
+ <location line="+426"/>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1065"/>
+ <location line="-1096"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -476,12 +431,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Change the passphrase used for wallet encryption</translation>
</message>
<message>
- <location line="+3"/>
- <source>&amp;Verify message...</source>
- <translation>&amp;Verify message...</translation>
- </message>
- <message>
- <location line="-73"/>
+ <location line="-70"/>
<source>&amp;Send</source>
<translation>&amp;Send</translation>
</message>
@@ -491,7 +441,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Receive</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+46"/>
+ <source>&amp;Options…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>&amp;Show / Hide</source>
<translation>&amp;Show / Hide</translation>
</message>
@@ -501,22 +456,77 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Show or hide the main Window</translation>
</message>
<message>
- <location line="+3"/>
+ <location line="+2"/>
+ <source>&amp;Encrypt Wallet…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Encrypt the private keys that belong to your wallet</source>
<translation>Encrypt the private keys that belong to your wallet</translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+2"/>
+ <source>&amp;Backup Wallet…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>&amp;Change Passphrase…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Sign &amp;message…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
<translation>Sign messages with your Bitcoin addresses to prove you own them</translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+1"/>
+ <source>&amp;Verify message…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
<translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
- <location line="+129"/>
+ <location line="+1"/>
+ <source>&amp;Load PSBT from file…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Load PSBT from clipboard…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Open &amp;URI…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Close Wallet…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Create Wallet…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Close All Wallets…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+97"/>
<source>&amp;File</source>
<translation>&amp;File</translation>
</message>
@@ -536,45 +546,57 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="-281"/>
- <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <location line="+422"/>
+ <source>Syncing Headers (%1%)…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
- <source>Show the list of used sending addresses and labels</source>
+ <location line="+46"/>
+ <source>Synchronizing with network…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Indexing blocks on disk…</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Show the list of used receiving addresses and labels</source>
+ <source>Processing blocks on disk…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
- <source>&amp;Command-line options</source>
+ <location line="+4"/>
+ <source>Reindexing blocks on disk…</source>
<translation type="unfinished"></translation>
</message>
- <message numerus="yes">
- <location line="+556"/>
- <source>%n active connection(s) to Bitcoin network</source>
- <translation>
- <numerusform>%n active connection to Bitcoin network</numerusform>
- <numerusform>%n active connections to Bitcoin network</numerusform>
- </translation>
+ <message>
+ <location line="+6"/>
+ <source>Connecting to peers…</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+80"/>
- <source>Indexing blocks on disk...</source>
+ <location line="-766"/>
+ <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+75"/>
+ <source>Show the list of used sending addresses and labels</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Processing blocks on disk...</source>
+ <source>Show the list of used receiving addresses and labels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>&amp;Command-line options</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+19"/>
+ <location line="+678"/>
<source>Processed %n block(s) of transaction history.</source>
<translation>
<numerusform>Processed %n block of transaction history.</numerusform>
@@ -587,7 +609,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>%1 behind</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+5"/>
+ <source>Catching up…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
<source>Last received block was generated %1 ago.</source>
<translation>Last received block was generated %1 ago.</translation>
</message>
@@ -617,22 +644,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Up to date</translation>
</message>
<message>
- <location line="-695"/>
- <source>&amp;Load PSBT from file...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="-715"/>
<source>Load Partially Signed Bitcoin Transaction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Load PSBT from clipboard...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Load Partially Signed Bitcoin Transaction from clipboard</source>
<translation type="unfinished"></translation>
</message>
@@ -672,22 +689,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Close Wallet...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+4"/>
<source>Close wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Close All Wallets...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+7"/>
<source>Close all wallets</source>
<translation type="unfinished"></translation>
</message>
@@ -737,22 +744,45 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+246"/>
+ <location line="+248"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
+ <message numerus="yes">
+ <location line="+158"/>
+ <source>%n active connection(s) to Bitcoin network.</source>
+ <extracomment>A substring of the tooltip.</extracomment>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Click for more actions.</source>
+ <extracomment>A substring of the tooltip. &quot;More actions&quot; are available via the context menu.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
<message>
- <location line="+249"/>
- <source>Connecting to peers...</source>
+ <location line="+17"/>
+ <source>Show Peers tab</source>
+ <extracomment>A context menu item. The &quot;Peers tab&quot; is an element of the &quot;Node window&quot;.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+37"/>
- <source>Catching up...</source>
- <translation>Catching up...</translation>
+ <location line="+8"/>
+ <source>Disable network activity</source>
+ <extracomment>A context menu item.</extracomment>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+47"/>
+ <location line="+2"/>
+ <source>Enable network activity</source>
+ <extracomment>A context menu item. The network activity was disabled previously.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+157"/>
<source>Error: %1</source>
<translation type="unfinished"></translation>
</message>
@@ -762,7 +792,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+100"/>
+ <location line="+110"/>
<source>Date: %1
</source>
<translation type="unfinished"></translation>
@@ -833,15 +863,10 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location line="+129"/>
+ <location line="+120"/>
<source>Original message:</source>
<translation type="unfinished"></translation>
</message>
- <message>
- <location filename="../bitcoin.cpp" line="+418"/>
- <source>A fatal error occurred. %1 can no longer continue safely and will quit.</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>CoinControlDialog</name>
@@ -931,7 +956,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+54"/>
+ <location filename="../coincontroldialog.cpp" line="+55"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -942,17 +967,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+1"/>
- <location line="+26"/>
+ <location line="+9"/>
<source>Copy amount</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-25"/>
+ <location line="-8"/>
<source>Copy transaction ID</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="+2"/>
<source>Lock unspent</source>
<translation type="unfinished"></translation>
</message>
@@ -962,7 +987,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+4"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -1017,7 +1042,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+47"/>
<location line="+54"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
@@ -1036,8 +1061,8 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>CreateWalletActivity</name>
<message>
- <location filename="../walletcontroller.cpp" line="+241"/>
- <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <location filename="../walletcontroller.cpp" line="+253"/>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1059,12 +1084,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
+ <location line="+11"/>
<source>Wallet Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+13"/>
+ <source>Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
<source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
<translation type="unfinished"></translation>
</message>
@@ -1074,7 +1104,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+26"/>
+ <source>Advanced Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
<source>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source>
<translation type="unfinished"></translation>
</message>
@@ -1084,7 +1119,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+7"/>
<source>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source>
<translation type="unfinished"></translation>
</message>
@@ -1094,7 +1129,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+7"/>
<source>Use descriptors for scriptPubKey management</source>
<translation type="unfinished"></translation>
</message>
@@ -1104,12 +1139,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../createwalletdialog.cpp" line="+19"/>
+ <location filename="../createwalletdialog.cpp" line="+21"/>
<source>Create</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+42"/>
<source>Compiled without sqlite support (required for descriptor wallets)</source>
<translation type="unfinished"></translation>
</message>
@@ -1185,7 +1220,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>FreespaceChecker</name>
<message>
- <location filename="../intro.cpp" line="+72"/>
+ <location filename="../intro.cpp" line="+73"/>
<source>A new data directory will be created.</source>
<translation>A new data directory will be created.</translation>
</message>
@@ -1251,12 +1286,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+32"/>
+ <source>Limit block chain storage to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+7"/>
+ <source> GB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-32"/>
<source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
<translation type="unfinished"></translation>
</message>
@@ -1266,7 +1311,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-170"/>
+ <location line="-160"/>
<source>Use the default data directory</source>
<translation>Use the default data directory</translation>
</message>
@@ -1281,12 +1326,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished">Bitcoin</translation>
</message>
<message>
- <location line="+8"/>
- <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <location line="+162"/>
+ <source>%1 GB of free space available</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+212"/>
+ <location line="+2"/>
+ <source>(of %1 GB needed)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>(%1 GB needed for full chain)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+72"/>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation type="unfinished"></translation>
</message>
@@ -1295,8 +1350,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<source>Approximately %1 GB of data will be stored in this directory.</source>
<translation type="unfinished"></translation>
</message>
+ <message numerus="yes">
+ <location line="+9"/>
+ <source>(sufficient to restore backups %n day(s) old)</source>
+ <extracomment>Explanatory text on the capability of the current prune target.</extracomment>
+ <translation type="unfinished">
+ <numerusform></numerusform>
+ <numerusform></numerusform>
+ </translation>
+ </message>
<message>
- <location line="+4"/>
+ <location line="+2"/>
<source>%1 will download and store a copy of the Bitcoin block chain.</source>
<translation type="unfinished"></translation>
</message>
@@ -1306,7 +1370,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-137"/>
+ <location line="-144"/>
<source>Error: Specified data directory &quot;%1&quot; cannot be created.</source>
<translation type="unfinished"></translation>
</message>
@@ -1315,30 +1379,6 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<source>Error</source>
<translation>Error</translation>
</message>
- <message numerus="yes">
- <location line="+21"/>
- <source>%n GB of free space available</source>
- <translation>
- <numerusform>%n GB of free space available</numerusform>
- <numerusform>%n GB of free space available</numerusform>
- </translation>
- </message>
- <message numerus="yes">
- <location line="+2"/>
- <source>(of %n GB needed)</source>
- <translation>
- <numerusform>(of %n GB needed)</numerusform>
- <numerusform>(of %n GB needed)</numerusform>
- </translation>
- </message>
- <message numerus="yes">
- <location line="+3"/>
- <source>(%n GB needed for full chain)</source>
- <translation type="unfinished">
- <numerusform></numerusform>
- <numerusform></numerusform>
- </translation>
- </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -1365,12 +1405,18 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<message>
<location line="+7"/>
<location line="+26"/>
- <location filename="../modaloverlay.cpp" line="+153"/>
- <source>Unknown...</source>
+ <location filename="../modaloverlay.cpp" line="+152"/>
+ <source>Unknown…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+44"/>
+ <location line="+20"/>
+ <source>calculating…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-13"/>
+ <location line="-77"/>
<source>Last block time</source>
<translation type="unfinished">Last block time</translation>
</message>
@@ -1380,18 +1426,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+34"/>
+ <location line="+24"/>
<source>Progress increase per hour</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
<location line="+20"/>
- <source>calculating...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-7"/>
<source>Estimated time left until synced</source>
<translation type="unfinished"></translation>
</message>
@@ -1406,13 +1446,13 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../modaloverlay.cpp" line="-119"/>
+ <location filename="../modaloverlay.cpp" line="-118"/>
<source>%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+125"/>
- <source>Unknown. Syncing Headers (%1, %2%)...</source>
+ <location line="+124"/>
+ <source>Unknown. Syncing Headers (%1, %2%)…</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1448,7 +1488,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+2"/>
- <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1475,7 +1515,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+72"/>
+ <location line="+22"/>
+ <source>Enabling pruning significantly reduces the disk space required to store transactions. All blocks are still fully validated. Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+50"/>
<source>Size of &amp;database cache</source>
<translation type="unfinished"></translation>
</message>
@@ -1485,7 +1530,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+161"/>
+ <location line="+171"/>
<location line="+187"/>
<source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
<translation type="unfinished"></translation>
@@ -1498,17 +1543,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+146"/>
- <source>Hide the icon from the system tray.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
- <source>&amp;Hide tray icon</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+17"/>
+ <location line="+169"/>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<translation type="unfinished"></translation>
</message>
@@ -1519,7 +1554,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+79"/>
+ <location line="+179"/>
<source>Open the %1 configuration file from the working directory.</source>
<translation type="unfinished"></translation>
</message>
@@ -1539,17 +1574,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Reset Options</translation>
</message>
<message>
- <location line="-532"/>
+ <location line="-645"/>
<source>&amp;Network</source>
<translation>&amp;Network</translation>
</message>
<message>
- <location line="-191"/>
- <source>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="-188"/>
<source>Prune &amp;block storage to</source>
<translation type="unfinished"></translation>
</message>
@@ -1610,6 +1640,16 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+7"/>
+ <source>Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Map port using NA&amp;T-PMP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
<source>Accept connections from outside.</source>
<translation type="unfinished"></translation>
</message>
@@ -1672,7 +1712,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Window</translation>
</message>
<message>
- <location line="+16"/>
+ <location line="+6"/>
+ <source>Show the icon in the system tray.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&amp;Show tray icon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
<source>Show only a tray icon after minimizing the window.</source>
<translation>Show only a tray icon after minimizing the window.</translation>
</message>
@@ -1712,12 +1762,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation>
</message>
<message>
- <location line="-450"/>
+ <location line="-463"/>
<source>Whether to show coin control features or not.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+250"/>
+ <location line="+260"/>
<source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source>
<translation type="unfinished"></translation>
</message>
@@ -1727,12 +1777,27 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+211"/>
+ <location line="+214"/>
<source>&amp;Third party transaction URLs</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+44"/>
+ <location line="+22"/>
+ <source>Monospaced font in the Overview tab:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>embedded &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+49"/>
+ <source>closest matching &quot;%1&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+65"/>
<source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
<translation type="unfinished"></translation>
</message>
@@ -1747,28 +1812,28 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>&amp;Cancel</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+96"/>
+ <location filename="../optionsdialog.cpp" line="+104"/>
<source>default</source>
<translation>default</translation>
</message>
<message>
- <location line="+67"/>
+ <location line="+81"/>
<source>none</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+89"/>
+ <location line="+91"/>
<source>Confirm options reset</source>
<translation>Confirm options reset</translation>
</message>
<message>
<location line="+1"/>
- <location line="+60"/>
+ <location line="+57"/>
<source>Client restart required to activate changes.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-60"/>
+ <location line="-57"/>
<source>Client will be shut down. Do you want to proceed?</source>
<translation type="unfinished"></translation>
</message>
@@ -1793,7 +1858,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+43"/>
+ <location line="+40"/>
<source>This change would require a client restart.</source>
<translation type="unfinished"></translation>
</message>
@@ -1812,12 +1877,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+62"/>
- <location line="+394"/>
+ <location line="+335"/>
<source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
<translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation>
</message>
<message>
- <location line="-141"/>
+ <location line="-127"/>
<source>Watch-only:</source>
<translation type="unfinished"></translation>
</message>
@@ -1827,22 +1892,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+10"/>
<source>Your current spendable balance</source>
<translation>Your current spendable balance</translation>
</message>
<message>
- <location line="+42"/>
+ <location line="+35"/>
<source>Pending:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-242"/>
+ <location line="-200"/>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
<translation>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</translation>
</message>
<message>
- <location line="+114"/>
+ <location line="+100"/>
<source>Immature:</source>
<translation>Immature:</translation>
</message>
@@ -1852,22 +1917,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Mined balance that has not yet matured</translation>
</message>
<message>
- <location line="-181"/>
+ <location line="-150"/>
<source>Balances</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+164"/>
+ <location line="+140"/>
<source>Total:</source>
<translation>Total:</translation>
</message>
<message>
- <location line="+63"/>
+ <location line="+49"/>
<source>Your current total balance</source>
<translation>Your current total balance</translation>
</message>
<message>
- <location line="+95"/>
+ <location line="+74"/>
<source>Your current balance in watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
@@ -1882,22 +1947,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-324"/>
+ <location line="-275"/>
<source>Unconfirmed transactions to watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+52"/>
+ <location line="+38"/>
<source>Mined balance in watch-only addresses that has not yet matured</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+131"/>
+ <location line="+110"/>
<source>Current total balance in watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../overviewpage.cpp" line="+166"/>
+ <location filename="../overviewpage.cpp" line="+193"/>
<source>Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings-&gt;Mask values.</source>
<translation type="unfinished"></translation>
</message>
@@ -1926,7 +1991,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+7"/>
- <source>Save...</source>
+ <source>Save…</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -1985,8 +2050,9 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Partially Signed Transaction (Binary) (*.psbt)</source>
+ <location line="+2"/>
+ <source>Partially Signed Transaction (Binary)</source>
+ <extracomment>Expanded name of the binary PSBT file format. See: BIP 174.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2058,7 +2124,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>PaymentServer</name>
<message>
- <location filename="../paymentserver.cpp" line="+174"/>
+ <location filename="../paymentserver.cpp" line="+173"/>
<source>Payment request error</source>
<translation type="unfinished"></translation>
</message>
@@ -2069,42 +2135,27 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+50"/>
- <location line="+13"/>
+ <location line="+16"/>
<location line="+6"/>
<location line="+7"/>
<source>URI handling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-26"/>
+ <location line="-29"/>
<source>&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
- <location line="+23"/>
- <source>Cannot process payment request because BIP70 is not supported.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-22"/>
- <location line="+23"/>
- <source>Due to widespread security flaws in BIP70 it&apos;s strongly recommended that any merchant instructions to switch wallets be ignored.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-22"/>
+ <location line="+17"/>
<location line="+23"/>
- <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <source>Cannot process payment request because BIP70 is not supported.
+Due to widespread security flaws in BIP70 it&apos;s strongly recommended that any merchant instructions to switch wallets be ignored.
+If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-20"/>
- <source>Invalid payment address %1</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+8"/>
+ <location line="-10"/>
<source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
<translation type="unfinished"></translation>
</message>
@@ -2117,50 +2168,113 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>PeerTableModel</name>
<message>
- <location filename="../peertablemodel.cpp" line="+107"/>
+ <location filename="../peertablemodel.h" line="+101"/>
<source>User Agent</source>
+ <extracomment>Title of Peers Table column which contains the peer&apos;s User Agent string.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Node/Service</source>
+ <location line="-9"/>
+ <source>Ping</source>
+ <extracomment>Title of Peers Table column which indicates the current latency of the connection with the peer.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>NodeId</source>
+ <location line="-12"/>
+ <source>Peer</source>
+ <extracomment>Title of Peers Table column which contains a unique number used to identify a connection.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Ping</source>
+ <location line="+15"/>
+ <source>Sent</source>
+ <extracomment>Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Sent</source>
+ <location line="+3"/>
+ <source>Received</source>
+ <extracomment>Title of Peers Table column which indicates the total amount of network information we have received from the peer.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Received</source>
+ <location line="-15"/>
+ <source>Address</source>
+ <extracomment>Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</extracomment>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+3"/>
+ <source>Type</source>
+ <extracomment>Title of Peers Table column which describes the type of peer connection. The &quot;type&quot; describes why the connection exists.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Network</source>
+ <extracomment>Title of Peers Table column which states the network the peer connected through.</extracomment>
+ <translation type="unfinished">Network</translation>
+ </message>
</context>
<context>
<name>QObject</name>
<message>
- <location filename="../bitcoinunits.cpp" line="+209"/>
+ <location filename="../bitcoinunits.cpp" line="+213"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+108"/>
+ <location filename="../guiutil.cpp" line="+120"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+652"/>
+ <location line="+540"/>
+ <source>Unroutable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Internal</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Inbound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Outbound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Full Relay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Block Relay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Manual</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Feeler</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Address Fetch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+14"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -2176,22 +2290,22 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+2"/>
- <location line="+26"/>
+ <location line="+28"/>
<source>%1 s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-10"/>
+ <location line="-12"/>
<source>None</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+6"/>
<source>N/A</source>
<translation type="unfinished">N/A</translation>
</message>
<message>
- <location line="+0"/>
+ <location line="+1"/>
<source>%1 ms</source>
<translation type="unfinished"></translation>
</message>
@@ -2256,7 +2370,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+2"/>
- <source>%1 KB</source>
+ <source>%1 kB</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2270,7 +2384,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+105"/>
+ <location filename="../bitcoin.cpp" line="+111"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
@@ -2291,11 +2405,11 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+63"/>
- <source>%1 didn&apos;t yet exit safely...</source>
+ <source>%1 didn&apos;t yet exit safely…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../modaloverlay.cpp" line="-36"/>
+ <location filename="../modaloverlay.cpp" line="-35"/>
<source>unknown</source>
<translation type="unfinished"></translation>
</message>
@@ -2304,16 +2418,16 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<name>QRImageWidget</name>
<message>
<location filename="../qrimagewidget.cpp" line="+30"/>
- <source>&amp;Save Image...</source>
+ <source>Save Image…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>&amp;Copy Image</source>
+ <location line="+1"/>
+ <source>Copy Image</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+13"/>
+ <location line="+11"/>
<source>Resulting URI too long, try to reduce the text for label / message.</source>
<translation type="unfinished"></translation>
</message>
@@ -2328,13 +2442,14 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+32"/>
+ <location line="+30"/>
<source>Save QR Code</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>PNG Image (*.png)</source>
+ <location line="+3"/>
+ <source>PNG Image</source>
+ <extracomment>Expanded name of the PNG file format. See https://en.wikipedia.org/wiki/Portable_Network_Graphics</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
@@ -2344,7 +2459,6 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<location filename="../forms/debugwindow.ui" line="+75"/>
<location line="+26"/>
<location line="+26"/>
- <location line="+26"/>
<location line="+29"/>
<location line="+26"/>
<location line="+36"/>
@@ -2353,15 +2467,20 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+710"/>
- <location line="+23"/>
+ <location line="+722"/>
+ <location line="+26"/>
+ <location line="+26"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
+ <location line="+29"/>
+ <location line="+26"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
+ <location line="+26"/>
+ <location line="+26"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
@@ -2371,13 +2490,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<location line="+23"/>
<location line="+23"/>
<location line="+26"/>
- <location filename="../rpcconsole.cpp" line="+1127"/>
- <location line="+8"/>
+ <location filename="../rpcconsole.h" line="+138"/>
<source>N/A</source>
<translation>N/A</translation>
</message>
<message>
- <location line="-1427"/>
+ <location line="-1549"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2393,11 +2511,6 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+56"/>
- <source>Using BerkeleyDB version</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+26"/>
<source>Datadir</source>
<translation type="unfinished"></translation>
</message>
@@ -2423,11 +2536,12 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+29"/>
+ <location line="+922"/>
<source>Network</source>
<translation>Network</translation>
</message>
<message>
- <location line="+7"/>
+ <location line="-915"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
@@ -2473,45 +2587,39 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+80"/>
- <location line="+560"/>
+ <location line="+708"/>
<source>Received</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-480"/>
- <location line="+457"/>
+ <location line="-628"/>
+ <location line="+605"/>
<source>Sent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-416"/>
+ <location line="-564"/>
<source>&amp;Peers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+67"/>
+ <location line="+76"/>
<source>Banned peers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+65"/>
- <location filename="../rpcconsole.cpp" line="-637"/>
- <location line="+766"/>
+ <location line="+68"/>
+ <location filename="../rpcconsole.cpp" line="+1091"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+54"/>
- <source>Direction</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+23"/>
+ <location line="+106"/>
<source>Version</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+69"/>
+ <location line="+124"/>
<source>Starting Block</source>
<translation type="unfinished"></translation>
</message>
@@ -2526,7 +2634,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+233"/>
+ <location line="+285"/>
<source>The mapped Autonomous System used for diversifying peer selection.</source>
<translation type="unfinished"></translation>
</message>
@@ -2536,18 +2644,18 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1394"/>
- <location line="+1066"/>
+ <location line="-1516"/>
+ <location line="+1081"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1140"/>
+ <location line="-1155"/>
<source>Node window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+279"/>
+ <location line="+253"/>
<source>Current block height</source>
<translation type="unfinished"></translation>
</message>
@@ -2567,22 +2675,77 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+546"/>
+ <location line="+558"/>
<source>Permissions</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+92"/>
+ <location line="+23"/>
+ <source>The direction and type of peer connection: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Direction/Type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+72"/>
<source>Services</source>
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+26"/>
+ <source>Whether the peer requested us to relay transactions.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Wants Tx Relay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>High bandwidth BIP152 compact block relay: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>High Bandwidth</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+92"/>
<source>Connection Time</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+23"/>
+ <source>Elapsed time since a novel block passing initial validity checks was received from this peer.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Last Block</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>Elapsed time since a novel transaction accepted into our mempool was received from this peer.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Last Tx</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
<source>Last Send</source>
<translation type="unfinished"></translation>
</message>
@@ -2617,7 +2780,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1140"/>
+ <location line="-1288"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2642,7 +2805,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-416"/>
+ <location filename="../rpcconsole.cpp" line="-201"/>
<source>In:</source>
<translation type="unfinished"></translation>
</message>
@@ -2662,75 +2825,111 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-243"/>
- <source>1 &amp;hour</source>
+ <location filename="../rpcconsole.h" line="-1"/>
+ <source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>1 &amp;day</source>
+ <location line="+0"/>
+ <source>No</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>1 &amp;week</source>
+ <location line="+0"/>
+ <source>To</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>From</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>1 &amp;year</source>
+ <source>Ban for</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-4"/>
- <source>&amp;Disconnect</source>
+ <location line="+38"/>
+ <source>Never</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../rpcconsole.cpp" line="-417"/>
+ <source>Inbound: initiated by peer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>Outbound Full Relay: default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+1"/>
- <location line="+1"/>
- <location line="+1"/>
- <source>Ban for</source>
+ <source>Outbound Block Relay: does not relay transactions or addresses</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
- <source>&amp;Unban</source>
+ <location line="+1"/>
+ <source>Outbound Manual: added using RPC %1 or %2/%3 configuration options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+164"/>
- <source>Welcome to the %1 RPC console.</source>
+ <location line="+4"/>
+ <source>Outbound Feeler: short-lived, for testing addresses</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <source>Outbound Address Fetch: short-lived, for soliciting addresses</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>we selected the peer for high bandwidth relay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Type %1 for an overview of available commands.</source>
+ <source>the peer selected us for high bandwidth relay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>For more information on using this console type %1.</source>
+ <source>no high bandwidth relay selected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Ctrl++</source>
+ <extracomment>Main shortcut to increase the RPC console font size.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.</source>
+ <source>Ctrl+=</source>
+ <extracomment>Secondary shortcut to increase the RPC console font size.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
+ <location line="+4"/>
+ <source>Ctrl+-</source>
+ <extracomment>Main shortcut to decrease the RPC console font size.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Ctrl+_</source>
+ <extracomment>Secondary shortcut to decrease the RPC console font size.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+385"/>
<source>Network activity disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+66"/>
+ <location line="+77"/>
<source>Executing command without any wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -2740,34 +2939,65 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+192"/>
- <source>(node id: %1)</source>
+ <location line="-319"/>
+ <source>Disconnect</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>via %1</source>
+ <location line="+1"/>
+ <source>1 hour</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
<location line="+1"/>
- <source>never</source>
+ <source>1 day</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
- <source>Inbound</source>
+ <location line="+1"/>
+ <source>1 week</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
- <source>Outbound</source>
+ <location line="+1"/>
+ <source>1 year</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
- <location line="+6"/>
+ <location line="+19"/>
+ <source>Unban</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+150"/>
+ <source>Welcome to the %1 RPC console.
+Use up and down arrows to navigate history, and %2 to clear screen.
+Use %3 and %4 to increase or decrease the font size.
+Type %5 for an overview of available commands.
+For more information on using this console, type %6.
+
+%7WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.%8</source>
+ <extracomment>RPC console welcome message. Placeholders %7 and %8 are style tags for the warning content, and they are not space separated from the rest of the text intentionally.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+156"/>
+ <source>Executing…</source>
+ <extracomment>A console message indicating an entered command is currently being executed.</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+118"/>
+ <source>(peer: %1)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>via %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../rpcconsole.h" line="-38"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
@@ -2871,12 +3101,17 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../receivecoinsdialog.cpp" line="+45"/>
+ <location filename="../receivecoinsdialog.cpp" line="+47"/>
<source>Copy URI</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
+ <source>Copy address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Copy label</source>
<translation type="unfinished"></translation>
</message>
@@ -2891,7 +3126,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+131"/>
+ <location line="+125"/>
<source>Could not unlock wallet.</source>
<translation type="unfinished"></translation>
</message>
@@ -2905,7 +3140,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<name>ReceiveRequestDialog</name>
<message>
<location filename="../forms/receiverequestdialog.ui" line="+14"/>
- <source>Request payment to ...</source>
+ <source>Request payment to …</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2945,7 +3180,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
</message>
<message>
<location line="+10"/>
- <source>&amp;Save Image...</source>
+ <source>&amp;Save Image…</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2962,7 +3197,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<context>
<name>RecentRequestsTableModel</name>
<message>
- <location filename="../recentrequeststablemodel.cpp" line="+27"/>
+ <location filename="../recentrequeststablemodel.cpp" line="+30"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -2977,7 +3212,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+39"/>
+ <location line="+41"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -2992,7 +3227,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+42"/>
+ <location line="+43"/>
<source>Requested</source>
<translation type="unfinished"></translation>
</message>
@@ -3001,7 +3236,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+664"/>
+ <location filename="../sendcoinsdialog.cpp" line="+674"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -3011,12 +3246,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
- <source>Inputs...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+10"/>
+ <location line="+30"/>
<source>automatically selected</source>
<translation type="unfinished"></translation>
</message>
@@ -3071,12 +3301,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
- <source>Choose...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+24"/>
+ <location line="+38"/>
<source>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source>
<translation type="unfinished"></translation>
</message>
@@ -3086,14 +3311,7 @@ Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+77"/>
- <source>Specify a custom fee per kB (1,000 bytes) of the transaction&apos;s virtual size.
-
-Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satoshis per kB&quot; for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+5"/>
+ <location line="+82"/>
<source>per kilobyte</source>
<translation type="unfinished"></translation>
</message>
@@ -3113,12 +3331,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+49"/>
- <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+166"/>
+ <location line="+215"/>
<source>Send to multiple recipients at once</source>
<translation>Send to multiple recipients at once</translation>
</message>
@@ -3133,17 +3346,34 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-800"/>
+ <location line="-1033"/>
+ <source>Inputs…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+233"/>
<source>Dust:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+457"/>
+ <location line="+398"/>
+ <source>Choose…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+59"/>
<source>Hide transaction fee settings</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+86"/>
+ <location line="+51"/>
+ <source>Specify a custom fee per kB (1,000 bytes) of the transaction&apos;s virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100 satoshis per kvB&quot; for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+35"/>
<source>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
<translation type="unfinished"></translation>
</message>
@@ -3153,7 +3383,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+131"/>
+ <location line="+105"/>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks…)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+26"/>
<source>Confirmation time target:</source>
<translation type="unfinished"></translation>
</message>
@@ -3188,7 +3423,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-572"/>
+ <location filename="../sendcoinsdialog.cpp" line="-582"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -3223,12 +3458,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+74"/>
+ <location line="+76"/>
<source>%1 (%2 blocks)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+29"/>
<source>Cr&amp;eate Unsigned</source>
<translation type="unfinished"></translation>
</message>
@@ -3263,7 +3498,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+71"/>
+ <location line="+52"/>
+ <source>To review recipient list click &quot;Show Details…&quot;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+19"/>
<source>Create Unsigned</source>
<translation type="unfinished"></translation>
</message>
@@ -3273,8 +3513,9 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Partially Signed Transaction (Binary) (*.psbt)</source>
+ <location line="+2"/>
+ <source>Partially Signed Transaction (Binary)</source>
+ <extracomment>Expanded name of the binary PSBT file format. See: BIP 174.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@@ -3283,7 +3524,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-75"/>
+ <location line="-76"/>
<source>or</source>
<translation type="unfinished"></translation>
</message>
@@ -3318,12 +3559,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>To review recipient list click &quot;Show Details...&quot;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+18"/>
+ <location line="+25"/>
<source>Confirm send coins</source>
<translation type="unfinished"></translation>
</message>
@@ -3338,7 +3574,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+228"/>
+ <location line="+229"/>
<source>Watch-only balance:</source>
<translation type="unfinished"></translation>
</message>
@@ -3391,7 +3627,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</translation>
</message>
<message>
- <location line="+100"/>
+ <location line="+101"/>
<source>Warning: Invalid Bitcoin address</source>
<translation type="unfinished"></translation>
</message>
@@ -3530,7 +3766,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<name>ShutdownWindow</name>
<message>
<location filename="../utilitydialog.cpp" line="+85"/>
- <source>%1 is shutting down...</source>
+ <source>%1 is shutting down…</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -3743,14 +3979,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<name>TrafficGraphWidget</name>
<message>
<location filename="../trafficgraphwidget.cpp" line="+82"/>
- <source>KB/s</source>
+ <source>kB/s</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TransactionDesc</name>
<message numerus="yes">
- <location filename="../transactiondesc.cpp" line="+34"/>
+ <location filename="../transactiondesc.cpp" line="+36"/>
<source>Open for %n more block(s)</source>
<translation>
<numerusform>Open for %n more block</numerusform>
@@ -3768,7 +4004,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+3"/>
<source>0/unconfirmed, %1</source>
<translation type="unfinished"></translation>
</message>
@@ -3783,12 +4019,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+0"/>
+ <location line="-1"/>
<source>abandoned</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+3"/>
<source>%1/unconfirmed</source>
<translation type="unfinished"></translation>
</message>
@@ -3798,7 +4034,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+51"/>
<source>Status</source>
<translation type="unfinished"></translation>
</message>
@@ -3996,7 +4232,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionTableModel</name>
<message>
- <location filename="../transactiontablemodel.cpp" line="+251"/>
+ <location filename="../transactiontablemodel.cpp" line="+260"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -4011,7 +4247,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+58"/>
+ <location line="+60"/>
<source>Open for %n more block(s)</source>
<translation>
<numerusform>Open for %n more block</numerusform>
@@ -4094,7 +4330,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+208"/>
+ <location line="+210"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
</message>
@@ -4132,7 +4368,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TransactionView</name>
<message>
- <location filename="../transactionview.cpp" line="+69"/>
+ <location filename="../transactionview.cpp" line="+70"/>
<location line="+16"/>
<source>All</source>
<translation type="unfinished"></translation>
@@ -4163,12 +4399,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Range...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+11"/>
+ <location line="+12"/>
<source>Received with</source>
<translation type="unfinished"></translation>
</message>
@@ -4203,17 +4434,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+51"/>
+ <location line="+75"/>
<source>Abandon transaction</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="-3"/>
<source>Increase transaction fee</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="-8"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -4243,23 +4474,29 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Edit label</source>
+ <location line="+7"/>
+ <source>Edit address label</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
+ <location line="-6"/>
<source>Show transaction details</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+194"/>
+ <location line="-96"/>
+ <source>Range…</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+276"/>
<source>Export Transaction History</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Comma separated file (*.csv)</source>
+ <location line="+3"/>
+ <source>Comma separated file</source>
+ <extracomment>Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@@ -4331,7 +4568,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
- <location filename="../bitcoingui.cpp" line="+40"/>
+ <location filename="../bitcoingui.cpp" line="+41"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
@@ -4339,7 +4576,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>WalletController</name>
<message>
- <location filename="../walletcontroller.cpp" line="-238"/>
+ <location filename="../walletcontroller.cpp" line="-250"/>
<source>Close wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4382,20 +4619,20 @@ Go to File &gt; Open Wallet to load a wallet.
<context>
<name>WalletModel</name>
<message>
- <location filename="../walletmodel.cpp" line="+214"/>
+ <location filename="../walletmodel.cpp" line="+218"/>
<source>Send Coins</source>
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+282"/>
- <location line="+45"/>
+ <location line="+279"/>
+ <location line="+52"/>
<location line="+13"/>
<location line="+5"/>
<source>Fee bump error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-63"/>
+ <location line="-70"/>
<source>Increasing transaction fee failed</source>
<translation type="unfinished"></translation>
</message>
@@ -4425,7 +4662,12 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+8"/>
+ <source>Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Confirm fee bump</source>
<translation type="unfinished"></translation>
</message>
@@ -4505,8 +4747,9 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Wallet Data (*.dat)</source>
+ <location line="+2"/>
+ <source>Wallet Data</source>
+ <extracomment>Name of the wallet data file format.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
@@ -4538,57 +4781,107 @@ Go to File &gt; Open Wallet to load a wallet.
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+27"/>
- <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <location filename="../bitcoinstrings.cpp" line="+12"/>
+ <source>The %s developers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
- <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <location line="+1"/>
+ <source>%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Cannot downgrade wallet from version %i to version %i. Wallet version unchanged.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+112"/>
- <source>Pruning blockstore...</source>
+ <location line="+3"/>
+ <source>Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+36"/>
- <source>Unable to start HTTP server. See debug log for details.</source>
+ <location line="+4"/>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-188"/>
- <source>The %s developers</source>
+ <location line="+3"/>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <location line="+3"/>
+ <source>Error: Dumpfile format record is incorrect. Got &quot;%s&quot;, expected &quot;format&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+2"/>
- <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <source>Error: Dumpfile identifier record is incorrect. Got &quot;%s&quot;, expected &quot;%s&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <location line="+2"/>
+ <source>Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+3"/>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>File %s already exists. If you are sure this is what you want, move it out of the way first.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
+ <source>No dump file provided. To use createfromdump, -dumpfile=&lt;filename&gt; must be provided.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>No dump file provided. To use dump, -dumpfile=&lt;filename&gt; must be provided.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>No wallet file format provided. To use createfromdump, -format=&lt;format&gt; must be provided.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
<source>Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
<translation type="unfinished"></translation>
</message>
@@ -4598,13 +4891,13 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
- <source>SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet schema version: %s</source>
+ <location line="+3"/>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>SQLiteDatabase: Failed to prepare the statement to fetch the application id: %s</source>
+ <location line="+2"/>
+ <source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -4618,439 +4911,454 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
+ <location line="+5"/>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <location line="+3"/>
+ <source>This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <location line="+3"/>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
- <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <location line="+2"/>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
- <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
+ <source>Transaction needs a change address, but we can&apos;t generate it. Please call keypoolrefill first.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>-maxmempool must be at least %d MB</source>
+ <location line="+3"/>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
- <source>Cannot resolve -%s address: &apos;%s&apos;</source>
+ <source>Unknown wallet file format &quot;%s&quot; provided. Please provide one of &quot;bdb&quot; or &quot;sqlite&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
- <source>Change index out of range</source>
+ <source>Warning: Dumpfile wallet format &quot;%s&quot; does not match command line specified format &quot;%s&quot;.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <location line="+3"/>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Copyright (C) %i-%i</source>
+ <location line="+2"/>
+ <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Corrupted block database detected</source>
- <translation>Corrupted block database detected</translation>
+ <location line="+3"/>
+ <source>Witness data for blocks after height %d requires validation. Please restart with -reindex.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>Could not find asmap file %s</source>
+ <location line="+3"/>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>%s is set very high!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Could not parse asmap file %s</source>
+ <source>-maxmempool must be at least %d MB</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Do you want to rebuild the block database now?</source>
- <translation>Do you want to rebuild the block database now?</translation>
+ <location line="+1"/>
+ <source>A fatal internal error occurred, see debug.log for details</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Error initializing block database</source>
- <translation>Error initializing block database</translation>
+ <location line="+1"/>
+ <source>Cannot resolve -%s address: &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error initializing wallet database environment %s!</source>
- <translation>Error initializing wallet database environment %s!</translation>
+ <source>Cannot set -peerblockfilters without -blockfilterindex.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error loading %s</source>
+ <source>Cannot write to data directory &apos;%s&apos;; check permissions.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <source>Change index out of range</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error loading %s: Wallet corrupted</source>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error loading %s: Wallet requires newer version of %s</source>
+ <source>Copyright (C) %i-%i</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error loading block database</source>
- <translation>Error loading block database</translation>
+ <source>Corrupted block database detected</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error opening block database</source>
- <translation>Error opening block database</translation>
+ <source>Could not find asmap file %s</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Failed to listen on any port. Use -listen=0 if you want this.</source>
- <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation>
+ <location line="+1"/>
+ <source>Could not parse asmap file %s</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Failed to rescan the wallet during initialization</source>
+ <source>Disk space is too low!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Failed to verify database</source>
+ <source>Do you want to rebuild the block database now?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Ignoring duplicate -wallet %s.</source>
+ <location line="+1"/>
+ <source>Done loading</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Importing...</source>
+ <source>Dump file %s does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
- <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
+ <source>Error creating %s</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Initialization sanity check failed. %s is shutting down.</source>
+ <source>Error initializing block database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Invalid P2P permission: &apos;%s&apos;</source>
+ <location line="+1"/>
+ <source>Error initializing wallet database environment %s!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <source>Error loading %s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <source>Error loading %s: Wallet corrupted</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+18"/>
- <source>SQLiteDatabase: Failed to execute statement to verify database: %s</source>
+ <location line="+1"/>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s</source>
+ <source>Error loading block database</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>SQLiteDatabase: Failed to fetch the application id: %s</source>
+ <source>Error opening block database</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
+ <source>Error reading from database, shutting down.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>SQLiteDatabase: Failed to read database verification error: %s</source>
+ <source>Error reading next record from wallet database</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
+ <source>Error upgrading chainstate database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Specified blocks directory &quot;%s&quot; does not exist.</source>
+ <location line="+1"/>
+ <source>Error: Couldn&apos;t create cursor into database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+22"/>
- <source>Unknown address type &apos;%s&apos;</source>
+ <location line="+1"/>
+ <source>Error: Disk space is low for %s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Unknown change type &apos;%s&apos;</source>
+ <source>Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Upgrading txindex database</source>
+ <location line="+1"/>
+ <source>Error: Got key that was not hex: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-53"/>
- <source>Loading P2P addresses...</source>
+ <location line="+1"/>
+ <source>Error: Got value that was not hex: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Loading banlist...</source>
+ <source>Error: Keypool ran out, please call keypoolrefill first</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Not enough file descriptors available.</source>
- <translation>Not enough file descriptors available.</translation>
+ <location line="+1"/>
+ <source>Error: Missing checksum</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Prune cannot be configured with a negative value.</source>
+ <source>Error: Unable to parse version %u as a uint32_t</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Prune mode is incompatible with -txindex.</source>
+ <location line="+1"/>
+ <source>Error: Unable to write record to new wallet</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Replaying blocks...</source>
+ <location line="+1"/>
+ <source>Failed to listen on any port. Use -listen=0 if you want this.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Rewinding blocks...</source>
+ <location line="+1"/>
+ <source>Failed to rescan the wallet during initialization</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
- <source>The source code is available from %s.</source>
+ <location line="+1"/>
+ <source>Failed to verify database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Transaction fee and change calculation failed</source>
+ <location line="+1"/>
+ <source>Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <location line="+1"/>
+ <source>Ignoring duplicate -wallet %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Unable to generate keys</source>
+ <location line="+1"/>
+ <source>Importing…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Unsupported logging category %s=%s.</source>
+ <location line="+1"/>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Upgrading UTXO database</source>
+ <source>Initialization sanity check failed. %s is shutting down.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>User Agent comment (%s) contains unsafe characters.</source>
+ <location line="+1"/>
+ <source>Insufficient funds</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Verifying blocks...</source>
- <translation>Verifying blocks...</translation>
+ <source>Invalid -i2psam address or hostname: &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <location line="+1"/>
+ <source>Invalid -onion address or hostname: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-178"/>
- <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <location line="+1"/>
+ <source>Invalid -proxy address or hostname: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-20"/>
- <source>%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source>
+ <location line="+1"/>
+ <source>Invalid P2P permission: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.</source>
+ <location line="+1"/>
+ <source>Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+14"/>
- <source>Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <location line="+1"/>
+ <source>Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+31"/>
- <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <location line="+1"/>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source>
+ <location line="+1"/>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
- <source>This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
+ <location line="+1"/>
+ <source>Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Transaction needs a change address, but we can&apos;t generate it. Please call keypoolrefill first.</source>
+ <location line="+1"/>
+ <source>Loading P2P addresses…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
- <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <location line="+1"/>
+ <source>Loading banlist…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>A fatal internal error occurred, see debug.log for details</source>
+ <location line="+1"/>
+ <source>Loading block index…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Cannot set -peerblockfilters without -blockfilterindex.</source>
+ <location line="+1"/>
+ <source>Loading wallet…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
- <source>Disk space is too low!</source>
+ <location line="+1"/>
+ <source>Need to specify a port with -whitebind: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Error reading from database, shutting down.</source>
+ <location line="+1"/>
+ <source>No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error upgrading chainstate database</source>
+ <source>Not enough file descriptors available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error: Disk space is low for %s</source>
+ <source>Prune cannot be configured with a negative value.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Error: Keypool ran out, please call keypoolrefill first</source>
+ <source>Prune mode is incompatible with -coinstatsindex.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
+ <location line="+1"/>
+ <source>Prune mode is incompatible with -txindex.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Invalid -onion address or hostname: &apos;%s&apos;</source>
+ <location line="+1"/>
+ <source>Pruning blockstore…</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Invalid -proxy address or hostname: &apos;%s&apos;</source>
+ <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <location line="+1"/>
+ <source>Replaying blocks…</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
+ <source>Rescanning…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Need to specify a port with -whitebind: &apos;%s&apos;</source>
+ <location line="+1"/>
+ <source>SQLiteDatabase: Failed to execute statement to verify database: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
+ <source>SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <location line="+1"/>
+ <source>SQLiteDatabase: Failed to read database verification error: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <location line="+1"/>
+ <source>SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
+ <location line="+1"/>
<source>Section [%s] is not recognized.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Signing transaction failed</source>
- <translation>Signing transaction failed</translation>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
@@ -5068,165 +5376,169 @@ Go to File &gt; Open Wallet to load a wallet.
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>The specified config file %s does not exist
-</source>
+ <location line="+1"/>
+ <source>Specified blocks directory &quot;%s&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>The transaction amount is too small to pay the fee</source>
+ <source>Starting network threads…</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>This is experimental software.</source>
+ <location line="+1"/>
+ <source>The source code is available from %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Transaction amount too small</source>
- <translation>Transaction amount too small</translation>
+ <location line="+1"/>
+ <source>The specified config file %s does not exist</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Transaction too large</source>
- <translation>Transaction too large</translation>
+ <location line="+1"/>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Unable to bind to %s on this computer (bind returned error %s)</source>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Unable to create the PID file &apos;%s&apos;: %s</source>
+ <location line="+1"/>
+ <source>This is experimental software.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Unable to generate initial keys</source>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+3"/>
- <source>Unknown -blockfilterindex value %s.</source>
+ <location line="+1"/>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+9"/>
- <source>Verifying wallet(s)...</source>
+ <location line="+1"/>
+ <source>Transaction amount too small</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <location line="+1"/>
+ <source>Transaction amounts must not be negative</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-196"/>
- <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <location line="+1"/>
+ <source>Transaction has too long of a mempool chain</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+68"/>
- <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <location line="+1"/>
+ <source>Transaction must have at least one recipient</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <location line="+1"/>
+ <source>Transaction too large</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
- <source>%s is set very high!</source>
+ <location line="+1"/>
+ <source>Unable to bind to %s on this computer (bind returned error %s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+72"/>
- <source>Starting network threads...</source>
+ <location line="+1"/>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <location line="+1"/>
+ <source>Unable to create the PID file &apos;%s&apos;: %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <location line="+1"/>
+ <source>Unable to generate initial keys</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <source>Unable to generate keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Transaction amounts must not be negative</source>
+ <location line="+1"/>
+ <source>Unable to open %s for writing</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Transaction has too long of a mempool chain</source>
+ <location line="+1"/>
+ <source>Unable to start HTTP server. See debug log for details.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Transaction must have at least one recipient</source>
+ <source>Unknown -blockfilterindex value %s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+11"/>
- <source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
- <translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
+ <location line="+1"/>
+ <source>Unknown address type &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="-59"/>
- <source>Insufficient funds</source>
- <translation>Insufficient funds</translation>
+ <location line="+1"/>
+ <source>Unknown change type &apos;%s&apos;</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="-110"/>
- <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <location line="+1"/>
+ <source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+63"/>
- <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <location line="+1"/>
+ <source>Unsupported logging category %s=%s.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
- <source>Cannot write to data directory &apos;%s&apos;; check permissions.</source>
+ <location line="+1"/>
+ <source>Upgrading UTXO database</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+41"/>
- <source>Loading block index...</source>
- <translation>Loading block index...</translation>
+ <location line="+1"/>
+ <source>Upgrading txindex database</source>
+ <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
- <source>Loading wallet...</source>
- <translation>Loading wallet...</translation>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="-45"/>
- <source>Cannot downgrade wallet</source>
- <translation>Cannot downgrade wallet</translation>
+ <location line="+1"/>
+ <source>Verifying blocks…</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="+55"/>
- <source>Rescanning...</source>
- <translation>Rescanning...</translation>
+ <location line="+1"/>
+ <source>Verifying wallet(s)…</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location line="-43"/>
- <source>Done loading</source>
- <translation>Done loading</translation>
+ <location line="+1"/>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation type="unfinished"></translation>
</message>
</context>
</TS>
diff --git a/src/qt/locale/bitcoin_en.xlf b/src/qt/locale/bitcoin_en.xlf
new file mode 100644
index 0000000000..ffe7812738
--- /dev/null
+++ b/src/qt/locale/bitcoin_en.xlf
@@ -0,0 +1,5690 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:trolltech="urn:trolltech:names:ts:document:1.0">
+ <file original="../forms/addressbookpage.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="AddressBookPage">
+ <trans-unit id="_msg1">
+ <source xml:space="preserve">Right-click to edit address or label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">37</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg2" approved="yes">
+ <source xml:space="preserve">Create a new address</source>
+ <target xml:space="preserve">Create a new address</target>
+ <context-group purpose="location"><context context-type="linenumber">64</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg3">
+ <source xml:space="preserve">&amp;New</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">67</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg4" approved="yes">
+ <source xml:space="preserve">Copy the currently selected address to the system clipboard</source>
+ <target xml:space="preserve">Copy the currently selected address to the system clipboard</target>
+ <context-group purpose="location"><context context-type="linenumber">81</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg5">
+ <source xml:space="preserve">&amp;Copy</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">84</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg6">
+ <source xml:space="preserve">C&amp;lose</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">151</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg7" approved="yes">
+ <source xml:space="preserve">Delete the currently selected address from the list</source>
+ <target xml:space="preserve">Delete the currently selected address from the list</target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg8">
+ <source xml:space="preserve">Enter address or label to search</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">27</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg9" approved="yes">
+ <source xml:space="preserve">Export the data in the current tab to a file</source>
+ <target xml:space="preserve">Export the data in the current tab to a file</target>
+ <context-group purpose="location"><context context-type="linenumber">128</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg10" approved="yes">
+ <source xml:space="preserve">&amp;Export</source>
+ <target xml:space="preserve">&amp;Export</target>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg11" approved="yes">
+ <source xml:space="preserve">&amp;Delete</source>
+ <target xml:space="preserve">&amp;Delete</target>
+ <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../addressbookpage.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="AddressBookPage">
+ <trans-unit id="_msg12">
+ <source xml:space="preserve">Choose the address to send coins to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">84</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg13">
+ <source xml:space="preserve">Choose the address to receive coins with</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg14">
+ <source xml:space="preserve">C&amp;hoose</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg15">
+ <source xml:space="preserve">Sending addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg16">
+ <source xml:space="preserve">Receiving addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg17">
+ <source xml:space="preserve">These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">104</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg18">
+ <source xml:space="preserve">These are your Bitcoin addresses for receiving payments. Use the &apos;Create new receiving address&apos; button in the receive tab to create new addresses.
+Signing is only possible with addresses of the type &apos;legacy&apos;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">109</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg19">
+ <source xml:space="preserve">Copy Address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">117</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg20">
+ <source xml:space="preserve">Copy Label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">118</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg21">
+ <source xml:space="preserve">Edit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">119</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg22">
+ <source xml:space="preserve">Delete</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg23">
+ <source xml:space="preserve">Export Address List</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg24">
+ <source xml:space="preserve">Comma separated file</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">286</context></context-group>
+ <note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note>
+ </trans-unit>
+ <trans-unit id="_msg25">
+ <source xml:space="preserve">There was an error trying to save the address list to %1. Please try again.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">302</context></context-group>
+ <note annotates="source" from="developer">An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</note>
+ </trans-unit>
+ <trans-unit id="_msg26">
+ <source xml:space="preserve">Exporting Failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">299</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../addresstablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="AddressTableModel">
+ <trans-unit id="_msg27">
+ <source xml:space="preserve">Label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg28">
+ <source xml:space="preserve">Address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg29">
+ <source xml:space="preserve">(no label)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/askpassphrasedialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="AskPassphraseDialog">
+ <trans-unit id="_msg30" approved="yes">
+ <source xml:space="preserve">Passphrase Dialog</source>
+ <target xml:space="preserve">Passphrase Dialog</target>
+ <context-group purpose="location"><context context-type="linenumber">26</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg31" approved="yes">
+ <source xml:space="preserve">Enter passphrase</source>
+ <target xml:space="preserve">Enter passphrase</target>
+ <context-group purpose="location"><context context-type="linenumber">56</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg32" approved="yes">
+ <source xml:space="preserve">New passphrase</source>
+ <target xml:space="preserve">New passphrase</target>
+ <context-group purpose="location"><context context-type="linenumber">70</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg33" approved="yes">
+ <source xml:space="preserve">Repeat new passphrase</source>
+ <target xml:space="preserve">Repeat new passphrase</target>
+ <context-group purpose="location"><context context-type="linenumber">84</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg34">
+ <source xml:space="preserve">Show passphrase</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../askpassphrasedialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="AskPassphraseDialog">
+ <trans-unit id="_msg35">
+ <source xml:space="preserve">Encrypt wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">51</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg36">
+ <source xml:space="preserve">This operation needs your wallet passphrase to unlock the wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">54</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg37">
+ <source xml:space="preserve">Unlock wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">59</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg38">
+ <source xml:space="preserve">Change passphrase</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">62</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg39">
+ <source xml:space="preserve">Confirm wallet encryption</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">110</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg40">
+ <source xml:space="preserve">Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg41">
+ <source xml:space="preserve">Are you sure you wish to encrypt your wallet?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg42">
+ <source xml:space="preserve">Wallet encrypted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">129</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">173</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg43">
+ <source xml:space="preserve">Enter the new passphrase for the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">48</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg44">
+ <source xml:space="preserve">Enter the old passphrase and new passphrase for the wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">63</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg45">
+ <source xml:space="preserve">Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">118</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg46">
+ <source xml:space="preserve">Wallet to be encrypted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg47">
+ <source xml:space="preserve">Your wallet is about to be encrypted. </source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">124</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg48">
+ <source xml:space="preserve">Your wallet is now encrypted. </source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg49">
+ <source xml:space="preserve">IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">133</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg50">
+ <source xml:space="preserve">Wallet encryption failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">147</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">179</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">185</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg51">
+ <source xml:space="preserve">Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">140</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg52">
+ <source xml:space="preserve">The supplied passphrases do not match.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">148</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">186</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg53">
+ <source xml:space="preserve">Wallet unlock failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">159</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">165</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg54">
+ <source xml:space="preserve">The passphrase entered for the wallet decryption was incorrect.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">160</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg55">
+ <source xml:space="preserve">Wallet passphrase was successfully changed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg56">
+ <source xml:space="preserve">Warning: The Caps Lock key is on!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">220</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../bantablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="BanTableModel">
+ <trans-unit id="_msg57">
+ <source xml:space="preserve">IP/Netmask</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg58">
+ <source xml:space="preserve">Banned Until</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../bitcoin.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="BitcoinApplication">
+ <trans-unit id="_msg59">
+ <source xml:space="preserve">Runaway exception</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">420</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg60">
+ <source xml:space="preserve">A fatal error occurred. %1 can no longer continue safely and will quit.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">421</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg61">
+ <source xml:space="preserve">Internal error</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">430</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg62">
+ <source xml:space="preserve">An internal error occurred. %1 will attempt to continue safely. This is an unexpected bug which can be reported as described below.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">431</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="QObject">
+ <trans-unit id="_msg63">
+ <source xml:space="preserve">Error: Specified data directory &quot;%1&quot; does not exist.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">542</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg64">
+ <source xml:space="preserve">Error: Cannot parse configuration file: %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">548</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg65">
+ <source xml:space="preserve">Error: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">563</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg66">
+ <source xml:space="preserve">Error initializing settings: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">572</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg67">
+ <source xml:space="preserve">%1 didn&apos;t yet exit safely…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">635</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../bitcoingui.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="BitcoinGUI">
+ <trans-unit id="_msg68" approved="yes">
+ <source xml:space="preserve">&amp;Overview</source>
+ <target xml:space="preserve">&amp;Overview</target>
+ <context-group purpose="location"><context context-type="linenumber">245</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg69" approved="yes">
+ <source xml:space="preserve">Show general overview of wallet</source>
+ <target xml:space="preserve">Show general overview of wallet</target>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg70" approved="yes">
+ <source xml:space="preserve">&amp;Transactions</source>
+ <target xml:space="preserve">&amp;Transactions</target>
+ <context-group purpose="location"><context context-type="linenumber">274</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg71" approved="yes">
+ <source xml:space="preserve">Browse transaction history</source>
+ <target xml:space="preserve">Browse transaction history</target>
+ <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg72" approved="yes">
+ <source xml:space="preserve">E&amp;xit</source>
+ <target xml:space="preserve">E&amp;xit</target>
+ <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg73" approved="yes">
+ <source xml:space="preserve">Quit application</source>
+ <target xml:space="preserve">Quit application</target>
+ <context-group purpose="location"><context context-type="linenumber">299</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg74">
+ <source xml:space="preserve">&amp;About %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">302</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg75">
+ <source xml:space="preserve">Show information about %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">303</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg76" approved="yes">
+ <source xml:space="preserve">About &amp;Qt</source>
+ <target xml:space="preserve">About &amp;Qt</target>
+ <context-group purpose="location"><context context-type="linenumber">306</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg77" approved="yes">
+ <source xml:space="preserve">Show information about Qt</source>
+ <target xml:space="preserve">Show information about Qt</target>
+ <context-group purpose="location"><context context-type="linenumber">307</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg78">
+ <source xml:space="preserve">Modify configuration options for %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">310</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg79">
+ <source xml:space="preserve">Create a new wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">356</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg80">
+ <source xml:space="preserve">Wallet:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">565</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg81">
+ <source xml:space="preserve">Network activity disabled.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">923</context></context-group>
+ <note annotates="source" from="developer">A substring of the tooltip.</note>
+ </trans-unit>
+ <trans-unit id="_msg82">
+ <source xml:space="preserve">Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1349</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg83" approved="yes">
+ <source xml:space="preserve">Send coins to a Bitcoin address</source>
+ <target xml:space="preserve">Send coins to a Bitcoin address</target>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg84" approved="yes">
+ <source xml:space="preserve">Backup wallet to another location</source>
+ <target xml:space="preserve">Backup wallet to another location</target>
+ <context-group purpose="location"><context context-type="linenumber">320</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg85" approved="yes">
+ <source xml:space="preserve">Change the passphrase used for wallet encryption</source>
+ <target xml:space="preserve">Change the passphrase used for wallet encryption</target>
+ <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg86" approved="yes">
+ <source xml:space="preserve">&amp;Send</source>
+ <target xml:space="preserve">&amp;Send</target>
+ <context-group purpose="location"><context context-type="linenumber">252</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg87" approved="yes">
+ <source xml:space="preserve">&amp;Receive</source>
+ <target xml:space="preserve">&amp;Receive</target>
+ <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg88">
+ <source xml:space="preserve">&amp;Options…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">309</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg89" approved="yes">
+ <source xml:space="preserve">&amp;Show / Hide</source>
+ <target xml:space="preserve">&amp;Show / Hide</target>
+ <context-group purpose="location"><context context-type="linenumber">313</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg90" approved="yes">
+ <source xml:space="preserve">Show or hide the main Window</source>
+ <target xml:space="preserve">Show or hide the main Window</target>
+ <context-group purpose="location"><context context-type="linenumber">314</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg91">
+ <source xml:space="preserve">&amp;Encrypt Wallet…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">316</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg92" approved="yes">
+ <source xml:space="preserve">Encrypt the private keys that belong to your wallet</source>
+ <target xml:space="preserve">Encrypt the private keys that belong to your wallet</target>
+ <context-group purpose="location"><context context-type="linenumber">317</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg93">
+ <source xml:space="preserve">&amp;Backup Wallet…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">319</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg94">
+ <source xml:space="preserve">&amp;Change Passphrase…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">321</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg95">
+ <source xml:space="preserve">Sign &amp;message…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">323</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg96" approved="yes">
+ <source xml:space="preserve">Sign messages with your Bitcoin addresses to prove you own them</source>
+ <target xml:space="preserve">Sign messages with your Bitcoin addresses to prove you own them</target>
+ <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg97">
+ <source xml:space="preserve">&amp;Verify message…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">325</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg98" approved="yes">
+ <source xml:space="preserve">Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <target xml:space="preserve">Verify messages to ensure they were signed with specified Bitcoin addresses</target>
+ <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg99">
+ <source xml:space="preserve">&amp;Load PSBT from file…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">327</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg100">
+ <source xml:space="preserve">Load PSBT from clipboard…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">329</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg101">
+ <source xml:space="preserve">Open &amp;URI…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">343</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg102">
+ <source xml:space="preserve">Close Wallet…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">351</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg103">
+ <source xml:space="preserve">Create Wallet…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">354</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg104">
+ <source xml:space="preserve">Close All Wallets…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">358</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg105" approved="yes">
+ <source xml:space="preserve">&amp;File</source>
+ <target xml:space="preserve">&amp;File</target>
+ <context-group purpose="location"><context context-type="linenumber">455</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg106" approved="yes">
+ <source xml:space="preserve">&amp;Settings</source>
+ <target xml:space="preserve">&amp;Settings</target>
+ <context-group purpose="location"><context context-type="linenumber">473</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg107" approved="yes">
+ <source xml:space="preserve">&amp;Help</source>
+ <target xml:space="preserve">&amp;Help</target>
+ <context-group purpose="location"><context context-type="linenumber">534</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg108" approved="yes">
+ <source xml:space="preserve">Tabs toolbar</source>
+ <target xml:space="preserve">Tabs toolbar</target>
+ <context-group purpose="location"><context context-type="linenumber">545</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg109">
+ <source xml:space="preserve">Syncing Headers (%1%)…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">967</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg110">
+ <source xml:space="preserve">Synchronizing with network…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1013</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg111">
+ <source xml:space="preserve">Indexing blocks on disk…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1018</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg112">
+ <source xml:space="preserve">Processing blocks on disk…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1020</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg113">
+ <source xml:space="preserve">Reindexing blocks on disk…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1024</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg114">
+ <source xml:space="preserve">Connecting to peers…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1030</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg115">
+ <source xml:space="preserve">Request payments (generates QR codes and bitcoin: URIs)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">264</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg116">
+ <source xml:space="preserve">Show the list of used sending addresses and labels</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">339</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg117">
+ <source xml:space="preserve">Show the list of used receiving addresses and labels</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">341</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg118">
+ <source xml:space="preserve">&amp;Command-line options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">361</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">1039</context></context-group>
+ <trans-unit id="_msg119[0]" approved="yes">
+ <source xml:space="preserve">Processed %n block(s) of transaction history.</source>
+ <target xml:space="preserve">Processed %n block of transaction history.</target>
+ </trans-unit>
+ <trans-unit id="_msg119[1]" approved="yes">
+ <source xml:space="preserve">Processed %n block(s) of transaction history.</source>
+ <target xml:space="preserve">Processed %n blocks of transaction history.</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg120" approved="yes">
+ <source xml:space="preserve">%1 behind</source>
+ <target xml:space="preserve">%1 behind</target>
+ <context-group purpose="location"><context context-type="linenumber">1062</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg121">
+ <source xml:space="preserve">Catching up…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1067</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg122" approved="yes">
+ <source xml:space="preserve">Last received block was generated %1 ago.</source>
+ <target xml:space="preserve">Last received block was generated %1 ago.</target>
+ <context-group purpose="location"><context context-type="linenumber">1086</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg123" approved="yes">
+ <source xml:space="preserve">Transactions after this will not yet be visible.</source>
+ <target xml:space="preserve">Transactions after this will not yet be visible.</target>
+ <context-group purpose="location"><context context-type="linenumber">1088</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg124" approved="yes">
+ <source xml:space="preserve">Error</source>
+ <target xml:space="preserve">Error</target>
+ <context-group purpose="location"><context context-type="linenumber">1113</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg125" approved="yes">
+ <source xml:space="preserve">Warning</source>
+ <target xml:space="preserve">Warning</target>
+ <context-group purpose="location"><context context-type="linenumber">1117</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg126" approved="yes">
+ <source xml:space="preserve">Information</source>
+ <target xml:space="preserve">Information</target>
+ <context-group purpose="location"><context context-type="linenumber">1121</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg127" approved="yes">
+ <source xml:space="preserve">Up to date</source>
+ <target xml:space="preserve">Up to date</target>
+ <context-group purpose="location"><context context-type="linenumber">1043</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg128">
+ <source xml:space="preserve">Load Partially Signed Bitcoin Transaction</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">328</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg129">
+ <source xml:space="preserve">Load Partially Signed Bitcoin Transaction from clipboard</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">330</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg130">
+ <source xml:space="preserve">Node window</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">332</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg131">
+ <source xml:space="preserve">Open node debugging and diagnostic console</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">333</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg132">
+ <source xml:space="preserve">&amp;Sending addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg133">
+ <source xml:space="preserve">&amp;Receiving addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">340</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg134">
+ <source xml:space="preserve">Open a bitcoin: URI</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">344</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg135">
+ <source xml:space="preserve">Open Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">346</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg136">
+ <source xml:space="preserve">Open a wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">348</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg137">
+ <source xml:space="preserve">Close wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">352</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg138">
+ <source xml:space="preserve">Close all wallets</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">359</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg139">
+ <source xml:space="preserve">Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">363</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg140">
+ <source xml:space="preserve">&amp;Mask values</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">365</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg141">
+ <source xml:space="preserve">Mask the values in the Overview tab</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">367</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg142">
+ <source xml:space="preserve">default wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">399</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg143">
+ <source xml:space="preserve">No wallets available</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">420</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg144">
+ <source xml:space="preserve">&amp;Window</source>
+ <target xml:space="preserve" state="needs-review-translation">&amp;Window</target>
+ <context-group purpose="location"><context context-type="linenumber">484</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg145">
+ <source xml:space="preserve">Minimize</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">486</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg146">
+ <source xml:space="preserve">Zoom</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">496</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg147">
+ <source xml:space="preserve">Main Window</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">514</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg148">
+ <source xml:space="preserve">%1 client</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">762</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">920</context></context-group>
+ <note annotates="source" from="developer">A substring of the tooltip.</note>
+ <trans-unit id="_msg149[0]">
+ <source xml:space="preserve">%n active connection(s) to Bitcoin network.</source>
+ <target xml:space="preserve"></target>
+ </trans-unit>
+ <trans-unit id="_msg149[1]">
+ <source xml:space="preserve">%n active connection(s) to Bitcoin network.</source>
+ <target xml:space="preserve"></target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg150">
+ <source xml:space="preserve">Click for more actions.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">930</context></context-group>
+ <note annotates="source" from="developer">A substring of the tooltip. &quot;More actions&quot; are available via the context menu.</note>
+ </trans-unit>
+ <trans-unit id="_msg151">
+ <source xml:space="preserve">Show Peers tab</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">947</context></context-group>
+ <note annotates="source" from="developer">A context menu item. The &quot;Peers tab&quot; is an element of the &quot;Node window&quot;.</note>
+ </trans-unit>
+ <trans-unit id="_msg152">
+ <source xml:space="preserve">Disable network activity</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">955</context></context-group>
+ <note annotates="source" from="developer">A context menu item.</note>
+ </trans-unit>
+ <trans-unit id="_msg153">
+ <source xml:space="preserve">Enable network activity</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">957</context></context-group>
+ <note annotates="source" from="developer">A context menu item. The network activity was disabled previously.</note>
+ </trans-unit>
+ <trans-unit id="_msg154">
+ <source xml:space="preserve">Error: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1114</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg155">
+ <source xml:space="preserve">Warning: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1118</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg156">
+ <source xml:space="preserve">Date: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1228</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg157">
+ <source xml:space="preserve">Amount: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1229</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg158">
+ <source xml:space="preserve">Wallet: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1231</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg159">
+ <source xml:space="preserve">Type: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1233</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg160">
+ <source xml:space="preserve">Label: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1235</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg161">
+ <source xml:space="preserve">Address: %1
+</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1237</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg162" approved="yes">
+ <source xml:space="preserve">Sent transaction</source>
+ <target xml:space="preserve">Sent transaction</target>
+ <context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg163" approved="yes">
+ <source xml:space="preserve">Incoming transaction</source>
+ <target xml:space="preserve">Incoming transaction</target>
+ <context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg164">
+ <source xml:space="preserve">HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg165">
+ <source xml:space="preserve">HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg166">
+ <source xml:space="preserve">Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1290</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg167" approved="yes">
+ <source xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
+ <target xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</target>
+ <context-group purpose="location"><context context-type="linenumber">1309</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg168" approved="yes">
+ <source xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
+ <target xml:space="preserve">Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</target>
+ <context-group purpose="location"><context context-type="linenumber">1317</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg169">
+ <source xml:space="preserve">Original message:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1437</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="UnitDisplayStatusBarControl">
+ <trans-unit id="_msg170">
+ <source xml:space="preserve">Unit to show amounts in. Click to select another unit.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1478</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/coincontroldialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="CoinControlDialog">
+ <trans-unit id="_msg171">
+ <source xml:space="preserve">Coin Selection</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg172">
+ <source xml:space="preserve">Quantity:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">48</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg173">
+ <source xml:space="preserve">Bytes:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">77</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg174">
+ <source xml:space="preserve">Amount:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg175">
+ <source xml:space="preserve">Fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">202</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg176">
+ <source xml:space="preserve">Dust:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">154</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg177">
+ <source xml:space="preserve">After Fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">247</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg178">
+ <source xml:space="preserve">Change:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">279</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg179">
+ <source xml:space="preserve">(un)select all</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg180">
+ <source xml:space="preserve">Tree mode</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">351</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg181">
+ <source xml:space="preserve">List mode</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">364</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg182">
+ <source xml:space="preserve">Amount</source>
+ <target xml:space="preserve" state="needs-review-translation">Amount</target>
+ <context-group purpose="location"><context context-type="linenumber">420</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg183">
+ <source xml:space="preserve">Received with label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">425</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg184">
+ <source xml:space="preserve">Received with address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">430</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg185">
+ <source xml:space="preserve">Date</source>
+ <target xml:space="preserve" state="needs-review-translation">Date</target>
+ <context-group purpose="location"><context context-type="linenumber">435</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg186">
+ <source xml:space="preserve">Confirmations</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">440</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg187">
+ <source xml:space="preserve">Confirmed</source>
+ <target xml:space="preserve" state="needs-review-translation">Confirmed</target>
+ <context-group purpose="location"><context context-type="linenumber">443</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../coincontroldialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="CoinControlDialog">
+ <trans-unit id="_msg188">
+ <source xml:space="preserve">Copy address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">55</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg189">
+ <source xml:space="preserve">Copy label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">56</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg190">
+ <source xml:space="preserve">Copy amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">57</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg191">
+ <source xml:space="preserve">Copy transaction ID</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg192">
+ <source xml:space="preserve">Lock unspent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">60</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg193">
+ <source xml:space="preserve">Unlock unspent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">61</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg194">
+ <source xml:space="preserve">Copy quantity</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">65</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg195">
+ <source xml:space="preserve">Copy fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">67</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg196">
+ <source xml:space="preserve">Copy after fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">68</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg197">
+ <source xml:space="preserve">Copy bytes</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg198">
+ <source xml:space="preserve">Copy dust</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">70</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg199">
+ <source xml:space="preserve">Copy change</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">71</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg200">
+ <source xml:space="preserve">(%1 locked)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">373</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg201">
+ <source xml:space="preserve">yes</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">528</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg202">
+ <source xml:space="preserve">no</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">528</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg203">
+ <source xml:space="preserve">This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">542</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg204">
+ <source xml:space="preserve">Can vary +/- %1 satoshi(s) per input.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">547</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg205">
+ <source xml:space="preserve">(no label)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">594</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">648</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg206">
+ <source xml:space="preserve">change from %1 (%2)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">641</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg207">
+ <source xml:space="preserve">(change)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">642</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../walletcontroller.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="CreateWalletActivity">
+ <trans-unit id="_msg208">
+ <source xml:space="preserve">Creating Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg209">
+ <source xml:space="preserve">Create wallet failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">281</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg210">
+ <source xml:space="preserve">Create wallet warning</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="OpenWalletActivity">
+ <trans-unit id="_msg211">
+ <source xml:space="preserve">Open wallet failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg212">
+ <source xml:space="preserve">Open wallet warning</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg213">
+ <source xml:space="preserve">default wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg214">
+ <source xml:space="preserve">Opening Wallet &lt;b&gt;%1&lt;/b&gt;…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">336</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="WalletController">
+ <trans-unit id="_msg215">
+ <source xml:space="preserve">Close wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg216">
+ <source xml:space="preserve">Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg217">
+ <source xml:space="preserve">Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg218">
+ <source xml:space="preserve">Close all wallets</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg219">
+ <source xml:space="preserve">Are you sure you wish to close all wallets?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/createwalletdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="CreateWalletDialog">
+ <trans-unit id="_msg220">
+ <source xml:space="preserve">Create Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg221">
+ <source xml:space="preserve">Wallet Name</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">25</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg222">
+ <source xml:space="preserve">Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">38</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg223">
+ <source xml:space="preserve">Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg224">
+ <source xml:space="preserve">Encrypt Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">50</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg225">
+ <source xml:space="preserve">Advanced Options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">76</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg226">
+ <source xml:space="preserve">Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg227">
+ <source xml:space="preserve">Disable Private Keys</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg228">
+ <source xml:space="preserve">Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg229">
+ <source xml:space="preserve">Make Blank Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg230">
+ <source xml:space="preserve">Use descriptors for scriptPubKey management</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">105</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg231">
+ <source xml:space="preserve">Descriptor Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">108</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../createwalletdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="CreateWalletDialog">
+ <trans-unit id="_msg232">
+ <source xml:space="preserve">Create</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">21</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg233">
+ <source xml:space="preserve">Compiled without sqlite support (required for descriptor wallets)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">63</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/editaddressdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="EditAddressDialog">
+ <trans-unit id="_msg234" approved="yes">
+ <source xml:space="preserve">Edit Address</source>
+ <target xml:space="preserve">Edit Address</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg235" approved="yes">
+ <source xml:space="preserve">&amp;Label</source>
+ <target xml:space="preserve">&amp;Label</target>
+ <context-group purpose="location"><context context-type="linenumber">25</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg236">
+ <source xml:space="preserve">The label associated with this address list entry</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">35</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg237">
+ <source xml:space="preserve">The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">52</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg238" approved="yes">
+ <source xml:space="preserve">&amp;Address</source>
+ <target xml:space="preserve">&amp;Address</target>
+ <context-group purpose="location"><context context-type="linenumber">42</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../editaddressdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="EditAddressDialog">
+ <trans-unit id="_msg239">
+ <source xml:space="preserve">New sending address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">29</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg240">
+ <source xml:space="preserve">Edit receiving address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">32</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg241">
+ <source xml:space="preserve">Edit sending address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">36</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg242">
+ <source xml:space="preserve">The entered address &quot;%1&quot; is not a valid Bitcoin address.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">113</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg243">
+ <source xml:space="preserve">Address &quot;%1&quot; already exists as a receiving address with label &quot;%2&quot; and so cannot be added as a sending address.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">146</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg244">
+ <source xml:space="preserve">The entered address &quot;%1&quot; is already in the address book with label &quot;%2&quot;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">151</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg245">
+ <source xml:space="preserve">Could not unlock wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">123</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg246">
+ <source xml:space="preserve">New key generation failed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">128</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../intro.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="FreespaceChecker">
+ <trans-unit id="_msg247" approved="yes">
+ <source xml:space="preserve">A new data directory will be created.</source>
+ <target xml:space="preserve">A new data directory will be created.</target>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg248" approved="yes">
+ <source xml:space="preserve">name</source>
+ <target xml:space="preserve">name</target>
+ <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg249" approved="yes">
+ <source xml:space="preserve">Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <target xml:space="preserve">Directory already exists. Add %1 if you intend to create a new directory here.</target>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg250" approved="yes">
+ <source xml:space="preserve">Path already exists, and is not a directory.</source>
+ <target xml:space="preserve">Path already exists, and is not a directory.</target>
+ <context-group purpose="location"><context context-type="linenumber">100</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg251" approved="yes">
+ <source xml:space="preserve">Cannot create data directory here.</source>
+ <target xml:space="preserve">Cannot create data directory here.</target>
+ <context-group purpose="location"><context context-type="linenumber">107</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="Intro">
+ <trans-unit id="_msg252">
+ <source xml:space="preserve">Bitcoin</source>
+ <target xml:space="preserve" state="needs-review-translation">Bitcoin</target>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg253">
+ <source xml:space="preserve">%1 GB of free space available</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">301</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg254">
+ <source xml:space="preserve">(of %1 GB needed)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">303</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg255">
+ <source xml:space="preserve">(%1 GB needed for full chain)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">306</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg256">
+ <source xml:space="preserve">At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">378</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg257">
+ <source xml:space="preserve">Approximately %1 GB of data will be stored in this directory.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">381</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ <note annotates="source" from="developer">Explanatory text on the capability of the current prune target.</note>
+ <trans-unit id="_msg258[0]">
+ <source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source>
+ <target xml:space="preserve"></target>
+ </trans-unit>
+ <trans-unit id="_msg258[1]">
+ <source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source>
+ <target xml:space="preserve"></target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg259">
+ <source xml:space="preserve">%1 will download and store a copy of the Bitcoin block chain.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">392</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg260">
+ <source xml:space="preserve">The wallet will also be stored in this directory.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">394</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg261">
+ <source xml:space="preserve">Error: Specified data directory &quot;%1&quot; cannot be created.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">250</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg262" approved="yes">
+ <source xml:space="preserve">Error</source>
+ <target xml:space="preserve">Error</target>
+ <context-group purpose="location"><context context-type="linenumber">280</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../utilitydialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="HelpMessageDialog">
+ <trans-unit id="_msg263">
+ <source xml:space="preserve">version</source>
+ <target xml:space="preserve" state="needs-review-translation">version</target>
+ <context-group purpose="location"><context context-type="linenumber">37</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg264">
+ <source xml:space="preserve">About %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">41</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg265">
+ <source xml:space="preserve">Command-line options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">60</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="ShutdownWindow">
+ <trans-unit id="_msg266">
+ <source xml:space="preserve">%1 is shutting down…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">145</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg267">
+ <source xml:space="preserve">Do not shut down the computer until this window disappears.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">146</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/intro.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="Intro">
+ <trans-unit id="_msg268" approved="yes">
+ <source xml:space="preserve">Welcome</source>
+ <target xml:space="preserve">Welcome</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg269">
+ <source xml:space="preserve">Welcome to %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">23</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg270">
+ <source xml:space="preserve">As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg271">
+ <source xml:space="preserve">When you click OK, %1 will begin to download and process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg272">
+ <source xml:space="preserve">Limit block chain storage to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">238</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg273">
+ <source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg274">
+ <source xml:space="preserve"> GB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">248</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg275">
+ <source xml:space="preserve">This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">216</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg276">
+ <source xml:space="preserve">If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg277" approved="yes">
+ <source xml:space="preserve">Use the default data directory</source>
+ <target xml:space="preserve">Use the default data directory</target>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg278" approved="yes">
+ <source xml:space="preserve">Use a custom data directory:</source>
+ <target xml:space="preserve">Use a custom data directory:</target>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/modaloverlay.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ModalOverlay">
+ <trans-unit id="_msg279">
+ <source xml:space="preserve">Form</source>
+ <target xml:space="preserve" state="needs-review-translation">Form</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg280">
+ <source xml:space="preserve">Recent transactions may not yet be visible, and therefore your wallet&apos;s balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">133</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg281">
+ <source xml:space="preserve">Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg282">
+ <source xml:space="preserve">Number of blocks left</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg283">
+ <source xml:space="preserve">Unknown…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">248</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../modaloverlay.cpp</context><context context-type="linenumber">152</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg284">
+ <source xml:space="preserve">calculating…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">312</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg285">
+ <source xml:space="preserve">Last block time</source>
+ <target xml:space="preserve" state="needs-review-translation">Last block time</target>
+ <context-group purpose="location"><context context-type="linenumber">235</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg286">
+ <source xml:space="preserve">Progress</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">261</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg287">
+ <source xml:space="preserve">Progress increase per hour</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg288">
+ <source xml:space="preserve">Estimated time left until synced</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg289">
+ <source xml:space="preserve">Hide</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">342</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg290">
+ <source xml:space="preserve">Esc</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">345</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../modaloverlay.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ModalOverlay">
+ <trans-unit id="_msg291">
+ <source xml:space="preserve">%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">34</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg292">
+ <source xml:space="preserve">Unknown. Syncing Headers (%1, %2%)…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">158</context></context-group>
+ </trans-unit>
+ </group>
+ <group restype="x-trolltech-linguist-context" resname="QObject">
+ <trans-unit id="_msg293">
+ <source xml:space="preserve">unknown</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">123</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/openuridialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="OpenURIDialog">
+ <trans-unit id="_msg294">
+ <source xml:space="preserve">Open bitcoin URI</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg295">
+ <source xml:space="preserve">URI:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">22</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/optionsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="OptionsDialog">
+ <trans-unit id="_msg296" approved="yes">
+ <source xml:space="preserve">Options</source>
+ <target xml:space="preserve">Options</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg297" approved="yes">
+ <source xml:space="preserve">&amp;Main</source>
+ <target xml:space="preserve">&amp;Main</target>
+ <context-group purpose="location"><context context-type="linenumber">27</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg298">
+ <source xml:space="preserve">Automatically start %1 after logging in to the system.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">33</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg299">
+ <source xml:space="preserve">&amp;Start %1 on system login</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">36</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg300">
+ <source xml:space="preserve">Enabling pruning significantly reduces the disk space required to store transactions. All blocks are still fully validated. Reverting this setting requires re-downloading the entire blockchain.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg301">
+ <source xml:space="preserve">Size of &amp;database cache</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">108</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg302">
+ <source xml:space="preserve">Number of script &amp;verification threads</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">151</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg303">
+ <source xml:space="preserve">IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg304">
+ <source xml:space="preserve">Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">391</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">414</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">437</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg305">
+ <source xml:space="preserve">Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">606</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg306">
+ <source xml:space="preserve">Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">686</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">699</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg307">
+ <source xml:space="preserve">Open the %1 configuration file from the working directory.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">878</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg308">
+ <source xml:space="preserve">Open Configuration File</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">881</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg309" approved="yes">
+ <source xml:space="preserve">Reset all client options to default.</source>
+ <target xml:space="preserve">Reset all client options to default.</target>
+ <context-group purpose="location"><context context-type="linenumber">891</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg310" approved="yes">
+ <source xml:space="preserve">&amp;Reset Options</source>
+ <target xml:space="preserve">&amp;Reset Options</target>
+ <context-group purpose="location"><context context-type="linenumber">894</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg311" approved="yes">
+ <source xml:space="preserve">&amp;Network</source>
+ <target xml:space="preserve">&amp;Network</target>
+ <context-group purpose="location"><context context-type="linenumber">249</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg312">
+ <source xml:space="preserve">Prune &amp;block storage to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">61</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg313">
+ <source xml:space="preserve">GB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">71</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg314">
+ <source xml:space="preserve">Reverting this setting requires re-downloading the entire blockchain.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg315">
+ <source xml:space="preserve">MiB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">124</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg316">
+ <source xml:space="preserve">(0 = auto, &lt;0 = leave that many cores free)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">164</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg317">
+ <source xml:space="preserve">W&amp;allet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">200</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg318">
+ <source xml:space="preserve">Expert</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg319">
+ <source xml:space="preserve">Enable coin &amp;control features</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg320">
+ <source xml:space="preserve">If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg321">
+ <source xml:space="preserve">&amp;Spend unconfirmed change</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">225</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg322" approved="yes">
+ <source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
+ <target xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</target>
+ <context-group purpose="location"><context context-type="linenumber">255</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg323" approved="yes">
+ <source xml:space="preserve">Map port using &amp;UPnP</source>
+ <target xml:space="preserve">Map port using &amp;UPnP</target>
+ <context-group purpose="location"><context context-type="linenumber">258</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg324">
+ <source xml:space="preserve">Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">265</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg325">
+ <source xml:space="preserve">Map port using NA&amp;T-PMP</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg326">
+ <source xml:space="preserve">Accept connections from outside.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg327">
+ <source xml:space="preserve">Allow incomin&amp;g connections</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">278</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg328">
+ <source xml:space="preserve">Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg329">
+ <source xml:space="preserve">&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">288</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg330" approved="yes">
+ <source xml:space="preserve">Proxy &amp;IP:</source>
+ <target xml:space="preserve">Proxy &amp;IP:</target>
+ <context-group purpose="location"><context context-type="linenumber">297</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">484</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg331" approved="yes">
+ <source xml:space="preserve">&amp;Port:</source>
+ <target xml:space="preserve">&amp;Port:</target>
+ <context-group purpose="location"><context context-type="linenumber">329</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">516</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg332" approved="yes">
+ <source xml:space="preserve">Port of the proxy (e.g. 9050)</source>
+ <target xml:space="preserve">Port of the proxy (e.g. 9050)</target>
+ <context-group purpose="location"><context context-type="linenumber">354</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">541</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg333">
+ <source xml:space="preserve">Used for reaching peers via:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">378</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg334">
+ <source xml:space="preserve">IPv4</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">401</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg335">
+ <source xml:space="preserve">IPv6</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">424</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg336">
+ <source xml:space="preserve">Tor</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">447</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg337" approved="yes">
+ <source xml:space="preserve">&amp;Window</source>
+ <target xml:space="preserve">&amp;Window</target>
+ <context-group purpose="location"><context context-type="linenumber">577</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg338">
+ <source xml:space="preserve">Show the icon in the system tray.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">583</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg339">
+ <source xml:space="preserve">&amp;Show tray icon</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">586</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg340" approved="yes">
+ <source xml:space="preserve">Show only a tray icon after minimizing the window.</source>
+ <target xml:space="preserve">Show only a tray icon after minimizing the window.</target>
+ <context-group purpose="location"><context context-type="linenumber">596</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg341" approved="yes">
+ <source xml:space="preserve">&amp;Minimize to the tray instead of the taskbar</source>
+ <target xml:space="preserve">&amp;Minimize to the tray instead of the taskbar</target>
+ <context-group purpose="location"><context context-type="linenumber">599</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg342" approved="yes">
+ <source xml:space="preserve">M&amp;inimize on close</source>
+ <target xml:space="preserve">M&amp;inimize on close</target>
+ <context-group purpose="location"><context context-type="linenumber">609</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg343" approved="yes">
+ <source xml:space="preserve">&amp;Display</source>
+ <target xml:space="preserve">&amp;Display</target>
+ <context-group purpose="location"><context context-type="linenumber">630</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg344" approved="yes">
+ <source xml:space="preserve">User Interface &amp;language:</source>
+ <target xml:space="preserve">User Interface &amp;language:</target>
+ <context-group purpose="location"><context context-type="linenumber">638</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg345">
+ <source xml:space="preserve">The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">651</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg346" approved="yes">
+ <source xml:space="preserve">&amp;Unit to show amounts in:</source>
+ <target xml:space="preserve">&amp;Unit to show amounts in:</target>
+ <context-group purpose="location"><context context-type="linenumber">662</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg347" approved="yes">
+ <source xml:space="preserve">Choose the default subdivision unit to show in the interface and when sending coins.</source>
+ <target xml:space="preserve">Choose the default subdivision unit to show in the interface and when sending coins.</target>
+ <context-group purpose="location"><context context-type="linenumber">675</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg348">
+ <source xml:space="preserve">Whether to show coin control features or not.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">212</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg349">
+ <source xml:space="preserve">Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">472</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg350">
+ <source xml:space="preserve">Use separate SOCKS&amp;5 proxy to reach peers via Tor onion services:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">475</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg351">
+ <source xml:space="preserve">&amp;Third party transaction URLs</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">689</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg352">
+ <source xml:space="preserve">Monospaced font in the Overview tab:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">711</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg353">
+ <source xml:space="preserve">embedded &quot;%1&quot;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">719</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg354">
+ <source xml:space="preserve">closest matching &quot;%1&quot;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">768</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg355">
+ <source xml:space="preserve">Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">833</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg356" approved="yes">
+ <source xml:space="preserve">&amp;OK</source>
+ <target xml:space="preserve">&amp;OK</target>
+ <context-group purpose="location"><context context-type="linenumber">974</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg357" approved="yes">
+ <source xml:space="preserve">&amp;Cancel</source>
+ <target xml:space="preserve">&amp;Cancel</target>
+ <context-group purpose="location"><context context-type="linenumber">987</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../optionsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="OptionsDialog">
+ <trans-unit id="_msg358" approved="yes">
+ <source xml:space="preserve">default</source>
+ <target xml:space="preserve">default</target>
+ <context-group purpose="location"><context context-type="linenumber">104</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg359">
+ <source xml:space="preserve">none</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">185</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg360" approved="yes">
+ <source xml:space="preserve">Confirm options reset</source>
+ <target xml:space="preserve">Confirm options reset</target>
+ <context-group purpose="location"><context context-type="linenumber">276</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg361">
+ <source xml:space="preserve">Client restart required to activate changes.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg362">
+ <source xml:space="preserve">Client will be shut down. Do you want to proceed?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg363">
+ <source xml:space="preserve">Configuration options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg364">
+ <source xml:space="preserve">The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">293</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg365">
+ <source xml:space="preserve">Error</source>
+ <target xml:space="preserve" state="needs-review-translation">Error</target>
+ <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg366">
+ <source xml:space="preserve">The configuration file could not be opened.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">298</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg367">
+ <source xml:space="preserve">This change would require a client restart.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg368" approved="yes">
+ <source xml:space="preserve">The supplied proxy address is invalid.</source>
+ <target xml:space="preserve">The supplied proxy address is invalid.</target>
+ <context-group purpose="location"><context context-type="linenumber">366</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/overviewpage.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="OverviewPage">
+ <trans-unit id="_msg369" approved="yes">
+ <source xml:space="preserve">Form</source>
+ <target xml:space="preserve">Form</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg370" approved="yes">
+ <source xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
+ <target xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</target>
+ <context-group purpose="location"><context context-type="linenumber">76</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">411</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg371">
+ <source xml:space="preserve">Watch-only:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg372">
+ <source xml:space="preserve">Available:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">294</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg373" approved="yes">
+ <source xml:space="preserve">Your current spendable balance</source>
+ <target xml:space="preserve">Your current spendable balance</target>
+ <context-group purpose="location"><context context-type="linenumber">304</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg374">
+ <source xml:space="preserve">Pending:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">339</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg375" approved="yes">
+ <source xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
+ <target xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</target>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg376" approved="yes">
+ <source xml:space="preserve">Immature:</source>
+ <target xml:space="preserve">Immature:</target>
+ <context-group purpose="location"><context context-type="linenumber">239</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg377" approved="yes">
+ <source xml:space="preserve">Mined balance that has not yet matured</source>
+ <target xml:space="preserve">Mined balance that has not yet matured</target>
+ <context-group purpose="location"><context context-type="linenumber">210</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg378">
+ <source xml:space="preserve">Balances</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">60</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg379" approved="yes">
+ <source xml:space="preserve">Total:</source>
+ <target xml:space="preserve">Total:</target>
+ <context-group purpose="location"><context context-type="linenumber">200</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg380" approved="yes">
+ <source xml:space="preserve">Your current total balance</source>
+ <target xml:space="preserve">Your current total balance</target>
+ <context-group purpose="location"><context context-type="linenumber">249</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg381">
+ <source xml:space="preserve">Your current balance in watch-only addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">323</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg382">
+ <source xml:space="preserve">Spendable:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">346</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg383">
+ <source xml:space="preserve">Recent transactions</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">395</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg384">
+ <source xml:space="preserve">Unconfirmed transactions to watch-only addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg385">
+ <source xml:space="preserve">Mined balance in watch-only addresses that has not yet matured</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">158</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg386">
+ <source xml:space="preserve">Current total balance in watch-only addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../overviewpage.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="OverviewPage">
+ <trans-unit id="_msg387">
+ <source xml:space="preserve">Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings-&gt;Mask values.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">193</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/psbtoperationsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog">
+ <trans-unit id="_msg388">
+ <source xml:space="preserve">Dialog</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg389">
+ <source xml:space="preserve">Sign Tx</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg390">
+ <source xml:space="preserve">Broadcast Tx</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg391">
+ <source xml:space="preserve">Copy to Clipboard</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg392">
+ <source xml:space="preserve">Save…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">129</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg393">
+ <source xml:space="preserve">Close</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">136</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../psbtoperationsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog">
+ <trans-unit id="_msg394">
+ <source xml:space="preserve">Failed to load transaction: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">55</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg395">
+ <source xml:space="preserve">Failed to sign transaction: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg396">
+ <source xml:space="preserve">Could not sign any more inputs.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">81</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg397">
+ <source xml:space="preserve">Signed %1 inputs, but more signatures are still required.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">83</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg398">
+ <source xml:space="preserve">Signed transaction successfully. Transaction is ready to broadcast.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg399">
+ <source xml:space="preserve">Unknown error processing transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg400">
+ <source xml:space="preserve">Transaction broadcast successfully! Transaction ID: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">108</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg401">
+ <source xml:space="preserve">Transaction broadcast failed: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg402">
+ <source xml:space="preserve">PSBT copied to clipboard.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg403">
+ <source xml:space="preserve">Save Transaction Data</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">143</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg404">
+ <source xml:space="preserve">Partially Signed Transaction (Binary)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">145</context></context-group>
+ <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note>
+ </trans-unit>
+ <trans-unit id="_msg405">
+ <source xml:space="preserve">PSBT saved to disk.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg406">
+ <source xml:space="preserve"> * Sends %1 to %2</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg407">
+ <source xml:space="preserve">Unable to calculate transaction fee or total transaction amount.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg408">
+ <source xml:space="preserve">Pays transaction fee: </source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg409">
+ <source xml:space="preserve">Total Amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">192</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg410">
+ <source xml:space="preserve">or</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">195</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg411">
+ <source xml:space="preserve">Transaction has %1 unsigned inputs.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">201</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg412">
+ <source xml:space="preserve">Transaction is missing some information about inputs.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">243</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg413">
+ <source xml:space="preserve">Transaction still needs signature(s).</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">247</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg414">
+ <source xml:space="preserve">(But this wallet cannot sign transactions.)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">250</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg415">
+ <source xml:space="preserve">(But this wallet does not have the right keys.)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg416">
+ <source xml:space="preserve">Transaction is fully signed and ready for broadcast.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">261</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg417">
+ <source xml:space="preserve">Transaction status is unknown.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">265</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../paymentserver.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="PaymentServer">
+ <trans-unit id="_msg418">
+ <source xml:space="preserve">Payment request error</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">173</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg419">
+ <source xml:space="preserve">Cannot start bitcoin: click-to-pay handler</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg420">
+ <source xml:space="preserve">URI handling</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">224</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">253</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg421">
+ <source xml:space="preserve">&apos;bitcoin://&apos; is not a valid URI. Use &apos;bitcoin:&apos; instead.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">224</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg422">
+ <source xml:space="preserve">Cannot process payment request because BIP70 is not supported.
+Due to widespread security flaws in BIP70 it&apos;s strongly recommended that any merchant instructions to switch wallets be ignored.
+If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">264</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg423">
+ <source xml:space="preserve">URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">254</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg424">
+ <source xml:space="preserve">Payment request file handling</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../peertablemodel.h" datatype="c" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="PeerTableModel">
+ <trans-unit id="_msg425">
+ <source xml:space="preserve">User Agent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which contains the peer&apos;s User Agent string.</note>
+ </trans-unit>
+ <trans-unit id="_msg426">
+ <source xml:space="preserve">Ping</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">92</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which indicates the current latency of the connection with the peer.</note>
+ </trans-unit>
+ <trans-unit id="_msg427">
+ <source xml:space="preserve">Peer</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which contains a unique number used to identify a connection.</note>
+ </trans-unit>
+ <trans-unit id="_msg428">
+ <source xml:space="preserve">Sent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</note>
+ </trans-unit>
+ <trans-unit id="_msg429">
+ <source xml:space="preserve">Received</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have received from the peer.</note>
+ </trans-unit>
+ <trans-unit id="_msg430">
+ <source xml:space="preserve">Address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">83</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</note>
+ </trans-unit>
+ <trans-unit id="_msg431">
+ <source xml:space="preserve">Type</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which describes the type of peer connection. The &quot;type&quot; describes why the connection exists.</note>
+ </trans-unit>
+ <trans-unit id="_msg432">
+ <source xml:space="preserve">Network</source>
+ <target xml:space="preserve" state="needs-review-translation">Network</target>
+ <context-group purpose="location"><context context-type="linenumber">89</context></context-group>
+ <note annotates="source" from="developer">Title of Peers Table column which states the network the peer connected through.</note>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../bitcoinunits.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="QObject">
+ <trans-unit id="_msg433">
+ <source xml:space="preserve">Amount</source>
+ <target xml:space="preserve" state="needs-review-translation">Amount</target>
+ <context-group purpose="location"><context context-type="linenumber">213</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../guiutil.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="QObject">
+ <trans-unit id="_msg434">
+ <source xml:space="preserve">Enter a Bitcoin address (e.g. %1)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg435">
+ <source xml:space="preserve">Unroutable</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">660</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg436">
+ <source xml:space="preserve">Internal</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">666</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg437">
+ <source xml:space="preserve">Inbound</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">676</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg438">
+ <source xml:space="preserve">Outbound</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">676</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg439">
+ <source xml:space="preserve">Full Relay</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">680</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg440">
+ <source xml:space="preserve">Block Relay</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">681</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg441">
+ <source xml:space="preserve">Manual</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">682</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg442">
+ <source xml:space="preserve">Feeler</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">683</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg443">
+ <source xml:space="preserve">Address Fetch</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">684</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg444">
+ <source xml:space="preserve">%1 d</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">698</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg445">
+ <source xml:space="preserve">%1 h</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">700</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg446">
+ <source xml:space="preserve">%1 m</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">702</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg447">
+ <source xml:space="preserve">%1 s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">704</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">732</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg448">
+ <source xml:space="preserve">None</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">720</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg449">
+ <source xml:space="preserve">N/A</source>
+ <target xml:space="preserve" state="needs-review-translation">N/A</target>
+ <context-group purpose="location"><context context-type="linenumber">726</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg450">
+ <source xml:space="preserve">%1 ms</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">727</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">745</context></context-group>
+ <trans-unit id="_msg451[0]" approved="yes">
+ <source xml:space="preserve">%n second(s)</source>
+ <target xml:space="preserve">%n second</target>
+ </trans-unit>
+ <trans-unit id="_msg451[1]" approved="yes">
+ <source xml:space="preserve">%n second(s)</source>
+ <target xml:space="preserve">%n seconds</target>
+ </trans-unit>
+ </group>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">749</context></context-group>
+ <trans-unit id="_msg452[0]" approved="yes">
+ <source xml:space="preserve">%n minute(s)</source>
+ <target xml:space="preserve">%n minute</target>
+ </trans-unit>
+ <trans-unit id="_msg452[1]" approved="yes">
+ <source xml:space="preserve">%n minute(s)</source>
+ <target xml:space="preserve">%n minutes</target>
+ </trans-unit>
+ </group>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">753</context></context-group>
+ <trans-unit id="_msg453[0]">
+ <source xml:space="preserve">%n hour(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n hour</target>
+ </trans-unit>
+ <trans-unit id="_msg453[1]">
+ <source xml:space="preserve">%n hour(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n hours</target>
+ </trans-unit>
+ </group>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">757</context></context-group>
+ <trans-unit id="_msg454[0]">
+ <source xml:space="preserve">%n day(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n day</target>
+ </trans-unit>
+ <trans-unit id="_msg454[1]">
+ <source xml:space="preserve">%n day(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n days</target>
+ </trans-unit>
+ </group>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">761</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">767</context></context-group>
+ <trans-unit id="_msg455[0]">
+ <source xml:space="preserve">%n week(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n week</target>
+ </trans-unit>
+ <trans-unit id="_msg455[1]">
+ <source xml:space="preserve">%n week(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n weeks</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg456">
+ <source xml:space="preserve">%1 and %2</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">767</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">767</context></context-group>
+ <trans-unit id="_msg457[0]">
+ <source xml:space="preserve">%n year(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n year</target>
+ </trans-unit>
+ <trans-unit id="_msg457[1]">
+ <source xml:space="preserve">%n year(s)</source>
+ <target xml:space="preserve" state="needs-review-translation">%n years</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg458">
+ <source xml:space="preserve">%1 B</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">775</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg459">
+ <source xml:space="preserve">%1 kB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">777</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg460">
+ <source xml:space="preserve">%1 MB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">779</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg461">
+ <source xml:space="preserve">%1 GB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">781</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../qrimagewidget.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="QRImageWidget">
+ <trans-unit id="_msg462">
+ <source xml:space="preserve">Save Image…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg463">
+ <source xml:space="preserve">Copy Image</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">31</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg464">
+ <source xml:space="preserve">Resulting URI too long, try to reduce the text for label / message.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">42</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg465">
+ <source xml:space="preserve">Error encoding URI into QR Code.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg466">
+ <source xml:space="preserve">QR code support not available.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg467">
+ <source xml:space="preserve">Save QR Code</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg468">
+ <source xml:space="preserve">PNG Image</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">123</context></context-group>
+ <note annotates="source" from="developer">Expanded name of the PNG file format. See https://en.wikipedia.org/wiki/Portable_Network_Graphics</note>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/debugwindow.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="RPCConsole">
+ <trans-unit id="_msg469" approved="yes">
+ <source xml:space="preserve">N/A</source>
+ <target xml:space="preserve">N/A</target>
+ <context-group purpose="location"><context context-type="linenumber">75</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">101</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">127</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">156</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">182</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">300</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">336</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">359</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1081</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1107</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1133</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1156</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1179</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1202</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1231</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1257</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1280</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1303</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1326</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1349</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1375</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1401</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1424</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1447</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1470</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1493</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1516</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1542</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1565</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1588</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1614</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.h</context><context context-type="linenumber">138</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg470" approved="yes">
+ <source xml:space="preserve">Client version</source>
+ <target xml:space="preserve">Client version</target>
+ <context-group purpose="location"><context context-type="linenumber">65</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg471" approved="yes">
+ <source xml:space="preserve">&amp;Information</source>
+ <target xml:space="preserve">&amp;Information</target>
+ <context-group purpose="location"><context context-type="linenumber">43</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg472">
+ <source xml:space="preserve">General</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg473">
+ <source xml:space="preserve">Datadir</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">114</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg474">
+ <source xml:space="preserve">To specify a non-default location of the data directory use the &apos;%1&apos; option.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">124</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg475">
+ <source xml:space="preserve">Blocksdir</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">143</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg476">
+ <source xml:space="preserve">To specify a non-default location of the blocks directory use the &apos;%1&apos; option.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">153</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg477" approved="yes">
+ <source xml:space="preserve">Startup time</source>
+ <target xml:space="preserve">Startup time</target>
+ <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg478" approved="yes">
+ <source xml:space="preserve">Network</source>
+ <target xml:space="preserve">Network</target>
+ <context-group purpose="location"><context context-type="linenumber">201</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1123</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg479">
+ <source xml:space="preserve">Name</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">208</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg480" approved="yes">
+ <source xml:space="preserve">Number of connections</source>
+ <target xml:space="preserve">Number of connections</target>
+ <context-group purpose="location"><context context-type="linenumber">231</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg481" approved="yes">
+ <source xml:space="preserve">Block chain</source>
+ <target xml:space="preserve">Block chain</target>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg482">
+ <source xml:space="preserve">Memory Pool</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">319</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg483">
+ <source xml:space="preserve">Current number of transactions</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg484">
+ <source xml:space="preserve">Memory usage</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">349</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg485">
+ <source xml:space="preserve">Wallet: </source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">443</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg486">
+ <source xml:space="preserve">(none)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">454</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg487">
+ <source xml:space="preserve">&amp;Reset</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">695</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg488">
+ <source xml:space="preserve">Received</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">775</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1483</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg489">
+ <source xml:space="preserve">Sent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">855</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1460</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg490">
+ <source xml:space="preserve">&amp;Peers</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">896</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg491">
+ <source xml:space="preserve">Banned peers</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">972</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg492">
+ <source xml:space="preserve">Select a peer to view detailed information.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1040</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1091</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg493">
+ <source xml:space="preserve">Version</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1146</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg494">
+ <source xml:space="preserve">Starting Block</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1270</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg495">
+ <source xml:space="preserve">Synced Headers</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1293</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg496">
+ <source xml:space="preserve">Synced Blocks</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1316</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg497">
+ <source xml:space="preserve">The mapped Autonomous System used for diversifying peer selection.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1601</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg498">
+ <source xml:space="preserve">Mapped AS</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1604</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg499">
+ <source xml:space="preserve">User Agent</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1169</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg500">
+ <source xml:space="preserve">Node window</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg501">
+ <source xml:space="preserve">Current block height</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">267</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg502">
+ <source xml:space="preserve">Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">397</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg503">
+ <source xml:space="preserve">Decrease font size</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">481</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg504">
+ <source xml:space="preserve">Increase font size</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">513</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg505">
+ <source xml:space="preserve">Permissions</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1071</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg506">
+ <source xml:space="preserve">The direction and type of peer connection: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1094</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg507">
+ <source xml:space="preserve">Direction/Type</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1097</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg508">
+ <source xml:space="preserve">The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg509">
+ <source xml:space="preserve">Services</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1192</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg510">
+ <source xml:space="preserve">Whether the peer requested us to relay transactions.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1218</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg511">
+ <source xml:space="preserve">Wants Tx Relay</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1221</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg512">
+ <source xml:space="preserve">High bandwidth BIP152 compact block relay: %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1244</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg513">
+ <source xml:space="preserve">High Bandwidth</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1247</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg514">
+ <source xml:space="preserve">Connection Time</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1339</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg515">
+ <source xml:space="preserve">Elapsed time since a novel block passing initial validity checks was received from this peer.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1362</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg516">
+ <source xml:space="preserve">Last Block</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1365</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg517">
+ <source xml:space="preserve">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1388</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg518">
+ <source xml:space="preserve">Last Tx</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1391</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg519">
+ <source xml:space="preserve">Last Send</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1414</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg520">
+ <source xml:space="preserve">Last Receive</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1437</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg521">
+ <source xml:space="preserve">Ping Time</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1506</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg522">
+ <source xml:space="preserve">The duration of a currently outstanding ping.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1529</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg523">
+ <source xml:space="preserve">Ping Wait</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1532</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg524">
+ <source xml:space="preserve">Min Ping</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1555</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg525">
+ <source xml:space="preserve">Time Offset</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1578</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg526" approved="yes">
+ <source xml:space="preserve">Last block time</source>
+ <target xml:space="preserve">Last block time</target>
+ <context-group purpose="location"><context context-type="linenumber">290</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg527" approved="yes">
+ <source xml:space="preserve">&amp;Open</source>
+ <target xml:space="preserve">&amp;Open</target>
+ <context-group purpose="location"><context context-type="linenumber">400</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg528" approved="yes">
+ <source xml:space="preserve">&amp;Console</source>
+ <target xml:space="preserve">&amp;Console</target>
+ <context-group purpose="location"><context context-type="linenumber">426</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg529">
+ <source xml:space="preserve">&amp;Network Traffic</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">643</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg530">
+ <source xml:space="preserve">Totals</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">711</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg531" approved="yes">
+ <source xml:space="preserve">Debug log file</source>
+ <target xml:space="preserve">Debug log file</target>
+ <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg532" approved="yes">
+ <source xml:space="preserve">Clear console</source>
+ <target xml:space="preserve">Clear console</target>
+ <context-group purpose="location"><context context-type="linenumber">545</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../rpcconsole.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="RPCConsole">
+ <trans-unit id="_msg533">
+ <source xml:space="preserve">In:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">890</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg534">
+ <source xml:space="preserve">Out:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">891</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg535">
+ <source xml:space="preserve">Inbound: initiated by peer</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">474</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg536">
+ <source xml:space="preserve">Outbound Full Relay: default</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">475</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg537">
+ <source xml:space="preserve">Outbound Block Relay: does not relay transactions or addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">476</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg538">
+ <source xml:space="preserve">Outbound Manual: added using RPC %1 or %2/%3 configuration options</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">477</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg539">
+ <source xml:space="preserve">Outbound Feeler: short-lived, for testing addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">481</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg540">
+ <source xml:space="preserve">Outbound Address Fetch: short-lived, for soliciting addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">482</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg541">
+ <source xml:space="preserve">we selected the peer for high bandwidth relay</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">486</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg542">
+ <source xml:space="preserve">the peer selected us for high bandwidth relay</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">487</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg543">
+ <source xml:space="preserve">no high bandwidth relay selected</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">488</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg544">
+ <source xml:space="preserve">Ctrl++</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">501</context></context-group>
+ <note annotates="source" from="developer">Main shortcut to increase the RPC console font size.</note>
+ </trans-unit>
+ <trans-unit id="_msg545">
+ <source xml:space="preserve">Ctrl+=</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">503</context></context-group>
+ <note annotates="source" from="developer">Secondary shortcut to increase the RPC console font size.</note>
+ </trans-unit>
+ <trans-unit id="_msg546">
+ <source xml:space="preserve">Ctrl+-</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">507</context></context-group>
+ <note annotates="source" from="developer">Main shortcut to decrease the RPC console font size.</note>
+ </trans-unit>
+ <trans-unit id="_msg547">
+ <source xml:space="preserve">Ctrl+_</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ <note annotates="source" from="developer">Secondary shortcut to decrease the RPC console font size.</note>
+ </trans-unit>
+ <trans-unit id="_msg548">
+ <source xml:space="preserve">Network activity disabled</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">894</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg549">
+ <source xml:space="preserve">Executing command without any wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">971</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg550">
+ <source xml:space="preserve">Executing command using &quot;%1&quot; wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">969</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg551">
+ <source xml:space="preserve">Disconnect</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">650</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg552">
+ <source xml:space="preserve">1 hour</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">651</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg553">
+ <source xml:space="preserve">1 day</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">652</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg554">
+ <source xml:space="preserve">1 week</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">653</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg555">
+ <source xml:space="preserve">1 year</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">654</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg556">
+ <source xml:space="preserve">Unban</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">673</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg557">
+ <source xml:space="preserve">Welcome to the %1 RPC console.
+Use up and down arrows to navigate history, and %2 to clear screen.
+Use %3 and %4 to increase or decrease the font size.
+Type %5 for an overview of available commands.
+For more information on using this console, type %6.
+
+%7WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.%8</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">823</context></context-group>
+ <note annotates="source" from="developer">RPC console welcome message. Placeholders %7 and %8 are style tags for the warning content, and they are not space separated from the rest of the text intentionally.</note>
+ </trans-unit>
+ <trans-unit id="_msg558">
+ <source xml:space="preserve">Executing…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">979</context></context-group>
+ <note annotates="source" from="developer">A console message indicating an entered command is currently being executed.</note>
+ </trans-unit>
+ <trans-unit id="_msg559">
+ <source xml:space="preserve">(peer: %1)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1097</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg560">
+ <source xml:space="preserve">via %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1099</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../rpcconsole.h" datatype="c" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="RPCConsole">
+ <trans-unit id="_msg561">
+ <source xml:space="preserve">Yes</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg562">
+ <source xml:space="preserve">No</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg563">
+ <source xml:space="preserve">To</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg564">
+ <source xml:space="preserve">From</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg565">
+ <source xml:space="preserve">Ban for</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg566">
+ <source xml:space="preserve">Never</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg567">
+ <source xml:space="preserve">Unknown</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/receivecoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog">
+ <trans-unit id="_msg568">
+ <source xml:space="preserve">&amp;Amount:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">37</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg569">
+ <source xml:space="preserve">&amp;Label:</source>
+ <target xml:space="preserve" state="needs-review-translation">&amp;Label:</target>
+ <context-group purpose="location"><context context-type="linenumber">83</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg570">
+ <source xml:space="preserve">&amp;Message:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">53</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg571">
+ <source xml:space="preserve">An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">50</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg572">
+ <source xml:space="preserve">An optional label to associate with the new receiving address.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg573">
+ <source xml:space="preserve">Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg574">
+ <source xml:space="preserve">An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">34</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">193</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg575">
+ <source xml:space="preserve">An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg576">
+ <source xml:space="preserve">An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg577">
+ <source xml:space="preserve">&amp;Create new receiving address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg578">
+ <source xml:space="preserve">Clear all fields of the form.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">134</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg579">
+ <source xml:space="preserve">Clear</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg580">
+ <source xml:space="preserve">Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don&apos;t support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg581">
+ <source xml:space="preserve">Generate native segwit (Bech32) address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg582">
+ <source xml:space="preserve">Requested payments history</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">279</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg583">
+ <source xml:space="preserve">Show the selected request (does the same as double clicking an entry)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">304</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg584">
+ <source xml:space="preserve">Show</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">307</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg585">
+ <source xml:space="preserve">Remove the selected entries from the list</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg586">
+ <source xml:space="preserve">Remove</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">327</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../receivecoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog">
+ <trans-unit id="_msg587">
+ <source xml:space="preserve">Copy URI</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg588">
+ <source xml:space="preserve">Copy address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">48</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg589">
+ <source xml:space="preserve">Copy label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg590">
+ <source xml:space="preserve">Copy message</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">50</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg591">
+ <source xml:space="preserve">Copy amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">51</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg592">
+ <source xml:space="preserve">Could not unlock wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg593">
+ <source xml:space="preserve">Could not generate new %1 address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">181</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/receiverequestdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog">
+ <trans-unit id="_msg594">
+ <source xml:space="preserve">Request payment to …</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg595">
+ <source xml:space="preserve">Address:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg596">
+ <source xml:space="preserve">Amount:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">119</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg597">
+ <source xml:space="preserve">Label:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">148</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg598">
+ <source xml:space="preserve">Message:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg599">
+ <source xml:space="preserve">Wallet:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">212</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg600">
+ <source xml:space="preserve">Copy &amp;URI</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg601">
+ <source xml:space="preserve">Copy &amp;Address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">250</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg602">
+ <source xml:space="preserve">&amp;Save Image…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg603">
+ <source xml:space="preserve">Payment information</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">39</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../receiverequestdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog">
+ <trans-unit id="_msg604">
+ <source xml:space="preserve">Request payment to %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../recentrequeststablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="RecentRequestsTableModel">
+ <trans-unit id="_msg605">
+ <source xml:space="preserve">Date</source>
+ <target xml:space="preserve" state="needs-review-translation">Date</target>
+ <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg606">
+ <source xml:space="preserve">Label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg607">
+ <source xml:space="preserve">Message</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">30</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg608">
+ <source xml:space="preserve">(no label)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">71</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg609">
+ <source xml:space="preserve">(no message)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg610">
+ <source xml:space="preserve">(no amount requested)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg611">
+ <source xml:space="preserve">Requested</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/sendcoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog">
+ <trans-unit id="_msg612" approved="yes">
+ <source xml:space="preserve">Send Coins</source>
+ <target xml:space="preserve">Send Coins</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">674</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg613">
+ <source xml:space="preserve">Coin Control Features</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">90</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg614">
+ <source xml:space="preserve">automatically selected</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg615">
+ <source xml:space="preserve">Insufficient funds!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg616">
+ <source xml:space="preserve">Quantity:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">228</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg617">
+ <source xml:space="preserve">Bytes:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">263</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg618">
+ <source xml:space="preserve">Amount:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">311</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg619">
+ <source xml:space="preserve">Fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">391</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg620">
+ <source xml:space="preserve">After Fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">442</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg621">
+ <source xml:space="preserve">Change:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">474</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg622">
+ <source xml:space="preserve">If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">518</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg623">
+ <source xml:space="preserve">Custom change address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">521</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg624">
+ <source xml:space="preserve">Transaction Fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">727</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg625">
+ <source xml:space="preserve">Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">765</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg626">
+ <source xml:space="preserve">Warning: Fee estimation is currently not possible.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">774</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg627">
+ <source xml:space="preserve">per kilobyte</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">856</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg628">
+ <source xml:space="preserve">Hide</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">803</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg629">
+ <source xml:space="preserve">Recommended:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">915</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg630">
+ <source xml:space="preserve">Custom:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">945</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg631" approved="yes">
+ <source xml:space="preserve">Send to multiple recipients at once</source>
+ <target xml:space="preserve">Send to multiple recipients at once</target>
+ <context-group purpose="location"><context context-type="linenumber">1160</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg632" approved="yes">
+ <source xml:space="preserve">Add &amp;Recipient</source>
+ <target xml:space="preserve">Add &amp;Recipient</target>
+ <context-group purpose="location"><context context-type="linenumber">1163</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg633">
+ <source xml:space="preserve">Clear all fields of the form.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1143</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg634">
+ <source xml:space="preserve">Inputs…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">110</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg635">
+ <source xml:space="preserve">Dust:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">343</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg636">
+ <source xml:space="preserve">Choose…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">741</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg637">
+ <source xml:space="preserve">Hide transaction fee settings</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">800</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg638">
+ <source xml:space="preserve">Specify a custom fee per kB (1,000 bytes) of the transaction&apos;s virtual size.
+
+Note: Since the fee is calculated on a per-byte basis, a fee rate of &quot;100 satoshis per kvB&quot; for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">851</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg639">
+ <source xml:space="preserve">When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">886</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg640">
+ <source xml:space="preserve">A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">889</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg641">
+ <source xml:space="preserve">(Smart fee not initialized yet. This usually takes a few blocks…)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">994</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg642">
+ <source xml:space="preserve">Confirmation time target:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1020</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg643">
+ <source xml:space="preserve">Enable Replace-By-Fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1078</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg644">
+ <source xml:space="preserve">With Replace-By-Fee (BIP-125) you can increase a transaction&apos;s fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1081</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg645" approved="yes">
+ <source xml:space="preserve">Clear &amp;All</source>
+ <target xml:space="preserve">Clear &amp;All</target>
+ <context-group purpose="location"><context context-type="linenumber">1146</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg646" approved="yes">
+ <source xml:space="preserve">Balance:</source>
+ <target xml:space="preserve">Balance:</target>
+ <context-group purpose="location"><context context-type="linenumber">1201</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg647" approved="yes">
+ <source xml:space="preserve">Confirm the send action</source>
+ <target xml:space="preserve">Confirm the send action</target>
+ <context-group purpose="location"><context context-type="linenumber">1117</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg648" approved="yes">
+ <source xml:space="preserve">S&amp;end</source>
+ <target xml:space="preserve">S&amp;end</target>
+ <context-group purpose="location"><context context-type="linenumber">1120</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../sendcoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog">
+ <trans-unit id="_msg649">
+ <source xml:space="preserve">Copy quantity</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">92</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg650">
+ <source xml:space="preserve">Copy amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">93</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg651">
+ <source xml:space="preserve">Copy fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">94</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg652">
+ <source xml:space="preserve">Copy after fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">95</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg653">
+ <source xml:space="preserve">Copy bytes</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">96</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg654">
+ <source xml:space="preserve">Copy dust</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg655">
+ <source xml:space="preserve">Copy change</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg656">
+ <source xml:space="preserve">%1 (%2 blocks)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg657">
+ <source xml:space="preserve">Cr&amp;eate Unsigned</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg658">
+ <source xml:space="preserve">Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">204</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg659">
+ <source xml:space="preserve"> from wallet &apos;%1&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">294</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg660">
+ <source xml:space="preserve">%1 to &apos;%2&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">305</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg661">
+ <source xml:space="preserve">%1 to %2</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">310</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg662">
+ <source xml:space="preserve">Do you want to draft this transaction?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">317</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg663">
+ <source xml:space="preserve">Are you sure you want to send?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">319</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg664">
+ <source xml:space="preserve">To review recipient list click &quot;Show Details…&quot;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">371</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg665">
+ <source xml:space="preserve">Create Unsigned</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg666">
+ <source xml:space="preserve">Save Transaction Data</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">434</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg667">
+ <source xml:space="preserve">Partially Signed Transaction (Binary)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">436</context></context-group>
+ <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note>
+ </trans-unit>
+ <trans-unit id="_msg668">
+ <source xml:space="preserve">PSBT saved</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">443</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg669">
+ <source xml:space="preserve">or</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">367</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg670">
+ <source xml:space="preserve">You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">348</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg671">
+ <source xml:space="preserve">Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">324</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg672">
+ <source xml:space="preserve">Please, review your transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg673">
+ <source xml:space="preserve">Transaction fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">334</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg674">
+ <source xml:space="preserve">Not signalling Replace-By-Fee, BIP-125.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">350</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg675">
+ <source xml:space="preserve">Total Amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">364</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg676">
+ <source xml:space="preserve">Confirm send coins</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">389</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg677">
+ <source xml:space="preserve">Confirm transaction proposal</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">389</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg678">
+ <source xml:space="preserve">Send</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg679">
+ <source xml:space="preserve">Watch-only balance:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">619</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg680">
+ <source xml:space="preserve">The recipient address is not valid. Please recheck.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">643</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg681">
+ <source xml:space="preserve">The amount to pay must be larger than 0.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">646</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg682">
+ <source xml:space="preserve">The amount exceeds your balance.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">649</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg683">
+ <source xml:space="preserve">The total exceeds your balance when the %1 transaction fee is included.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">652</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg684">
+ <source xml:space="preserve">Duplicate address found: addresses should only be used once each.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">655</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg685">
+ <source xml:space="preserve">Transaction creation failed!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">658</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg686">
+ <source xml:space="preserve">A fee higher than %1 is considered an absurdly high fee.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">662</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg687">
+ <source xml:space="preserve">Payment request expired.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">665</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">789</context></context-group>
+ <trans-unit id="_msg688[0]" approved="yes">
+ <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source>
+ <target xml:space="preserve">Estimated to begin confirmation within %n block.</target>
+ </trans-unit>
+ <trans-unit id="_msg688[1]" approved="yes">
+ <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source>
+ <target xml:space="preserve">Estimated to begin confirmation within %n blocks.</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg689">
+ <source xml:space="preserve">Warning: Invalid Bitcoin address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">890</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg690">
+ <source xml:space="preserve">Warning: Unknown change address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">895</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg691">
+ <source xml:space="preserve">Confirm custom change address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">898</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg692">
+ <source xml:space="preserve">The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">898</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg693">
+ <source xml:space="preserve">(no label)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">919</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/sendcoinsentry.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="SendCoinsEntry">
+ <trans-unit id="_msg694" approved="yes">
+ <source xml:space="preserve">A&amp;mount:</source>
+ <target xml:space="preserve">A&amp;mount:</target>
+ <context-group purpose="location"><context context-type="linenumber">155</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">705</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1238</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg695" approved="yes">
+ <source xml:space="preserve">Pay &amp;To:</source>
+ <target xml:space="preserve">Pay &amp;To:</target>
+ <context-group purpose="location"><context context-type="linenumber">39</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg696" approved="yes">
+ <source xml:space="preserve">&amp;Label:</source>
+ <target xml:space="preserve">&amp;Label:</target>
+ <context-group purpose="location"><context context-type="linenumber">132</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg697">
+ <source xml:space="preserve">Choose previously used address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">64</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg698">
+ <source xml:space="preserve">The Bitcoin address to send the payment to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">57</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg699" approved="yes">
+ <source xml:space="preserve">Alt+A</source>
+ <target xml:space="preserve">Alt+A</target>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg700" approved="yes">
+ <source xml:space="preserve">Paste address from clipboard</source>
+ <target xml:space="preserve">Paste address from clipboard</target>
+ <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg701" approved="yes">
+ <source xml:space="preserve">Alt+P</source>
+ <target xml:space="preserve">Alt+P</target>
+ <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg702">
+ <source xml:space="preserve">Remove this entry</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">110</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">672</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1205</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg703">
+ <source xml:space="preserve">The amount to send in the selected unit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">170</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg704">
+ <source xml:space="preserve">The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">177</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg705">
+ <source xml:space="preserve">S&amp;ubtract fee from amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg706">
+ <source xml:space="preserve">Use available balance</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">187</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg707">
+ <source xml:space="preserve">Message:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">196</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg708">
+ <source xml:space="preserve">This is an unauthenticated payment request.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">639</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg709">
+ <source xml:space="preserve">This is an authenticated payment request.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">1168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg710">
+ <source xml:space="preserve">Enter a label for this address to add it to the list of used addresses</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">145</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">148</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg711">
+ <source xml:space="preserve">A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg712">
+ <source xml:space="preserve">Pay To:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">654</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1183</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg713">
+ <source xml:space="preserve">Memo:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">688</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">1221</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/signverifymessagedialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog">
+ <trans-unit id="_msg714" approved="yes">
+ <source xml:space="preserve">Signatures - Sign / Verify a Message</source>
+ <target xml:space="preserve">Signatures - Sign / Verify a Message</target>
+ <context-group purpose="location"><context context-type="linenumber">14</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg715" approved="yes">
+ <source xml:space="preserve">&amp;Sign Message</source>
+ <target xml:space="preserve">&amp;Sign Message</target>
+ <context-group purpose="location"><context context-type="linenumber">27</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg716">
+ <source xml:space="preserve">You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">33</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg717">
+ <source xml:space="preserve">The Bitcoin address to sign the message with</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">51</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg718">
+ <source xml:space="preserve">Choose previously used address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">274</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg719" approved="yes">
+ <source xml:space="preserve">Alt+A</source>
+ <target xml:space="preserve">Alt+A</target>
+ <context-group purpose="location"><context context-type="linenumber">68</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg720" approved="yes">
+ <source xml:space="preserve">Paste address from clipboard</source>
+ <target xml:space="preserve">Paste address from clipboard</target>
+ <context-group purpose="location"><context context-type="linenumber">78</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg721" approved="yes">
+ <source xml:space="preserve">Alt+P</source>
+ <target xml:space="preserve">Alt+P</target>
+ <context-group purpose="location"><context context-type="linenumber">88</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg722" approved="yes">
+ <source xml:space="preserve">Enter the message you want to sign here</source>
+ <target xml:space="preserve">Enter the message you want to sign here</target>
+ <context-group purpose="location"><context context-type="linenumber">100</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">103</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg723" approved="yes">
+ <source xml:space="preserve">Signature</source>
+ <target xml:space="preserve">Signature</target>
+ <context-group purpose="location"><context context-type="linenumber">110</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg724" approved="yes">
+ <source xml:space="preserve">Copy the current signature to the system clipboard</source>
+ <target xml:space="preserve">Copy the current signature to the system clipboard</target>
+ <context-group purpose="location"><context context-type="linenumber">140</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg725" approved="yes">
+ <source xml:space="preserve">Sign the message to prove you own this Bitcoin address</source>
+ <target xml:space="preserve">Sign the message to prove you own this Bitcoin address</target>
+ <context-group purpose="location"><context context-type="linenumber">161</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg726" approved="yes">
+ <source xml:space="preserve">Sign &amp;Message</source>
+ <target xml:space="preserve">Sign &amp;Message</target>
+ <context-group purpose="location"><context context-type="linenumber">164</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg727" approved="yes">
+ <source xml:space="preserve">Reset all sign message fields</source>
+ <target xml:space="preserve">Reset all sign message fields</target>
+ <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg728" approved="yes">
+ <source xml:space="preserve">Clear &amp;All</source>
+ <target xml:space="preserve">Clear &amp;All</target>
+ <context-group purpose="location"><context context-type="linenumber">181</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg729" approved="yes">
+ <source xml:space="preserve">&amp;Verify Message</source>
+ <target xml:space="preserve">&amp;Verify Message</target>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg730">
+ <source xml:space="preserve">Enter the receiver&apos;s address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg731">
+ <source xml:space="preserve">The Bitcoin address the message was signed with</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">267</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg732">
+ <source xml:space="preserve">The signed message to verify</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">296</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">299</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg733">
+ <source xml:space="preserve">The signature given when the message was signed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">306</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">309</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg734" approved="yes">
+ <source xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</source>
+ <target xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</target>
+ <context-group purpose="location"><context context-type="linenumber">318</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg735" approved="yes">
+ <source xml:space="preserve">Verify &amp;Message</source>
+ <target xml:space="preserve">Verify &amp;Message</target>
+ <context-group purpose="location"><context context-type="linenumber">321</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg736" approved="yes">
+ <source xml:space="preserve">Reset all verify message fields</source>
+ <target xml:space="preserve">Reset all verify message fields</target>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg737">
+ <source xml:space="preserve">Click &quot;Sign Message&quot; to generate signature</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">125</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../signverifymessagedialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog">
+ <trans-unit id="_msg738">
+ <source xml:space="preserve">The entered address is invalid.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">219</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg739">
+ <source xml:space="preserve">Please check the address and try again.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">120</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">127</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">220</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">227</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg740">
+ <source xml:space="preserve">The entered address does not refer to a key.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">127</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg741">
+ <source xml:space="preserve">Wallet unlock was cancelled.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">135</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg742">
+ <source xml:space="preserve">No error</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">146</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg743">
+ <source xml:space="preserve">Private key for the entered address is not available.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">149</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg744">
+ <source xml:space="preserve">Message signing failed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg745">
+ <source xml:space="preserve">Message signed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">164</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg746">
+ <source xml:space="preserve">The signature could not be decoded.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">233</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg747">
+ <source xml:space="preserve">Please check the signature and try again.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">234</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">241</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg748">
+ <source xml:space="preserve">The signature did not match the message digest.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">240</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg749">
+ <source xml:space="preserve">Message verification failed.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg750">
+ <source xml:space="preserve">Message verified.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">214</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../trafficgraphwidget.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TrafficGraphWidget">
+ <trans-unit id="_msg751">
+ <source xml:space="preserve">kB/s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">82</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../transactiondesc.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TransactionDesc">
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">36</context></context-group>
+ <trans-unit id="_msg752[0]" approved="yes">
+ <source xml:space="preserve">Open for %n more block(s)</source>
+ <target xml:space="preserve">Open for %n more block</target>
+ </trans-unit>
+ <trans-unit id="_msg752[1]" approved="yes">
+ <source xml:space="preserve">Open for %n more block(s)</source>
+ <target xml:space="preserve">Open for %n more blocks</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg753">
+ <source xml:space="preserve">Open until %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">38</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg754">
+ <source xml:space="preserve">conflicted with a transaction with %1 confirmations</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">44</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg755">
+ <source xml:space="preserve">0/unconfirmed, %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg756">
+ <source xml:space="preserve">in memory pool</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg757">
+ <source xml:space="preserve">not in memory pool</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">47</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg758">
+ <source xml:space="preserve">abandoned</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">46</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg759">
+ <source xml:space="preserve">%1/unconfirmed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg760">
+ <source xml:space="preserve">%1 confirmations</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">51</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg761">
+ <source xml:space="preserve">Status</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg762">
+ <source xml:space="preserve">Date</source>
+ <target xml:space="preserve" state="needs-review-translation">Date</target>
+ <context-group purpose="location"><context context-type="linenumber">105</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg763">
+ <source xml:space="preserve">Source</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">112</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg764">
+ <source xml:space="preserve">Generated</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">112</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg765">
+ <source xml:space="preserve">From</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">117</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg766">
+ <source xml:space="preserve">unknown</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg767">
+ <source xml:space="preserve">To</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">132</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg768">
+ <source xml:space="preserve">own address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">134</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg769">
+ <source xml:space="preserve">watch-only</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">134</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg770">
+ <source xml:space="preserve">label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">136</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg771">
+ <source xml:space="preserve">Credit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">184</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">238</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">268</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">328</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ <trans-unit id="_msg772[0]" approved="yes">
+ <source xml:space="preserve">matures in %n more block(s)</source>
+ <target xml:space="preserve">matures in %n more block</target>
+ </trans-unit>
+ <trans-unit id="_msg772[1]" approved="yes">
+ <source xml:space="preserve">matures in %n more block(s)</source>
+ <target xml:space="preserve">matures in %n more blocks</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg773">
+ <source xml:space="preserve">not accepted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg774">
+ <source xml:space="preserve">Debit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">262</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">325</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg775">
+ <source xml:space="preserve">Total debit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">246</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg776">
+ <source xml:space="preserve">Total credit</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">247</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg777">
+ <source xml:space="preserve">Transaction fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">252</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg778">
+ <source xml:space="preserve">Net amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">274</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg779">
+ <source xml:space="preserve">Message</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">280</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">292</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg780">
+ <source xml:space="preserve">Comment</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">282</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg781">
+ <source xml:space="preserve">Transaction ID</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">284</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg782">
+ <source xml:space="preserve">Transaction total size</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">285</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg783">
+ <source xml:space="preserve">Transaction virtual size</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">286</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg784">
+ <source xml:space="preserve">Output index</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg785">
+ <source xml:space="preserve"> (Certificate was not verified)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">303</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg786">
+ <source xml:space="preserve">Merchant</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">306</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg787">
+ <source xml:space="preserve">Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to &quot;not accepted&quot; and it won&apos;t be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">314</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg788">
+ <source xml:space="preserve">Debug information</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">322</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg789">
+ <source xml:space="preserve">Transaction</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">330</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg790">
+ <source xml:space="preserve">Inputs</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">333</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg791">
+ <source xml:space="preserve">Amount</source>
+ <target xml:space="preserve" state="needs-review-translation">Amount</target>
+ <context-group purpose="location"><context context-type="linenumber">354</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg792">
+ <source xml:space="preserve">true</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">355</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">356</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg793">
+ <source xml:space="preserve">false</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">355</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">356</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../forms/transactiondescdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog">
+ <trans-unit id="_msg794" approved="yes">
+ <source xml:space="preserve">This pane shows a detailed description of the transaction</source>
+ <target xml:space="preserve">This pane shows a detailed description of the transaction</target>
+ <context-group purpose="location"><context context-type="linenumber">20</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../transactiondescdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog">
+ <trans-unit id="_msg795">
+ <source xml:space="preserve">Details for %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">18</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../transactiontablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TransactionTableModel">
+ <trans-unit id="_msg796">
+ <source xml:space="preserve">Date</source>
+ <target xml:space="preserve" state="needs-review-translation">Date</target>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg797">
+ <source xml:space="preserve">Type</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg798">
+ <source xml:space="preserve">Label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">260</context></context-group>
+ </trans-unit>
+ <group restype="x-gettext-plurals">
+ <context-group purpose="location"><context context-type="linenumber">320</context></context-group>
+ <trans-unit id="_msg799[0]" approved="yes">
+ <source xml:space="preserve">Open for %n more block(s)</source>
+ <target xml:space="preserve">Open for %n more block</target>
+ </trans-unit>
+ <trans-unit id="_msg799[1]" approved="yes">
+ <source xml:space="preserve">Open for %n more block(s)</source>
+ <target xml:space="preserve">Open for %n more blocks</target>
+ </trans-unit>
+ </group>
+ <trans-unit id="_msg800">
+ <source xml:space="preserve">Open until %1</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">323</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg801">
+ <source xml:space="preserve">Unconfirmed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">326</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg802">
+ <source xml:space="preserve">Abandoned</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">329</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg803">
+ <source xml:space="preserve">Confirming (%1 of %2 recommended confirmations)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">332</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg804">
+ <source xml:space="preserve">Confirmed (%1 confirmations)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">335</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg805">
+ <source xml:space="preserve">Conflicted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">338</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg806">
+ <source xml:space="preserve">Immature (%1 confirmations, will be available after %2)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">341</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg807">
+ <source xml:space="preserve">Generated but not accepted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">344</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg808">
+ <source xml:space="preserve">Received with</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">383</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg809">
+ <source xml:space="preserve">Received from</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">385</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg810">
+ <source xml:space="preserve">Sent to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">388</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg811">
+ <source xml:space="preserve">Payment to yourself</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">390</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg812">
+ <source xml:space="preserve">Mined</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">392</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg813">
+ <source xml:space="preserve">watch-only</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">420</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg814">
+ <source xml:space="preserve">(n/a)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">436</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg815">
+ <source xml:space="preserve">(no label)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">646</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg816">
+ <source xml:space="preserve">Transaction status. Hover over this field to show number of confirmations.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">685</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg817">
+ <source xml:space="preserve">Date and time that the transaction was received.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">687</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg818">
+ <source xml:space="preserve">Type of transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">689</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg819">
+ <source xml:space="preserve">Whether or not a watch-only address is involved in this transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">691</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg820">
+ <source xml:space="preserve">User-defined intent/purpose of the transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">693</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg821">
+ <source xml:space="preserve">Amount removed from or added to balance.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">695</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../transactionview.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="TransactionView">
+ <trans-unit id="_msg822">
+ <source xml:space="preserve">All</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">70</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">86</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg823">
+ <source xml:space="preserve">Today</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">71</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg824">
+ <source xml:space="preserve">This week</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">72</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg825">
+ <source xml:space="preserve">This month</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">73</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg826">
+ <source xml:space="preserve">Last month</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">74</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg827">
+ <source xml:space="preserve">This year</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">75</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg828">
+ <source xml:space="preserve">Received with</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg829">
+ <source xml:space="preserve">Sent to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">89</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg830">
+ <source xml:space="preserve">To yourself</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">91</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg831">
+ <source xml:space="preserve">Mined</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">92</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg832">
+ <source xml:space="preserve">Other</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">93</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg833">
+ <source xml:space="preserve">Enter address, transaction id, or label to search</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">98</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg834">
+ <source xml:space="preserve">Min amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg835">
+ <source xml:space="preserve">Abandon transaction</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">177</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg836">
+ <source xml:space="preserve">Increase transaction fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg837">
+ <source xml:space="preserve">Copy address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">166</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg838">
+ <source xml:space="preserve">Copy label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">167</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg839">
+ <source xml:space="preserve">Copy amount</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg840">
+ <source xml:space="preserve">Copy transaction ID</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">169</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg841">
+ <source xml:space="preserve">Copy raw transaction</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">170</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg842">
+ <source xml:space="preserve">Copy full transaction details</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">171</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg843">
+ <source xml:space="preserve">Edit address label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg844">
+ <source xml:space="preserve">Show transaction details</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg845">
+ <source xml:space="preserve">Range…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">76</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg846">
+ <source xml:space="preserve">Export Transaction History</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">352</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg847">
+ <source xml:space="preserve">Comma separated file</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">355</context></context-group>
+ <note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note>
+ </trans-unit>
+ <trans-unit id="_msg848">
+ <source xml:space="preserve">Confirmed</source>
+ <target xml:space="preserve" state="needs-review-translation">Confirmed</target>
+ <context-group purpose="location"><context context-type="linenumber">364</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg849">
+ <source xml:space="preserve">Watch-only</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">366</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg850">
+ <source xml:space="preserve">Date</source>
+ <target xml:space="preserve" state="needs-review-translation">Date</target>
+ <context-group purpose="location"><context context-type="linenumber">367</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg851">
+ <source xml:space="preserve">Type</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">368</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg852">
+ <source xml:space="preserve">Label</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">369</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg853">
+ <source xml:space="preserve">Address</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">370</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg854">
+ <source xml:space="preserve">ID</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">372</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg855">
+ <source xml:space="preserve">Exporting Failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">375</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg856">
+ <source xml:space="preserve">There was an error trying to save the transaction history to %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">375</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg857">
+ <source xml:space="preserve">Exporting Successful</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">379</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg858">
+ <source xml:space="preserve">The transaction history was successfully saved to %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">379</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg859">
+ <source xml:space="preserve">Range:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">551</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg860">
+ <source xml:space="preserve">to</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">559</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../walletframe.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="WalletFrame">
+ <trans-unit id="_msg861">
+ <source xml:space="preserve">No wallet has been loaded.
+Go to File &gt; Open Wallet to load a wallet.
+- OR -</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">39</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg862">
+ <source xml:space="preserve">Create a new wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">44</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../walletmodel.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="WalletModel">
+ <trans-unit id="_msg863">
+ <source xml:space="preserve">Send Coins</source>
+ <target xml:space="preserve" state="needs-review-translation">Send Coins</target>
+ <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg864">
+ <source xml:space="preserve">Fee bump error</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">497</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">549</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">562</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">567</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg865">
+ <source xml:space="preserve">Increasing transaction fee failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">497</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg866">
+ <source xml:space="preserve">Do you want to increase the fee?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg867">
+ <source xml:space="preserve">Do you want to draft a transaction with fee increase?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">505</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg868">
+ <source xml:space="preserve">Current fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">509</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg869">
+ <source xml:space="preserve">Increase:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">513</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg870">
+ <source xml:space="preserve">New fee:</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">517</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg871">
+ <source xml:space="preserve">Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">525</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg872">
+ <source xml:space="preserve">Confirm fee bump</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">528</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg873">
+ <source xml:space="preserve">Can&apos;t draft transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">549</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg874">
+ <source xml:space="preserve">PSBT copied</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">556</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg875">
+ <source xml:space="preserve">Can&apos;t sign transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">562</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg876">
+ <source xml:space="preserve">Could not commit transaction</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">567</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg877">
+ <source xml:space="preserve">default wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">587</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../walletview.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="WalletView">
+ <trans-unit id="_msg878">
+ <source xml:space="preserve">&amp;Export</source>
+ <target xml:space="preserve" state="needs-review-translation">&amp;Export</target>
+ <context-group purpose="location"><context context-type="linenumber">51</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg879">
+ <source xml:space="preserve">Export the data in the current tab to a file</source>
+ <target xml:space="preserve" state="needs-review-translation">Export the data in the current tab to a file</target>
+ <context-group purpose="location"><context context-type="linenumber">52</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg880">
+ <source xml:space="preserve">Error</source>
+ <target xml:space="preserve" state="needs-review-translation">Error</target>
+ <context-group purpose="location"><context context-type="linenumber">217</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg881">
+ <source xml:space="preserve">Unable to decode PSBT from clipboard (invalid base64)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">217</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg882">
+ <source xml:space="preserve">Load Transaction Data</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg883">
+ <source xml:space="preserve">Partially Signed Transaction (*.psbt)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">223</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg884">
+ <source xml:space="preserve">PSBT file must be smaller than 100 MiB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg885">
+ <source xml:space="preserve">Unable to decode PSBT</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg886">
+ <source xml:space="preserve">Backup Wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">275</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg887">
+ <source xml:space="preserve">Wallet Data</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">277</context></context-group>
+ <note annotates="source" from="developer">Name of the wallet data file format.</note>
+ </trans-unit>
+ <trans-unit id="_msg888">
+ <source xml:space="preserve">Backup Failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg889">
+ <source xml:space="preserve">There was an error trying to save the wallet data to %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">283</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg890">
+ <source xml:space="preserve">Backup Successful</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg891">
+ <source xml:space="preserve">The wallet data was successfully saved to %1.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">287</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg892">
+ <source xml:space="preserve">Cancel</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">331</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+ <file original="../bitcoinstrings.cpp" datatype="cpp" source-language="en" target-language="en"><body>
+ <group restype="x-trolltech-linguist-context" resname="bitcoin-core">
+ <trans-unit id="_msg893">
+ <source xml:space="preserve">The %s developers</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">12</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg894">
+ <source xml:space="preserve">%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">13</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg895">
+ <source xml:space="preserve">-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">16</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg896">
+ <source xml:space="preserve">Cannot downgrade wallet from version %i to version %i. Wallet version unchanged.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">19</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg897">
+ <source xml:space="preserve">Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">22</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg898">
+ <source xml:space="preserve">Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">24</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg899">
+ <source xml:space="preserve">Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">27</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg900">
+ <source xml:space="preserve">Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">31</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg901">
+ <source xml:space="preserve">Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">34</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg902">
+ <source xml:space="preserve">Error: Dumpfile format record is incorrect. Got &quot;%s&quot;, expected &quot;format&quot;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">37</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg903">
+ <source xml:space="preserve">Error: Dumpfile identifier record is incorrect. Got &quot;%s&quot;, expected &quot;%s&quot;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">39</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg904">
+ <source xml:space="preserve">Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">41</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg905">
+ <source xml:space="preserve">Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">44</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg906">
+ <source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">46</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg907">
+ <source xml:space="preserve">File %s already exists. If you are sure this is what you want, move it out of the way first.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">49</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg908">
+ <source xml:space="preserve">Invalid amount for -maxtxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">52</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg909">
+ <source xml:space="preserve">More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">55</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg910">
+ <source xml:space="preserve">No dump file provided. To use createfromdump, -dumpfile=&lt;filename&gt; must be provided.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">58</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg911">
+ <source xml:space="preserve">No dump file provided. To use dump, -dumpfile=&lt;filename&gt; must be provided.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">61</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg912">
+ <source xml:space="preserve">No wallet file format provided. To use createfromdump, -format=&lt;format&gt; must be provided.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">63</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg913">
+ <source xml:space="preserve">Please check that your computer&apos;s date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">66</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg914">
+ <source xml:space="preserve">Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">69</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg915">
+ <source xml:space="preserve">Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">72</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg916">
+ <source xml:space="preserve">Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">74</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg917">
+ <source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">77</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg918">
+ <source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer&apos;s date and time being set incorrectly. Only rebuild the block database if you are sure that your computer&apos;s date and time are correct</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">80</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg919">
+ <source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">85</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg920">
+ <source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">87</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg921">
+ <source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">91</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg922">
+ <source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">94</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg923">
+ <source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">97</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg924">
+ <source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">100</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg925">
+ <source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">102</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg926">
+ <source xml:space="preserve">Transaction needs a change address, but we can&apos;t generate it. Please call keypoolrefill first.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">105</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg927">
+ <source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">108</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg928">
+ <source xml:space="preserve">Unknown wallet file format &quot;%s&quot; provided. Please provide one of &quot;bdb&quot; or &quot;sqlite&quot;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">111</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg929">
+ <source xml:space="preserve">Warning: Dumpfile wallet format &quot;%s&quot; does not match command line specified format &quot;%s&quot;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">114</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg930">
+ <source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">117</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg931">
+ <source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">119</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg932">
+ <source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">122</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg933">
+ <source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">125</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg934">
+ <source xml:space="preserve">%s is set very high!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">128</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg935">
+ <source xml:space="preserve">-maxmempool must be at least %d MB</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">129</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg936">
+ <source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">130</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg937">
+ <source xml:space="preserve">Cannot resolve -%s address: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">131</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg938">
+ <source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">132</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg939">
+ <source xml:space="preserve">Cannot write to data directory &apos;%s&apos;; check permissions.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">133</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg940">
+ <source xml:space="preserve">Change index out of range</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">134</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg941">
+ <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">135</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg942">
+ <source xml:space="preserve">Copyright (C) %i-%i</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">136</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg943">
+ <source xml:space="preserve">Corrupted block database detected</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">137</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg944">
+ <source xml:space="preserve">Could not find asmap file %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">138</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg945">
+ <source xml:space="preserve">Could not parse asmap file %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">139</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg946">
+ <source xml:space="preserve">Disk space is too low!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">140</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg947">
+ <source xml:space="preserve">Do you want to rebuild the block database now?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">141</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg948">
+ <source xml:space="preserve">Done loading</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">142</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg949">
+ <source xml:space="preserve">Dump file %s does not exist.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">143</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg950">
+ <source xml:space="preserve">Error creating %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">144</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg951">
+ <source xml:space="preserve">Error initializing block database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">145</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg952">
+ <source xml:space="preserve">Error initializing wallet database environment %s!</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">146</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg953">
+ <source xml:space="preserve">Error loading %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">147</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg954">
+ <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">148</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg955">
+ <source xml:space="preserve">Error loading %s: Wallet corrupted</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">149</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg956">
+ <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">150</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg957">
+ <source xml:space="preserve">Error loading block database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">151</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg958">
+ <source xml:space="preserve">Error opening block database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">152</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg959">
+ <source xml:space="preserve">Error reading from database, shutting down.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">153</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg960">
+ <source xml:space="preserve">Error reading next record from wallet database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">154</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg961">
+ <source xml:space="preserve">Error upgrading chainstate database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">155</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg962">
+ <source xml:space="preserve">Error: Couldn&apos;t create cursor into database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">156</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg963">
+ <source xml:space="preserve">Error: Disk space is low for %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">157</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg964">
+ <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">158</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg965">
+ <source xml:space="preserve">Error: Got key that was not hex: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">159</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg966">
+ <source xml:space="preserve">Error: Got value that was not hex: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">160</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg967">
+ <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">161</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg968">
+ <source xml:space="preserve">Error: Missing checksum</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">162</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg969">
+ <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">163</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg970">
+ <source xml:space="preserve">Error: Unable to write record to new wallet</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">164</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg971">
+ <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">165</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg972">
+ <source xml:space="preserve">Failed to rescan the wallet during initialization</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">166</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg973">
+ <source xml:space="preserve">Failed to verify database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">167</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg974">
+ <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">168</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg975">
+ <source xml:space="preserve">Ignoring duplicate -wallet %s.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">169</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg976">
+ <source xml:space="preserve">Importing…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">170</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg977">
+ <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">171</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg978">
+ <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">172</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg979">
+ <source xml:space="preserve">Insufficient funds</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">173</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg980">
+ <source xml:space="preserve">Invalid -i2psam address or hostname: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">174</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg981">
+ <source xml:space="preserve">Invalid -onion address or hostname: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">175</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg982">
+ <source xml:space="preserve">Invalid -proxy address or hostname: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">176</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg983">
+ <source xml:space="preserve">Invalid P2P permission: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">177</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg984">
+ <source xml:space="preserve">Invalid amount for -%s=&lt;amount&gt;: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">178</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg985">
+ <source xml:space="preserve">Invalid amount for -discardfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">179</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg986">
+ <source xml:space="preserve">Invalid amount for -fallbackfee=&lt;amount&gt;: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">180</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg987">
+ <source xml:space="preserve">Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos; (must be at least %s)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">181</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg988">
+ <source xml:space="preserve">Invalid netmask specified in -whitelist: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">182</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg989">
+ <source xml:space="preserve">Loading P2P addresses…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">183</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg990">
+ <source xml:space="preserve">Loading banlist…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">184</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg991">
+ <source xml:space="preserve">Loading block index…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">185</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg992">
+ <source xml:space="preserve">Loading wallet…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">186</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg993">
+ <source xml:space="preserve">Need to specify a port with -whitebind: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">187</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg994">
+ <source xml:space="preserve">No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">188</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg995">
+ <source xml:space="preserve">Not enough file descriptors available.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">189</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg996">
+ <source xml:space="preserve">Prune cannot be configured with a negative value.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">190</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg997">
+ <source xml:space="preserve">Prune mode is incompatible with -coinstatsindex.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">191</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg998">
+ <source xml:space="preserve">Prune mode is incompatible with -txindex.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">192</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg999">
+ <source xml:space="preserve">Pruning blockstore…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">193</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1000">
+ <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">194</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1001">
+ <source xml:space="preserve">Replaying blocks…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">195</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1002">
+ <source xml:space="preserve">Rescanning…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">196</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1003">
+ <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">197</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1004">
+ <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">198</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1005">
+ <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">199</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1006">
+ <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">200</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1007">
+ <source xml:space="preserve">Section [%s] is not recognized.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">201</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1008">
+ <source xml:space="preserve">Signing transaction failed</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">202</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1009">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; does not exist</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">203</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1010">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is a relative path</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">204</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1011">
+ <source xml:space="preserve">Specified -walletdir &quot;%s&quot; is not a directory</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">205</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1012">
+ <source xml:space="preserve">Specified blocks directory &quot;%s&quot; does not exist.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">206</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1013">
+ <source xml:space="preserve">Starting network threads…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">207</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1014">
+ <source xml:space="preserve">The source code is available from %s.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">208</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1015">
+ <source xml:space="preserve">The specified config file %s does not exist</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">209</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1016">
+ <source xml:space="preserve">The transaction amount is too small to pay the fee</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">210</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1017">
+ <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">211</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1018">
+ <source xml:space="preserve">This is experimental software.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">212</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1019">
+ <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">213</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1020">
+ <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">214</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1021">
+ <source xml:space="preserve">Transaction amount too small</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">215</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1022">
+ <source xml:space="preserve">Transaction amounts must not be negative</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">216</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1023">
+ <source xml:space="preserve">Transaction has too long of a mempool chain</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">217</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1024">
+ <source xml:space="preserve">Transaction must have at least one recipient</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">218</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1025">
+ <source xml:space="preserve">Transaction too large</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">219</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1026">
+ <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">220</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1027">
+ <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">221</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1028">
+ <source xml:space="preserve">Unable to create the PID file &apos;%s&apos;: %s</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">222</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1029">
+ <source xml:space="preserve">Unable to generate initial keys</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">223</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1030">
+ <source xml:space="preserve">Unable to generate keys</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">224</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1031">
+ <source xml:space="preserve">Unable to open %s for writing</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">225</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1032">
+ <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">226</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1033">
+ <source xml:space="preserve">Unknown -blockfilterindex value %s.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">227</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1034">
+ <source xml:space="preserve">Unknown address type &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">228</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1035">
+ <source xml:space="preserve">Unknown change type &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">229</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1036">
+ <source xml:space="preserve">Unknown network specified in -onlynet: &apos;%s&apos;</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">230</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1037">
+ <source xml:space="preserve">Unsupported logging category %s=%s.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">231</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1038">
+ <source xml:space="preserve">Upgrading UTXO database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">232</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1039">
+ <source xml:space="preserve">Upgrading txindex database</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">233</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1040">
+ <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">234</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1041">
+ <source xml:space="preserve">Verifying blocks…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">235</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1042">
+ <source xml:space="preserve">Verifying wallet(s)…</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">236</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1043">
+ <source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">237</context></context-group>
+ </trans-unit>
+ <trans-unit id="_msg1044">
+ <source xml:space="preserve">Warning: unknown new rules activated (versionbit %i)</source>
+ <target xml:space="preserve"></target>
+ <context-group purpose="location"><context context-type="linenumber">238</context></context-group>
+ </trans-unit>
+ </group>
+ </body></file>
+</xliff>
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 8070aa627c..ae27cad477 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -134,7 +134,6 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
// show the percentage done according to nVerificationProgress
ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%");
- ui->progressBar->setValue(nVerificationProgress*100);
if (!bestHeaderDate.isValid())
// not syncing
@@ -150,13 +149,13 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
} else {
UpdateHeaderSyncLabel();
- ui->expectedTimeLeft->setText(tr("Unknown..."));
+ ui->expectedTimeLeft->setText(tr("Unknown…"));
}
}
void ModalOverlay::UpdateHeaderSyncLabel() {
int est_headers_left = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
- ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)...").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
+ ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)…").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
}
void ModalOverlay::toggleVisibility()
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index e6b9488344..8a32994e3f 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -197,9 +197,9 @@ void OptionsDialog::setModel(OptionsModel *_model)
/* Main */
connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning);
- connect(ui->pruneSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
- connect(ui->databaseCache, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
- connect(ui->threadsScriptVerif, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
+ connect(ui->pruneSize, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
+ connect(ui->databaseCache, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
+ connect(ui->threadsScriptVerif, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
/* Wallet */
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Network */
@@ -207,7 +207,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Display */
- connect(ui->lang, static_cast<void (QValueComboBox::*)()>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); });
+ connect(ui->lang, qOverload<>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); });
connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
}
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index d51a5b06ff..abdf9e9ae6 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -21,6 +21,7 @@
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <QDebug>
+#include <QLatin1Char>
#include <QSettings>
#include <QStringList>
@@ -195,7 +196,7 @@ void OptionsModel::Reset()
QSettings settings;
// Backup old settings to chain-specific datadir for troubleshooting
- BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
+ BackupSettings(gArgs.GetDataDirNet() / "guisettings.ini.bak", settings);
// Save the strDataDir setting
QString dataDir = GUIUtil::getDefaultDataDirectory();
@@ -244,7 +245,7 @@ static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
{
- settings.setValue(name, ip_port.ip + ":" + ip_port.port);
+ settings.setValue(name, QString{ip_port.ip + QLatin1Char(':') + ip_port.port});
}
static const QString GetDefaultProxyAddress()
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 7f12b1d2b5..27783bdf87 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -78,6 +78,7 @@ public:
{
QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole));
QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight);
+ iconWatchonly = platformStyle->TextColorIcon(iconWatchonly);
iconWatchonly.paint(painter, watchonlyRect);
address_rect_min_width += 5 + watchonlyRect.width();
}
@@ -143,6 +144,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
ui(new Ui::OverviewPage),
clientModel(nullptr),
walletModel(nullptr),
+ m_platform_style{platformStyle},
txdelegate(new TxViewDelegate(platformStyle, this))
{
ui->setupUi(this);
@@ -150,7 +152,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
m_balances.balance = -1;
// use a SingleColorIcon for the "out of sync warning" icon
- QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
+ QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
ui->labelTransactionsStatus->setIcon(icon);
ui->labelWalletStatus->setIcon(icon);
@@ -298,6 +300,17 @@ void OverviewPage::setWalletModel(WalletModel *model)
updateDisplayUnit();
}
+void OverviewPage::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
+ ui->labelTransactionsStatus->setIcon(icon);
+ ui->labelWalletStatus->setIcon(icon);
+ }
+#endif
+}
+
void OverviewPage::updateDisplayUnit()
{
if(walletModel && walletModel->getOptionsModel())
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 5158c81678..755a107a00 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -45,6 +45,9 @@ Q_SIGNALS:
void transactionClicked(const QModelIndex &index);
void outOfSyncWarningClicked();
+protected:
+ void changeEvent(QEvent* e) override;
+
private:
Ui::OverviewPage *ui;
ClientModel *clientModel;
@@ -52,6 +55,8 @@ private:
interfaces::WalletBalances m_balances;
bool m_privacy{false};
+ const PlatformStyle* m_platform_style;
+
TxViewDelegate *txdelegate;
std::unique_ptr<TransactionFilterProxy> filter;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 58f6c14e7a..098fe5ac61 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -49,9 +49,9 @@ static QString ipcServerName()
QString name("BitcoinQt");
// Append a simple hash of the datadir
- // Note that GetDataDir(true) returns a different path
+ // Note that gArgs.GetDataDirNet() returns a different path
// for -testnet versus main net
- QString ddir(GUIUtil::boostPathToQString(GetDataDir(true)));
+ QString ddir(GUIUtil::boostPathToQString(gArgs.GetDataDirNet()));
name.append(QString::number(qHash(ddir)));
return name;
@@ -232,7 +232,10 @@ void PaymentServer::handleURIOrFile(const QString& s)
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient))
{
- if (!IsValidDestinationString(recipient.address.toStdString())) {
+ std::string error_msg;
+ const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg);
+
+ if (!IsValidDestination(dest)) {
if (uri.hasQueryItem("r")) { // payment request
Q_EMIT message(tr("URI handling"),
tr("Cannot process payment request because BIP70 is not supported.\n"
@@ -240,7 +243,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
"If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
CClientUIInterface::ICON_WARNING);
}
- Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
+ Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg),
CClientUIInterface::MSG_ERROR);
}
else
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 3459bf4cf8..fff079d5db 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -11,56 +11,19 @@
#include <utility>
-#include <QDebug>
#include <QList>
#include <QTimer>
-bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
-{
- const CNodeStats *pLeft = &(left.nodeStats);
- const CNodeStats *pRight = &(right.nodeStats);
-
- if (order == Qt::DescendingOrder)
- std::swap(pLeft, pRight);
-
- 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_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;
- } // no default case, so the compiler can warn about missing cases
- assert(false);
-}
-
// private implementation
class PeerTablePriv
{
public:
/** Local cache of peer information */
QList<CNodeCombinedStats> cachedNodeStats;
- /** Column to sort nodes by (default to unsorted) */
- int sortColumn{-1};
- /** Order (ascending or descending) to sort nodes by */
- Qt::SortOrder sortOrder;
- /** Index of rows by node ID */
- std::map<NodeId, int> mapNodeRows;
/** Pull a full list of peers from vNodes into our cache */
void refreshPeers(interfaces::Node& node)
{
- {
cachedNodeStats.clear();
interfaces::Node::NodesStats nodes_stats;
@@ -74,17 +37,6 @@ public:
stats.nodeStateStats = std::get<2>(node_stats);
cachedNodeStats.append(stats);
}
- }
-
- if (sortColumn >= 0)
- // sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily)
- std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
-
- // build index map
- mapNodeRows.clear();
- int row = 0;
- for (const CNodeCombinedStats& stats : cachedNodeStats)
- mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
}
int size() const
@@ -162,7 +114,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
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);
+ return QString::fromStdString((rec->nodeStats.fInbound ? "↓ " : "↑ ") + rec->nodeStats.addrName);
case ConnectionType:
return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false);
case Network:
@@ -180,6 +132,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
} else if (role == Qt::TextAlignmentRole) {
switch (column) {
case NetNodeId:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case Address:
return {};
case ConnectionType:
@@ -194,10 +147,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
} // 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);
- default: return QVariant();
- }
+ return QVariant::fromValue(rec);
}
return QVariant();
@@ -239,19 +189,3 @@ void PeerTableModel::refresh()
priv->refreshPeers(m_node);
Q_EMIT layoutChanged();
}
-
-int PeerTableModel::getRowByNodeId(NodeId nodeid)
-{
- std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
- if (it == priv->mapNodeRows.end())
- return -1;
-
- return it->second;
-}
-
-void PeerTableModel::sort(int column, Qt::SortOrder order)
-{
- priv->sortColumn = column;
- priv->sortOrder = order;
- refresh();
-}
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 0823235ec0..3d195342f1 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -30,18 +30,6 @@ struct CNodeCombinedStats {
};
Q_DECLARE_METATYPE(CNodeCombinedStats*)
-class NodeLessThan
-{
-public:
- NodeLessThan(int nColumn, Qt::SortOrder fOrder) :
- column(nColumn), order(fOrder) {}
- bool operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const;
-
-private:
- int column;
- Qt::SortOrder order;
-};
-
/**
Qt model providing information about connected peers, similar to the
"getpeerinfo" RPC call. Used by the rpc console UI.
@@ -53,7 +41,6 @@ class PeerTableModel : public QAbstractTableModel
public:
explicit PeerTableModel(interfaces::Node& node, QObject* parent);
~PeerTableModel();
- int getRowByNodeId(NodeId nodeid);
void startAutoRefresh();
void stopAutoRefresh();
@@ -80,7 +67,6 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
- void sort(int column, Qt::SortOrder order) override;
/*@}*/
public Q_SLOTS:
@@ -88,7 +74,31 @@ public Q_SLOTS:
private:
interfaces::Node& m_node;
- const QStringList columns{tr("Peer Id"), tr("Address"), tr("Type"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")};
+ const QStringList columns{
+ /*: Title of Peers Table column which contains a
+ unique number used to identify a connection. */
+ tr("Peer"),
+ /*: Title of Peers Table column which contains the
+ IP/Onion/I2P address of the connected peer. */
+ tr("Address"),
+ /*: Title of Peers Table column which describes the type of
+ peer connection. The "type" describes why the connection exists. */
+ tr("Type"),
+ /*: Title of Peers Table column which states the network the peer
+ connected through. */
+ tr("Network"),
+ /*: Title of Peers Table column which indicates the current latency
+ of the connection with the peer. */
+ tr("Ping"),
+ /*: Title of Peers Table column which indicates the total amount of
+ network information we have sent to the peer. */
+ tr("Sent"),
+ /*: Title of Peers Table column which indicates the total amount of
+ network information we have received from the peer. */
+ tr("Received"),
+ /*: Title of Peers Table column which contains the peer's
+ User Agent string. */
+ tr("User Agent")};
std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};
diff --git a/src/qt/peertablesortproxy.cpp b/src/qt/peertablesortproxy.cpp
new file mode 100644
index 0000000000..78932da8d4
--- /dev/null
+++ b/src/qt/peertablesortproxy.cpp
@@ -0,0 +1,43 @@
+// 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 <qt/peertablesortproxy.h>
+
+#include <qt/peertablemodel.h>
+#include <util/check.h>
+
+#include <QModelIndex>
+#include <QString>
+#include <QVariant>
+
+PeerTableSortProxy::PeerTableSortProxy(QObject* parent)
+ : QSortFilterProxyModel(parent)
+{
+}
+
+bool PeerTableSortProxy::lessThan(const QModelIndex& left_index, const QModelIndex& right_index) const
+{
+ const CNodeStats left_stats = Assert(sourceModel()->data(left_index, PeerTableModel::StatsRole).value<CNodeCombinedStats*>())->nodeStats;
+ const CNodeStats right_stats = Assert(sourceModel()->data(right_index, PeerTableModel::StatsRole).value<CNodeCombinedStats*>())->nodeStats;
+
+ switch (static_cast<PeerTableModel::ColumnIndex>(left_index.column())) {
+ case PeerTableModel::NetNodeId:
+ return left_stats.nodeid < right_stats.nodeid;
+ case PeerTableModel::Address:
+ return left_stats.addrName.compare(right_stats.addrName) < 0;
+ case PeerTableModel::ConnectionType:
+ return left_stats.m_conn_type < right_stats.m_conn_type;
+ case PeerTableModel::Network:
+ return left_stats.m_network < right_stats.m_network;
+ case PeerTableModel::Ping:
+ return left_stats.m_min_ping_time < right_stats.m_min_ping_time;
+ case PeerTableModel::Sent:
+ return left_stats.nSendBytes < right_stats.nSendBytes;
+ case PeerTableModel::Received:
+ return left_stats.nRecvBytes < right_stats.nRecvBytes;
+ case PeerTableModel::Subversion:
+ return left_stats.cleanSubVer.compare(right_stats.cleanSubVer) < 0;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+}
diff --git a/src/qt/peertablesortproxy.h b/src/qt/peertablesortproxy.h
new file mode 100644
index 0000000000..1879f6b400
--- /dev/null
+++ b/src/qt/peertablesortproxy.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef BITCOIN_QT_PEERTABLESORTPROXY_H
+#define BITCOIN_QT_PEERTABLESORTPROXY_H
+
+#include <QSortFilterProxyModel>
+
+QT_BEGIN_NAMESPACE
+class QModelIndex;
+QT_END_NAMESPACE
+
+class PeerTableSortProxy : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit PeerTableSortProxy(QObject* parent = nullptr);
+
+protected:
+ bool lessThan(const QModelIndex& left_index, const QModelIndex& right_index) const override;
+};
+
+#endif // BITCOIN_QT_PEERTABLESORTPROXY_H
diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp
index 1d0605c903..2257c2ad4f 100644
--- a/src/qt/platformstyle.cpp
+++ b/src/qt/platformstyle.cpp
@@ -71,25 +71,28 @@ PlatformStyle::PlatformStyle(const QString &_name, bool _imagesOnButtons, bool _
name(_name),
imagesOnButtons(_imagesOnButtons),
colorizeIcons(_colorizeIcons),
- useExtraSpacing(_useExtraSpacing),
- singleColor(0,0,0),
- textColor(0,0,0)
+ useExtraSpacing(_useExtraSpacing)
+{
+}
+
+QColor PlatformStyle::TextColor() const
+{
+ return QApplication::palette().color(QPalette::WindowText);
+}
+
+QColor PlatformStyle::SingleColor() const
{
- // Determine icon highlighting color
if (colorizeIcons) {
const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight));
const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText));
const QColor colorText(QApplication::palette().color(QPalette::WindowText));
const int colorTextLightness = colorText.lightness();
- QColor colorbase;
- if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness))
- colorbase = colorHighlightBg;
- else
- colorbase = colorHighlightFg;
- singleColor = colorbase;
+ if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) {
+ return colorHighlightBg;
+ }
+ return colorHighlightFg;
}
- // Determine text color
- textColor = QColor(QApplication::palette().color(QPalette::WindowText));
+ return {0, 0, 0};
}
QImage PlatformStyle::SingleColorImage(const QString& filename) const
diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h
index 53632e56e2..9df0a1f985 100644
--- a/src/qt/platformstyle.h
+++ b/src/qt/platformstyle.h
@@ -21,8 +21,8 @@ public:
bool getImagesOnButtons() const { return imagesOnButtons; }
bool getUseExtraSpacing() const { return useExtraSpacing; }
- QColor TextColor() const { return textColor; }
- QColor SingleColor() const { return singleColor; }
+ QColor TextColor() const;
+ QColor SingleColor() const;
/** Colorize an image (given filename) with the icon color */
QImage SingleColorImage(const QString& filename) const;
@@ -43,9 +43,6 @@ private:
bool imagesOnButtons;
bool colorizeIcons;
bool useExtraSpacing;
- QColor singleColor;
- QColor textColor;
- /* ... more to come later */
};
#endif // BITCOIN_QT_PLATFORMSTYLE_H
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 17746b395b..99318c3bc0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -141,7 +141,8 @@ void PSBTOperationsDialog::saveTransaction() {
filename_suggestion.append(".psbt");
QString filename = GUIUtil::getSaveFileName(this,
tr("Save Transaction Data"), filename_suggestion,
- tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selected_filter);
+ //: Expanded name of the binary PSBT file format. See: BIP 174.
+ tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selected_filter);
if (filename.isEmpty()) {
return;
}
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index a71c8831e9..f5200bb5c0 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -27,12 +27,8 @@ QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(nullptr)
{
contextMenu = new QMenu(this);
- QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
- connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage);
- contextMenu->addAction(saveImageAction);
- QAction *copyImageAction = new QAction(tr("&Copy Image"), this);
- connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage);
- contextMenu->addAction(copyImageAction);
+ contextMenu->addAction(tr("Save Image…"), this, &QRImageWidget::saveImage);
+ contextMenu->addAction(tr("Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
@@ -122,7 +118,9 @@ void QRImageWidget::saveImage()
return;
QString fn = GUIUtil::getSaveFileName(
this, tr("Save QR Code"), QString(),
- tr("PNG Image", "Name of PNG file format") + QLatin1String(" (*.png)"), nullptr);
+ /*: Expanded name of the PNG file format.
+ See https://en.wikipedia.org/wiki/Portable_Network_Graphics */
+ tr("PNG Image") + QLatin1String(" (*.png)"), nullptr);
if (!fn.isEmpty())
{
exportImage().save(fn);
diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp
index 76f94ecf85..5672396819 100644
--- a/src/qt/qvaluecombobox.cpp
+++ b/src/qt/qvaluecombobox.cpp
@@ -7,7 +7,7 @@
QValueComboBox::QValueComboBox(QWidget *parent) :
QComboBox(parent), role(Qt::UserRole)
{
- connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &QValueComboBox::handleSelectionChanged);
+ connect(this, qOverload<int>(&QComboBox::currentIndexChanged), this, &QValueComboBox::handleSelectionChanged);
}
QVariant QValueComboBox::value() const
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 61cb89d75a..3f4d7f85e6 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -42,28 +42,14 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
}
- // context menu actions
- QAction *copyURIAction = new QAction(tr("Copy URI"), 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);
-
- // context menu signals
+ contextMenu->addAction(tr("Copy URI"), this, &ReceiveCoinsDialog::copyURI);
+ contextMenu->addAction(tr("Copy address"), this, &ReceiveCoinsDialog::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &ReceiveCoinsDialog::copyLabel);
+ copyMessageAction = contextMenu->addAction(tr("Copy message"), this, &ReceiveCoinsDialog::copyMessage);
+ copyAmountAction = contextMenu->addAction(tr("Copy amount"), this, &ReceiveCoinsDialog::copyAmount);
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);
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 03531a1381..ec3d970a7f 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -10,18 +10,23 @@
#include <qt/walletmodel.h>
#include <clientversion.h>
+#include <interfaces/wallet.h>
+#include <key_io.h>
#include <streams.h>
+#include <util/string.h>
#include <utility>
+#include <QLatin1Char>
+#include <QLatin1String>
+
RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
{
// Load entries from wallet
- std::vector<std::string> vReceiveRequests;
- parent->loadReceiveRequests(vReceiveRequests);
- for (const std::string& request : vReceiveRequests)
+ for (const std::string& request : parent->wallet().getAddressReceiveRequests()) {
addNewRequest(request);
+ }
/* These columns must match the indices in the ColumnIndex enumeration */
columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle();
@@ -124,7 +129,11 @@ void RecentRequestsTableModel::updateAmountColumnTitle()
/** Gets title for amount column including current display unit if optionsModel reference available. */
QString RecentRequestsTableModel::getAmountTitle()
{
- return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : "";
+ if (!walletModel->getOptionsModel()) return {};
+ return tr("Requested") +
+ QLatin1String(" (") +
+ BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) +
+ QLatin1Char(')');
}
QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const
@@ -143,7 +152,7 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex
for (int i = 0; i < count; ++i)
{
const RecentRequestEntry* rec = &list[row+i];
- if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, ""))
+ if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(rec->recipient.address.toStdString()), ToString(rec->id), ""))
return false;
}
@@ -172,7 +181,7 @@ void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << newEntry;
- if (!walletModel->saveReceiveRequest(recipient.address.toStdString(), newEntry.id, ss.str()))
+ if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(recipient.address.toStdString()), ToString(newEntry.id), ss.str()))
return;
addNewRequest(newEntry);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 34d055e5a5..e6e767c5af 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -9,13 +9,14 @@
#include <qt/rpcconsole.h>
#include <qt/forms/ui_debugwindow.h>
+#include <chainparams.h>
+#include <interfaces/node.h>
+#include <netbase.h>
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
+#include <qt/peertablesortproxy.h>
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
-#include <chainparams.h>
-#include <interfaces/node.h>
-#include <netbase.h>
#include <rpc/client.h>
#include <rpc/server.h>
#include <util/strencodings.h>
@@ -33,9 +34,12 @@
#include <wallet/wallet.h>
#endif
+#include <QAbstractButton>
#include <QDateTime>
#include <QFont>
#include <QKeyEvent>
+#include <QLatin1String>
+#include <QLocale>
#include <QMenu>
#include <QMessageBox>
#include <QScreen>
@@ -43,9 +47,10 @@
#include <QSettings>
#include <QString>
#include <QStringList>
+#include <QStyledItemDelegate>
#include <QTime>
#include <QTimer>
-
+#include <QVariant>
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
@@ -127,6 +132,20 @@ public:
}
};
+class PeerIdViewDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit PeerIdViewDelegate(QObject* parent = nullptr)
+ : QStyledItemDelegate(parent) {}
+
+ QString displayText(const QVariant& value, const QLocale& locale) const override
+ {
+ // Additional spaces should visually separate right-aligned content
+ // from the next column to the right.
+ return value.toString() + QLatin1String(" ");
+ }
+};
#include <qt/rpcconsole.moc>
@@ -453,12 +472,23 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
{
ui->setupUi(this);
QSettings settings;
- if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
- // Restore failed (perhaps missing setting), center the window
- move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
+#ifdef ENABLE_WALLET
+ if (WalletModel::isWalletEnabled()) {
+ // RPCConsole widget is a window.
+ if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
+ // Restore failed (perhaps missing setting), center the window
+ move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
+ }
+ ui->splitter->restoreState(settings.value("RPCConsoleWindowPeersTabSplitterSizes").toByteArray());
+ } else
+#endif // ENABLE_WALLET
+ {
+ // RPCConsole is a child widget.
+ ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray());
}
- ui->splitter->restoreState(settings.value("PeersTabSplitterSizes").toByteArray());
+ m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
+ m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
@@ -486,17 +516,27 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->openDebugLogfileButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
}
ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
+
ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(":/icons/fontbigger"));
+ //: Main shortcut to increase the RPC console font size.
+ ui->fontBiggerButton->setShortcut(tr("Ctrl++"));
+ //: Secondary shortcut to increase the RPC console font size.
+ GUIUtil::AddButtonShortcut(ui->fontBiggerButton, tr("Ctrl+="));
+
ui->fontSmallerButton->setIcon(platformStyle->SingleColorIcon(":/icons/fontsmaller"));
+ //: Main shortcut to decrease the RPC console font size.
+ ui->fontSmallerButton->setShortcut(tr("Ctrl+-"));
+ //: Secondary shortcut to decrease the RPC console font size.
+ GUIUtil::AddButtonShortcut(ui->fontSmallerButton, tr("Ctrl+_"));
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
- connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
- connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
- connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
+ connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
+ connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
+ connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
// disable the wallet selector by default
@@ -521,8 +561,21 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
RPCConsole::~RPCConsole()
{
QSettings settings;
- settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
- settings.setValue("PeersTabSplitterSizes", ui->splitter->saveState());
+#ifdef ENABLE_WALLET
+ if (WalletModel::isWalletEnabled()) {
+ // RPCConsole widget is a window.
+ settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
+ settings.setValue("RPCConsoleWindowPeersTabSplitterSizes", ui->splitter->saveState());
+ } else
+#endif // ENABLE_WALLET
+ {
+ // RPCConsole is a child widget.
+ settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState());
+ }
+
+ settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state);
+ settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state);
+
m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
@@ -606,46 +659,32 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
connect(model, &ClientModel::mempoolSizeChanged, this, &RPCConsole::setMempoolSize);
// set up peer table
- ui->peerWidget->setModel(model->getPeerTableModel());
+ ui->peerWidget->setModel(model->peerTableSortProxy());
ui->peerWidget->verticalHeader()->hide();
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
- ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
- // create peer table context menu actions
- QAction* disconnectAction = new QAction(tr("&Disconnect"), 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);
+ if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
+ ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+ }
+ ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
+ ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
// create peer table context menu
peersTableContextMenu = new QMenu(this);
- peersTableContextMenu->addAction(disconnectAction);
- peersTableContextMenu->addAction(banAction1h);
- peersTableContextMenu->addAction(banAction24h);
- peersTableContextMenu->addAction(banAction7d);
- peersTableContextMenu->addAction(banAction365d);
-
- connect(banAction1h, &QAction::triggered, [this] { banSelectedNode(60 * 60); });
- connect(banAction24h, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24); });
- connect(banAction7d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 7); });
- connect(banAction365d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 365); });
-
- // peer table context menu signals
+ peersTableContextMenu->addAction(tr("Disconnect"), this, &RPCConsole::disconnectSelectedNode);
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 hour"), [this] { banSelectedNode(60 * 60); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 day"), [this] { banSelectedNode(60 * 60 * 24); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
- connect(disconnectAction, &QAction::triggered, this, &RPCConsole::disconnectSelectedNode);
// peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
- // peer table signal handling - update peer details when new nodes are added to the model
- connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::peerLayoutChanged);
- // peer table signal handling - cache selected node ids
- connect(model->getPeerTableModel(), &PeerTableModel::layoutAboutToBeChanged, this, &RPCConsole::peerLayoutAboutToChange);
+ connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::updateDetailWidget);
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
@@ -653,20 +692,17 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
- ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
- ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
- // create ban table context menu action
- QAction* unbanAction = new QAction(tr("&Unban"), this);
+ if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) {
+ ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
+ ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+ }
+ ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu
banTableContextMenu = new QMenu(this);
- banTableContextMenu->addAction(unbanAction);
-
- // ban table context menu signals
+ banTableContextMenu->addAction(tr("Unban"), this, &RPCConsole::unbanSelectedNode);
connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
- connect(unbanAction, &QAction::triggered, this, &RPCConsole::unbanSelectedNode);
// ban table signal handling - clear peer details when clicking a peer in the ban table
connect(ui->banlistWidget, &QTableView::clicked, this, &RPCConsole::clearSelectedNode);
@@ -776,20 +812,15 @@ void RPCConsole::setFontSize(int newSize)
// clear console (reset icon sizes, default stylesheet) and re-add the content
float oldPosFactor = 1.0 / ui->messagesWidget->verticalScrollBar()->maximum() * ui->messagesWidget->verticalScrollBar()->value();
- clear(false);
+ clear(/* keep_prompt */ true);
ui->messagesWidget->setHtml(str);
ui->messagesWidget->verticalScrollBar()->setValue(oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
}
-void RPCConsole::clear(bool clearHistory)
+void RPCConsole::clear(bool keep_prompt)
{
ui->messagesWidget->clear();
- if(clearHistory)
- {
- history.clear();
- historyPtr = 0;
- }
- ui->lineEdit->clear();
+ if (!keep_prompt) ui->lineEdit->clear();
ui->lineEdit->setFocus();
// Add smoothly scaled icon images.
@@ -816,20 +847,29 @@ void RPCConsole::clear(bool clearHistory)
).arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize))
);
-#ifdef Q_OS_MAC
- QString clsKey = "(⌘)-L";
-#else
- QString clsKey = "Ctrl-L";
-#endif
-
- message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(PACKAGE_NAME) + "<br>" +
- tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" +
- tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" +
- tr("For more information on using this console type %1.").arg("<b>help-console</b>") +
- "<br><span class=\"secwarning\"><br>" +
- tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.") +
- "</span>"),
- true);
+ static const QString welcome_message =
+ /*: RPC console welcome message.
+ Placeholders %7 and %8 are style tags for the warning content, and
+ they are not space separated from the rest of the text intentionally. */
+ tr("Welcome to the %1 RPC console.\n"
+ "Use up and down arrows to navigate history, and %2 to clear screen.\n"
+ "Use %3 and %4 to increase or decrease the font size.\n"
+ "Type %5 for an overview of available commands.\n"
+ "For more information on using this console, type %6.\n"
+ "\n"
+ "%7WARNING: Scammers have been active, telling users to type"
+ " commands here, stealing their wallet contents. Do not use this console"
+ " without fully understanding the ramifications of a command.%8")
+ .arg(PACKAGE_NAME,
+ "<b>" + ui->clearButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
+ "<b>" + ui->fontBiggerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
+ "<b>" + ui->fontSmallerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
+ "<b>help</b>",
+ "<b>help-console</b>",
+ "<span class=\"secwarning\">",
+ "<span>");
+
+ message(CMD_REPLY, welcome_message, true);
}
void RPCConsole::keyPressEvent(QKeyEvent *event)
@@ -840,6 +880,25 @@ void RPCConsole::keyPressEvent(QKeyEvent *event)
}
}
+void RPCConsole::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ ui->clearButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontbigger")));
+ ui->fontSmallerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontsmaller")));
+ ui->promptIcon->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/prompticon")));
+
+ for (int i = 0; ICON_MAPPING[i].url; ++i) {
+ ui->messagesWidget->document()->addResource(
+ QTextDocument::ImageResource,
+ QUrl(ICON_MAPPING[i].url),
+ platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize * 2, consoleFontSize * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ }
+ }
+#endif
+}
+
void RPCConsole::message(int category, const QString &message, bool html)
{
QTime time = QTime::currentTime();
@@ -902,57 +961,71 @@ void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage)
void RPCConsole::on_lineEdit_returnPressed()
{
- QString cmd = ui->lineEdit->text();
+ QString cmd = ui->lineEdit->text().trimmed();
- if(!cmd.isEmpty())
- {
- std::string strFilteredCmd;
- try {
- std::string dummy;
- if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) {
- // Failed to parse command, so we cannot even filter it for the history
- throw std::runtime_error("Invalid command line");
- }
- } catch (const std::exception& e) {
- QMessageBox::critical(this, "Error", QString("Error: ") + QString::fromStdString(e.what()));
- return;
+ if (cmd.isEmpty()) {
+ return;
+ }
+
+ std::string strFilteredCmd;
+ try {
+ std::string dummy;
+ if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) {
+ // Failed to parse command, so we cannot even filter it for the history
+ throw std::runtime_error("Invalid command line");
}
+ } catch (const std::exception& e) {
+ QMessageBox::critical(this, "Error", QString("Error: ") + QString::fromStdString(e.what()));
+ return;
+ }
+
+ // A special case allows to request shutdown even a long-running command is executed.
+ if (cmd == QLatin1String("stop")) {
+ std::string dummy;
+ RPCExecuteCommandLine(m_node, dummy, cmd.toStdString());
+ return;
+ }
- ui->lineEdit->clear();
+ if (m_is_executing) {
+ return;
+ }
- cmdBeforeBrowsing = QString();
+ ui->lineEdit->clear();
#ifdef ENABLE_WALLET
- WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>();
+ WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>();
- if (m_last_wallet_model != wallet_model) {
- if (wallet_model) {
- message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(wallet_model->getWalletName()));
- } else {
- message(CMD_REQUEST, tr("Executing command without any wallet"));
- }
- m_last_wallet_model = wallet_model;
+ if (m_last_wallet_model != wallet_model) {
+ if (wallet_model) {
+ message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(wallet_model->getWalletName()));
+ } else {
+ message(CMD_REQUEST, tr("Executing command without any wallet"));
}
-#endif
-
- message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
- Q_EMIT cmdRequest(cmd, m_last_wallet_model);
-
- cmd = QString::fromStdString(strFilteredCmd);
-
- // Remove command, if already in history
- history.removeOne(cmd);
- // Append command to history
- history.append(cmd);
- // Enforce maximum history size
- while(history.size() > CONSOLE_HISTORY)
- history.removeFirst();
- // Set pointer to end of history
- historyPtr = history.size();
+ m_last_wallet_model = wallet_model;
+ }
+#endif // ENABLE_WALLET
- // Scroll console view to end
- scrollToEnd();
+ message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
+ //: A console message indicating an entered command is currently being executed.
+ message(CMD_REPLY, tr("Executing…"));
+ m_is_executing = true;
+ Q_EMIT cmdRequest(cmd, m_last_wallet_model);
+
+ cmd = QString::fromStdString(strFilteredCmd);
+
+ // Remove command, if already in history
+ history.removeOne(cmd);
+ // Append command to history
+ history.append(cmd);
+ // Enforce maximum history size
+ while (history.size() > CONSOLE_HISTORY) {
+ history.removeFirst();
}
+ // Set pointer to end of history
+ historyPtr = history.size();
+
+ // Scroll console view to end
+ scrollToEnd();
}
void RPCConsole::browseHistory(int offset)
@@ -982,7 +1055,13 @@ void RPCConsole::startExecutor()
executor->moveToThread(&thread);
// Replies from executor object must go to this object
- connect(executor, &RPCExecutor::reply, this, static_cast<void (RPCConsole::*)(int, const QString&)>(&RPCConsole::message));
+ connect(executor, &RPCExecutor::reply, this, [this](int category, const QString& command) {
+ // Remove "Executing…" message.
+ ui->messagesWidget->undo();
+ message(category, command);
+ scrollToEnd();
+ m_is_executing = false;
+ });
// Requests from this object must go to executor
connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request);
@@ -1035,67 +1114,6 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
}
-void RPCConsole::peerLayoutAboutToChange()
-{
- cachedNodeids.clear();
- for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) {
- const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>();
- cachedNodeids.append(stats->nodeStats.nodeid);
- }
-}
-
-void RPCConsole::peerLayoutChanged()
-{
- if (!clientModel || !clientModel->getPeerTableModel())
- return;
-
- bool fUnselect = false;
- bool fReselect = false;
-
- if (cachedNodeids.empty()) // no node selected yet
- return;
-
- // find the currently selected row
- int selectedRow = -1;
- QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes();
- if (!selectedModelIndex.isEmpty()) {
- selectedRow = selectedModelIndex.first().row();
- }
-
- // check if our detail node has a row in the table (it may not necessarily
- // be at selectedRow since its position can change after a layout change)
- int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
-
- if (detailNodeRow < 0)
- {
- // detail node disappeared from table (node disconnected)
- fUnselect = true;
- }
- else
- {
- if (detailNodeRow != selectedRow)
- {
- // detail node moved position
- fUnselect = true;
- fReselect = true;
- }
- }
-
- if (fUnselect && selectedRow >= 0) {
- clearSelectedNode();
- }
-
- if (fReselect)
- {
- for(int i = 0; i < cachedNodeids.size(); i++)
- {
- ui->peerWidget->selectRow(clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.at(i)));
- }
- }
-
- updateDetailWidget();
-}
-
void RPCConsole::updateDetailWidget()
{
const QList<QModelIndex> selected_peers = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
@@ -1107,7 +1125,7 @@ void RPCConsole::updateDetailWidget()
const auto stats = selected_peers.first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>();
// update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " ");
- peerAddrDetails += tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid));
+ peerAddrDetails += tr("(peer: %1)").arg(QString::number(stats->nodeStats.nodeid));
if (!stats->nodeStats.addrLocal.empty())
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);
@@ -1118,7 +1136,7 @@ void RPCConsole::updateDetailWidget()
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()};
+ const int64_t time_now{GetTimeSeconds()};
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));
@@ -1133,7 +1151,7 @@ void RPCConsole::updateDetailWidget()
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
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) {
+ if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) {
ui->peerPermissions->setText(ts.na);
} else {
QStringList permissions;
@@ -1184,6 +1202,11 @@ void RPCConsole::showEvent(QShowEvent *event)
void RPCConsole::hideEvent(QHideEvent *event)
{
+ // It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of
+ // the columns of QTableView child widgets will have zero width at that moment.
+ m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState();
+ m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState();
+
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel())
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index b9806e40c9..2412ae543c 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -10,9 +10,10 @@
#include <net.h>
-#include <QWidget>
+#include <QByteArray>
#include <QCompleter>
#include <QThread>
+#include <QWidget>
class ClientModel;
class PlatformStyle;
@@ -74,6 +75,7 @@ public:
protected:
virtual bool eventFilter(QObject* obj, QEvent *event) override;
void keyPressEvent(QKeyEvent *) override;
+ void changeEvent(QEvent* e) override;
private Q_SLOTS:
void on_lineEdit_returnPressed();
@@ -99,7 +101,7 @@ private Q_SLOTS:
void updateDetailWidget();
public Q_SLOTS:
- void clear(bool clearHistory = true);
+ void clear(bool keep_prompt = false);
void fontBigger();
void fontSmaller();
void setFontSize(int newSize);
@@ -118,10 +120,6 @@ public Q_SLOTS:
void browseHistory(int offset);
/** Scroll console view to end */
void scrollToEnd();
- /** Handle selection caching before update */
- void peerLayoutAboutToChange();
- /** Handle updated peer information */
- void peerLayoutChanged();
/** Disconnect a selected node on the Peers tab */
void disconnectSelectedNode();
/** Ban a selected node on the Peers tab */
@@ -169,6 +167,9 @@ private:
QCompleter *autoCompleter = nullptr;
QThread thread;
WalletModel* m_last_wallet_model{nullptr};
+ bool m_is_executing{false};
+ QByteArray m_peer_widget_header_state;
+ QByteArray m_banlist_widget_header_state;
/** Update UI with latest network info from model. */
void updateNetworkState();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 95e1ce2210..160b43324f 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -129,6 +129,8 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui->customFee->SetAllowEmpty(false);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
+
+ GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
}
void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
@@ -171,15 +173,15 @@ void SendCoinsDialog::setModel(WalletModel *_model)
for (const int n : confTargets) {
ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
}
- connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
- connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
+ connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
+ connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls);
connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels);
#else
- connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
- connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
+ connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
+ connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
#endif
connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
@@ -269,7 +271,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
WalletModel::SendCoinsReturn prepareStatus;
- updateCoinControlState(*m_coin_control);
+ updateCoinControlState();
prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control);
@@ -366,7 +368,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
if (formatted.size() > 1) {
question_string = question_string.arg("");
- informative_text = tr("To review recipient list click \"Show Details...\"");
+ informative_text = tr("To review recipient list click \"Show Details…\"");
detailed_text = formatted.join("\n\n");
} else {
question_string = question_string.arg("<br /><br />" + formatted.at(0));
@@ -375,7 +377,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
return true;
}
-void SendCoinsDialog::on_sendButton_clicked()
+void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
{
if(!model || !model->getOptionsModel())
return;
@@ -430,7 +432,8 @@ void SendCoinsDialog::on_sendButton_clicked()
fileNameSuggestion.append(".psbt");
QString filename = GUIUtil::getSaveFileName(this,
tr("Save Transaction Data"), fileNameSuggestion,
- tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selectedFilter);
+ //: Expanded name of the binary PSBT file format. See: BIP 174.
+ tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter);
if (filename.isEmpty()) {
return;
}
@@ -734,23 +737,23 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
if (ui->radioSmartFee->isChecked())
ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
else {
- ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB");
+ ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kvB");
}
}
-void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
+void SendCoinsDialog::updateCoinControlState()
{
if (ui->radioCustomFee->isChecked()) {
- ctrl.m_feerate = CFeeRate(ui->customFee->value());
+ m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
} else {
- ctrl.m_feerate.reset();
+ m_coin_control->m_feerate.reset();
}
// Avoid using global defaults when sending money from the GUI
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
- ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
- ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked();
+ m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
+ m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
// Include watch-only for wallets without private key
- ctrl.fAllowWatchOnly = model->wallet().privateKeysDisabled();
+ m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
}
void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
@@ -763,13 +766,13 @@ void SendCoinsDialog::updateSmartFeeLabel()
{
if(!model || !model->getOptionsModel())
return;
- updateCoinControlState(*m_coin_control);
+ updateCoinControlState();
m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
int returned_target;
FeeReason reason;
CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
- ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
+ ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kvB");
if (reason == FeeReason::FALLBACK) {
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
@@ -837,8 +840,9 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
{
ui->frameCoinControl->setVisible(checked);
- if (!checked && model) // coin control features disabled
- m_coin_control->SetNull();
+ if (!checked && model) { // coin control features disabled
+ m_coin_control = std::make_unique<CCoinControl>();
+ }
coinControlUpdateLabels();
}
@@ -926,7 +930,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
if (!model || !model->getOptionsModel())
return;
- updateCoinControlState(*m_coin_control);
+ updateCoinControlState();
// set pay amounts
CoinControlDialog::payAmounts.clear();
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 4fc2f57cd6..33736f8095 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -76,11 +76,10 @@ private:
// Format confirmation message
bool PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text);
void updateFeeMinimizedLabel();
- // Update the passed in CCoinControl with state from the GUI
- void updateCoinControlState(CCoinControl& ctrl);
+ void updateCoinControlState();
private Q_SLOTS:
- void on_sendButton_clicked();
+ void sendButtonClicked(bool checked);
void on_buttonChooseFee_clicked();
void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry* entry);
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index 444dc79a2e..f701bb9615 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -236,6 +236,19 @@ void SendCoinsEntry::updateDisplayUnit()
}
}
+void SendCoinsEntry::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
+ ui->pasteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
+ ui->deleteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ }
+#endif
+}
+
bool SendCoinsEntry::updateLabel(const QString &address)
{
if(!model)
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index 254cc186e2..e682e6423a 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -69,6 +69,9 @@ private Q_SLOTS:
void on_pasteButton_clicked();
void updateDisplayUnit();
+protected:
+ void changeEvent(QEvent* e) override;
+
private:
SendCoinsRecipient recipient;
Ui::SendCoinsEntry *ui;
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 2e7df60574..b982cc577d 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -283,3 +283,19 @@ bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event)
}
return QDialog::eventFilter(object, event);
}
+
+void SignVerifyMessageDialog::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
+ ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
+ ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editcopy")));
+ ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/edit")));
+ ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
+ ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/transaction_0")));
+ ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
+ }
+#endif
+}
diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h
index d98cb290a1..3d2d5f281e 100644
--- a/src/qt/signverifymessagedialog.h
+++ b/src/qt/signverifymessagedialog.h
@@ -31,6 +31,7 @@ public:
protected:
bool eventFilter(QObject *object, QEvent *event) override;
+ void changeEvent(QEvent* e) override;
private:
Ui::SignVerifyMessageDialog *ui;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index a026069232..39c69fe184 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -63,8 +63,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
- bool firstRun;
- wallet->LoadWallet(firstRun);
+ wallet->LoadWallet();
auto build_address = [&wallet]() {
CKey key;
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 8dffd2f59f..cb3dbd2267 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -20,9 +20,9 @@
#endif
#include <QAction>
-#include <QEventLoop>
#include <QLineEdit>
#include <QScopedPointer>
+#include <QSignalSpy>
#include <QTest>
#include <QTextEdit>
#include <QtGlobal>
@@ -33,13 +33,14 @@ namespace {
//! Call getblockchaininfo RPC and check first field of JSON output.
void TestRpcCommand(RPCConsole* console)
{
- QEventLoop loop;
QTextEdit* messagesWidget = console->findChild<QTextEdit*>("messagesWidget");
- QObject::connect(messagesWidget, &QTextEdit::textChanged, &loop, &QEventLoop::quit);
QLineEdit* lineEdit = console->findChild<QLineEdit*>("lineEdit");
+ QSignalSpy mw_spy(messagesWidget, &QTextEdit::textChanged);
+ QVERIFY(mw_spy.isValid());
QTest::keyClicks(lineEdit, "getblockchaininfo");
QTest::keyClick(lineEdit, Qt::Key_Return);
- loop.exec();
+ QVERIFY(mw_spy.wait(1000));
+ QCOMPARE(mw_spy.count(), 4);
QString output = messagesWidget->toPlainText();
UniValue value;
value.read(output.right(output.size() - output.lastIndexOf(QChar::ObjectReplacementCharacter) - 1).toStdString());
@@ -64,7 +65,7 @@ void AppTests::appTests()
fs::create_directories([] {
BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
- return GetDataDir() / "blocks";
+ return gArgs.GetDataDirNet() / "blocks";
}());
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 0d9928d363..df3b85ea71 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -11,8 +11,10 @@
#include <univalue.h>
#include <util/system.h>
-#include <QDir>
-#include <QtGlobal>
+#include <QTest>
+
+#include <string>
+#include <stdexcept>
static RPCHelpMan rpcNestedTest_rpc()
{
@@ -24,23 +26,25 @@ static RPCHelpMan rpcNestedTest_rpc()
{"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
{"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
},
- {},
+ RPCResult{RPCResult::Type::ANY, "", ""},
RPCExamples{""},
- [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
return request.params.write(0, 0);
},
};
}
static const CRPCCommand vRPCCommands[] = {
- {"test", &rpcNestedTest_rpc},
+ {"rpcNestedTest", &rpcNestedTest_rpc},
};
void RPCNestedTests::rpcNestedTests()
{
// do some test setup
// could be moved to a more generic place when we add more tests on QT level
- tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
+ for (const auto& c : vRPCCommands) {
+ tableRPC.appendCommand(c.name, &c);
+ }
TestingSetup test;
m_node.setContext(&test.m_node);
@@ -68,13 +72,13 @@ void RPCNestedTests::rpcNestedTests()
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo "); //whitespace at the end will be tolerated
QVERIFY(result.substr(0,1) == "{");
- (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key
+ RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[\"chain\"]"); //Quote path identifier are allowed, but look after a child containing the quotes in the key
QVERIFY(result == "null");
- (RPCConsole::RPCExecuteCommandLine(m_node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed
- (RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed
+ RPCConsole::RPCExecuteCommandLine(m_node, result, "createrawtransaction [] {} 0"); //parameter not in brackets are allowed
+ RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction([],{},0)"); //parameter in brackets are allowed
QVERIFY(result == result2);
- (RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed
+ RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction( [], {} , 0 )"); //whitespace between parameters is allowed
QVERIFY(result == result2);
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock(getbestblockhash())[tx][0]", &filtered);
@@ -123,11 +127,10 @@ void RPCNestedTests::rpcNestedTests()
RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest( abc , cba )");
QVERIFY(result == "[\"abc\",\"cba\"]");
- // do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
- (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments
- (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()()()")); //tolerate non command brackets
+ RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo("); //tolerate non closing brackets if we have no arguments
+ RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()()()"); //tolerate non command brackets
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(True)"), UniValue); //invalid argument
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "a(getblockchaininfo(True))"), UniValue); //method not found
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tolerate empty arguments when using ,
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 1107c44dc9..ea35f80cf5 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -73,7 +73,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
if (status == CT_NEW) txid = hash;
}));
ConfirmSend();
- bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
+ bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "sendButtonClicked", Q_ARG(bool, false));
assert(invoked);
return txid;
}
@@ -140,8 +140,7 @@ void TestGUI(interfaces::Node& node)
}
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
- bool firstRun;
- wallet->LoadWallet(firstRun);
+ wallet->LoadWallet();
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -225,6 +224,7 @@ void TestGUI(interfaces::Node& node)
int initialRowCount = requestTableModel->rowCount({});
QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");
requestPaymentButton->click();
+ QString address;
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("ReceiveRequestDialog")) {
ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
@@ -233,10 +233,13 @@ void TestGUI(interfaces::Node& node)
QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();
QCOMPARE(uri.count("bitcoin:"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));
+ QVERIFY(address.isEmpty());
+ address = receiveRequestDialog->QObject::findChild<QLabel*>("address_content")->text();
+ QVERIFY(!address.isEmpty());
QCOMPARE(uri.count("amount=0.00000001"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));
- QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
+ QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString::fromStdString("0.00000001 " + CURRENCY_UNIT));
QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));
@@ -259,12 +262,30 @@ void TestGUI(interfaces::Node& node)
int currentRowCount = requestTableModel->rowCount({});
QCOMPARE(currentRowCount, initialRowCount+1);
+ // Check addition to wallet
+ std::vector<std::string> requests = walletModel.wallet().getAddressReceiveRequests();
+ QCOMPARE(requests.size(), size_t{1});
+ RecentRequestEntry entry;
+ CDataStream{MakeUCharSpan(requests[0]), SER_DISK, CLIENT_VERSION} >> entry;
+ QCOMPARE(entry.nVersion, int{1});
+ QCOMPARE(entry.id, int64_t{1});
+ QVERIFY(entry.date.isValid());
+ QCOMPARE(entry.recipient.address, address);
+ QCOMPARE(entry.recipient.label, QString{"TEST_LABEL_1"});
+ QCOMPARE(entry.recipient.amount, CAmount{1});
+ QCOMPARE(entry.recipient.message, QString{"TEST_MESSAGE_1"});
+ QCOMPARE(entry.recipient.sPaymentRequest, std::string{});
+ QCOMPARE(entry.recipient.authenticatedMerchant, QString{});
+
// Check Remove button
QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");
table->selectRow(currentRowCount-1);
QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
removeRequestButton->click();
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
+
+ // Check removal from wallet
+ QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(), size_t{0});
}
} // namespace
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index ece3a9cf48..02d220db20 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -26,6 +26,8 @@
#include <stdint.h>
#include <string>
+#include <QLatin1String>
+
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
{
if (!status.is_final)
@@ -38,14 +40,16 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i
else
{
int nDepth = status.depth_in_main_chain;
- if (nDepth < 0)
+ if (nDepth < 0) {
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
- else if (nDepth == 0)
- return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
- else if (nDepth < 6)
+ } else if (nDepth == 0) {
+ const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()};
+ return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned;
+ } else if (nDepth < 6) {
return tr("%1/unconfirmed").arg(nDepth);
- else
+ } else {
return tr("%1 confirmations").arg(nDepth);
+ }
}
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index a7556eed04..b68ceaedbb 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -25,6 +25,8 @@
#include <QDateTime>
#include <QDebug>
#include <QIcon>
+#include <QLatin1Char>
+#include <QLatin1String>
#include <QList>
@@ -96,18 +98,20 @@ public:
*/
QList<TransactionRecord> cachedWallet;
- bool fQueueNotifications = false;
+ /** True when model finishes loading all wallet transactions on start */
+ bool m_loaded = false;
+ /** True when transactions are being notified, for instance when scanning */
+ bool m_loading = false;
std::vector< TransactionNotification > vQueueNotifications;
void NotifyTransactionChanged(const uint256 &hash, ChangeType status);
- void ShowProgress(const std::string &title, int nProgress);
+ void DispatchNotifications();
/* Query entire wallet anew from core.
*/
void refreshWallet(interfaces::Wallet& wallet)
{
- qDebug() << "TransactionTablePriv::refreshWallet";
- cachedWallet.clear();
+ assert(!m_loaded);
{
for (const auto& wtx : wallet.getWalletTxs()) {
if (TransactionRecord::showTransaction()) {
@@ -115,6 +119,8 @@ public:
}
}
}
+ m_loaded = true;
+ DispatchNotifications();
}
/* Update our model of the wallet incrementally, to synchronize our model of the wallet
@@ -249,12 +255,12 @@ TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle
fProcessingQueuedTransactions(false),
platformStyle(_platformStyle)
{
+ subscribeToCoreSignals();
+
columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
priv->refreshWallet(walletModel->wallet());
connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &TransactionTableModel::updateDisplayUnit);
-
- subscribeToCoreSignals();
}
TransactionTableModel::~TransactionTableModel()
@@ -409,9 +415,9 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
{
QString watchAddress;
- if (tooltip) {
+ if (tooltip && wtx->involvesWatchAddress) {
// Mark transactions involving watch-only addresses by adding " (watch-only)"
- watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : "";
+ watchAddress = QLatin1String(" (") + tr("watch-only") + QLatin1Char(')');
}
switch(wtx->type)
@@ -719,7 +725,7 @@ void TransactionTablePriv::NotifyTransactionChanged(const uint256 &hash, ChangeT
TransactionNotification notification(hash, status, showTransaction);
- if (fQueueNotifications)
+ if (!m_loaded || m_loading)
{
vQueueNotifications.push_back(notification);
return;
@@ -727,36 +733,34 @@ void TransactionTablePriv::NotifyTransactionChanged(const uint256 &hash, ChangeT
notification.invoke(parent);
}
-void TransactionTablePriv::ShowProgress(const std::string &title, int nProgress)
+void TransactionTablePriv::DispatchNotifications()
{
- if (nProgress == 0)
- fQueueNotifications = true;
+ if (!m_loaded || m_loading) return;
- if (nProgress == 100)
+ if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons
+ bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
+ assert(invoked);
+ }
+ for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
{
- fQueueNotifications = false;
- if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons
- bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
+ if (vQueueNotifications.size() - i <= 10) {
+ bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
assert(invoked);
}
- for (unsigned int i = 0; i < vQueueNotifications.size(); ++i)
- {
- if (vQueueNotifications.size() - i <= 10) {
- bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
- assert(invoked);
- }
- vQueueNotifications[i].invoke(parent);
- }
- vQueueNotifications.clear();
+ vQueueNotifications[i].invoke(parent);
}
+ vQueueNotifications.clear();
}
void TransactionTableModel::subscribeToCoreSignals()
{
// Connect signals to wallet
m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(&TransactionTablePriv::NotifyTransactionChanged, priv, std::placeholders::_1, std::placeholders::_2));
- m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(&TransactionTablePriv::ShowProgress, priv, std::placeholders::_1, std::placeholders::_2));
+ m_handler_show_progress = walletModel->wallet().handleShowProgress([this](const std::string&, int progress) {
+ priv->m_loading = progress < 100;
+ priv->DispatchNotifications();
+ });
}
void TransactionTableModel::unsubscribeFromCoreSignals()
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 42e08c6af7..1e8e012dcf 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -37,8 +37,8 @@
#include <QUrl>
#include <QVBoxLayout>
-TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
- QWidget(parent)
+TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent)
+ : QWidget(parent), m_platform_style{platformStyle}
{
// Build filter row
setContentsMargins(0,0,0,0);
@@ -73,7 +73,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
dateWidget->addItem(tr("This month"), ThisMonth);
dateWidget->addItem(tr("Last month"), LastMonth);
dateWidget->addItem(tr("This year"), ThisYear);
- dateWidget->addItem(tr("Range..."), Range);
+ dateWidget->addItem(tr("Range…"), Range);
hlayout->addWidget(dateWidget);
typeWidget = new QComboBox(this);
@@ -161,54 +161,33 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
transactionView->horizontalHeader()->setStretchLastSection(true);
}
- // Actions
- abandonAction = new QAction(tr("Abandon transaction"), this);
- bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
- bumpFeeAction->setObjectName("bumpFeeAction");
- copyAddressAction = new QAction(tr("Copy address"), this);
- copyLabelAction = new QAction(tr("Copy label"), this);
- QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
- 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 address label"), this);
- QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
-
contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
- contextMenu->addAction(copyAddressAction);
- contextMenu->addAction(copyLabelAction);
- contextMenu->addAction(copyAmountAction);
- contextMenu->addAction(copyTxIDAction);
- contextMenu->addAction(copyTxHexAction);
- contextMenu->addAction(copyTxPlainText);
- contextMenu->addAction(showDetailsAction);
+ copyAddressAction = contextMenu->addAction(tr("Copy address"), this, &TransactionView::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &TransactionView::copyLabel);
+ contextMenu->addAction(tr("Copy amount"), this, &TransactionView::copyAmount);
+ contextMenu->addAction(tr("Copy transaction ID"), this, &TransactionView::copyTxID);
+ contextMenu->addAction(tr("Copy raw transaction"), this, &TransactionView::copyTxHex);
+ contextMenu->addAction(tr("Copy full transaction details"), this, &TransactionView::copyTxPlainText);
+ contextMenu->addAction(tr("Show transaction details"), this, &TransactionView::showDetails);
contextMenu->addSeparator();
- contextMenu->addAction(bumpFeeAction);
- contextMenu->addAction(abandonAction);
- contextMenu->addAction(editLabelAction);
-
- connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate);
- connect(typeWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseType);
- connect(watchOnlyWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseWatchonly);
- connect(amountWidget, &QLineEdit::textChanged, amount_typing_delay, static_cast<void (QTimer::*)()>(&QTimer::start));
+ bumpFeeAction = contextMenu->addAction(tr("Increase transaction fee"));
+ GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
+ bumpFeeAction->setObjectName("bumpFeeAction");
+ abandonAction = contextMenu->addAction(tr("Abandon transaction"), this, &TransactionView::abandonTx);
+ contextMenu->addAction(tr("Edit address label"), this, &TransactionView::editLabel);
+
+ connect(dateWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseDate);
+ connect(typeWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseType);
+ connect(watchOnlyWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseWatchonly);
+ connect(amountWidget, &QLineEdit::textChanged, amount_typing_delay, qOverload<>(&QTimer::start));
connect(amount_typing_delay, &QTimer::timeout, this, &TransactionView::changedAmount);
- connect(search_widget, &QLineEdit::textChanged, prefix_typing_delay, static_cast<void (QTimer::*)()>(&QTimer::start));
+ connect(search_widget, &QLineEdit::textChanged, prefix_typing_delay, qOverload<>(&QTimer::start));
connect(prefix_typing_delay, &QTimer::timeout, this, &TransactionView::changedSearch);
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);
- connect(copyAddressAction, &QAction::triggered, this, &TransactionView::copyAddress);
- connect(copyLabelAction, &QAction::triggered, this, &TransactionView::copyLabel);
- connect(copyAmountAction, &QAction::triggered, this, &TransactionView::copyAmount);
- connect(copyTxIDAction, &QAction::triggered, this, &TransactionView::copyTxID);
- connect(copyTxHexAction, &QAction::triggered, this, &TransactionView::copyTxHex);
- connect(copyTxPlainText, &QAction::triggered, this, &TransactionView::copyTxPlainText);
- connect(editLabelAction, &QAction::triggered, this, &TransactionView::editLabel);
- connect(showDetailsAction, &QAction::triggered, this, &TransactionView::showDetails);
// Double-clicking on a transaction on the transaction history page shows details
connect(this, &TransactionView::doubleClicked, this, &TransactionView::showDetails);
// Highlight transaction after fee bump
@@ -264,6 +243,20 @@ void TransactionView::setModel(WalletModel *_model)
}
}
+void TransactionView::changeEvent(QEvent* e)
+{
+#ifdef Q_OS_MACOS
+ if (e->type() == QEvent::PaletteChange) {
+ watchOnlyWidget->setItemIcon(
+ TransactionFilterProxy::WatchOnlyFilter_Yes,
+ m_platform_style->SingleColorIcon(QStringLiteral(":/icons/eye_plus")));
+ watchOnlyWidget->setItemIcon(
+ TransactionFilterProxy::WatchOnlyFilter_No,
+ m_platform_style->SingleColorIcon(QStringLiteral(":/icons/eye_minus")));
+ }
+#endif
+}
+
void TransactionView::chooseDate(int idx)
{
if (!transactionProxyModel) return;
@@ -357,7 +350,9 @@ void TransactionView::exportClicked()
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Transaction History"), QString(),
- tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr);
+ /*: Expanded name of the CSV file format.
+ See https://en.wikipedia.org/wiki/Comma-separated_values */
+ tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
return;
@@ -424,7 +419,7 @@ void TransactionView::abandonTx()
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
}
-void TransactionView::bumpFee()
+void TransactionView::bumpFee([[maybe_unused]] bool checked)
{
if(!transactionView || !transactionView->selectionModel())
return;
diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h
index 35ada4aa7a..3e2321d91b 100644
--- a/src/qt/transactionview.h
+++ b/src/qt/transactionview.h
@@ -60,6 +60,9 @@ public:
MINIMUM_COLUMN_WIDTH = 23
};
+protected:
+ void changeEvent(QEvent* e) override;
+
private:
WalletModel *model{nullptr};
TransactionFilterProxy *transactionProxyModel{nullptr};
@@ -85,6 +88,8 @@ private:
bool eventFilter(QObject *obj, QEvent *event) override;
+ const PlatformStyle* m_platform_style;
+
private Q_SLOTS:
void contextualMenu(const QPoint &);
void dateRangeChanged();
@@ -99,7 +104,7 @@ private Q_SLOTS:
void openThirdPartyTxUrl(QString url);
void updateWatchOnlyColumn(bool fHaveWatchOnly);
void abandonTx();
- void bumpFee();
+ void bumpFee(bool checked);
Q_SIGNALS:
void doubleClicked(const QModelIndex&);
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 05499b2a41..4d246bc8dc 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -142,7 +142,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
{
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(new QLabel(
- tr("%1 is shutting down...").arg(PACKAGE_NAME) + "<br /><br />" +
+ tr("%1 is shutting down…").arg(PACKAGE_NAME) + "<br /><br />" +
tr("Do not shut down the computer until this window disappears.")));
setLayout(layout);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 7a89325ddf..aa26a01541 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -207,6 +207,9 @@ void WalletControllerActivity::showProgressDialog(const QString& label_text)
m_progress_dialog->setCancelButton(nullptr);
m_progress_dialog->setWindowModality(Qt::ApplicationModal);
GUIUtil::PolishProgressDialog(m_progress_dialog);
+ // The setValue call forces QProgressDialog to start the internal duration estimation.
+ // See details in https://bugreports.qt.io/browse/QTBUG-47042.
+ m_progress_dialog->setValue(0);
}
void WalletControllerActivity::destroyProgressDialog()
@@ -247,7 +250,7 @@ void CreateWalletActivity::askPassphrase()
void CreateWalletActivity::createWallet()
{
- showProgressDialog(tr("Creating Wallet <b>%1</b>...").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
+ showProgressDialog(tr("Creating Wallet <b>%1</b>…").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
std::string name = m_create_wallet_dialog->walletName().toStdString();
uint64_t flags = 0;
@@ -330,7 +333,7 @@ void OpenWalletActivity::open(const std::string& path)
{
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
- showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
+ showProgressDialog(tr("Opening Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));
QTimer::singleShot(0, worker(), [this, path] {
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 02b3c62867..0f2ea99685 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -4,10 +4,7 @@
#include <qt/walletframe.h>
-#include <qt/bitcoingui.h>
-#include <qt/createwalletdialog.h>
#include <qt/overviewpage.h>
-#include <qt/walletcontroller.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
@@ -19,9 +16,8 @@
#include <QPushButton>
#include <QVBoxLayout>
-WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
- : QFrame(_gui),
- gui(_gui),
+WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
+ : QFrame(parent),
platformStyle(_platformStyle),
m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint())
{
@@ -42,11 +38,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
// A button for create wallet dialog
QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
- connect(create_wallet_button, &QPushButton::clicked, [this] {
- auto activity = new CreateWalletActivity(gui->getWalletController(), this);
- connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
- activity->create();
- });
+ connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
no_wallet_group->setLayout(no_wallet_layout);
@@ -66,17 +58,15 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
}
}
-bool WalletFrame::addWallet(WalletModel *walletModel)
+bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
{
- if (!gui || !clientModel || !walletModel) return false;
+ if (!clientModel || !walletModel) return false;
if (mapWalletViews.count(walletModel) > 0) return false;
- WalletView *walletView = new WalletView(platformStyle, this);
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
- walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@@ -88,17 +78,6 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletStack->addWidget(walletView);
mapWalletViews[walletModel] = walletView;
- connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
- connect(walletView, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
- gui->message(title, message, style);
- });
- connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
- connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
-
return true;
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index f57f8678d6..844ed121a0 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -8,7 +8,6 @@
#include <QFrame>
#include <QMap>
-class BitcoinGUI;
class ClientModel;
class PlatformStyle;
class SendCoinsRecipient;
@@ -31,12 +30,12 @@ class WalletFrame : public QFrame
Q_OBJECT
public:
- explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = nullptr);
+ explicit WalletFrame(const PlatformStyle* platformStyle, QWidget* parent);
~WalletFrame();
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel *walletModel);
+ bool addWallet(WalletModel* walletModel, WalletView* walletView);
void setCurrentWallet(WalletModel* wallet_model);
void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
@@ -51,9 +50,10 @@ Q_SIGNALS:
/** Notify that the user has requested more information about the out-of-sync warning */
void requestedSyncWarningInfo();
+ void createWalletButtonClicked();
+
private:
QStackedWidget *walletStack;
- BitcoinGUI *gui;
ClientModel *clientModel;
QMap<WalletModel*, WalletView*> mapWalletViews;
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 02254da3ce..7c58b8afd2 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -65,7 +65,10 @@ WalletModel::~WalletModel()
void WalletModel::startPollBalance()
{
// This timer will be fired repeatedly to update the balance
- connect(timer, &QTimer::timeout, this, &WalletModel::pollBalanceChanged);
+ // Since the QTimer::timeout is a private signal, it cannot be used
+ // in the GUIUtil::ExceptionSafeConnect directly.
+ connect(timer, &QTimer::timeout, this, &WalletModel::timerTimeout);
+ GUIUtil::ExceptionSafeConnect(this, &WalletModel::timerTimeout, this, &WalletModel::pollBalanceChanged);
timer->start(MODEL_UPDATE_DELAY);
}
@@ -245,7 +248,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *newTx;
- transaction_array.append((const char*)&(ssTx[0]), ssTx.size());
+ transaction_array.append((const char*)ssTx.data(), ssTx.size());
}
// Add addresses / update labels that we've sent to the address book,
@@ -463,25 +466,6 @@ void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
rhs.relock = false;
}
-void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
-{
- vReceiveRequests = m_wallet->getDestValues("rr"); // receive request
-}
-
-bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
-{
- CTxDestination dest = DecodeDestination(sAddress);
-
- std::stringstream ss;
- ss << nId;
- std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
-
- if (sRequest.empty())
- return m_wallet->eraseDestData(dest, key);
- else
- return m_wallet->addDestData(dest, key, sRequest);
-}
-
bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
{
CCoinControl coin_control;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 9a3c3f2f66..b2ce5d69fb 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -135,9 +135,6 @@ public:
UnlockContext requestUnlock();
- void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
- bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
-
bool bumpFee(uint256 hash, uint256& new_hash);
static bool isWalletEnabled();
@@ -223,6 +220,8 @@ Q_SIGNALS:
// Notify that there are now keys in the keypool
void canGetAddressesChanged();
+ void timerTimeout();
+
public Q_SLOTS:
/* Starts a timer to periodically update the balance */
void startPollBalance();
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 8612893683..cc9e1502f0 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -71,13 +71,13 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
connect(overviewPage, &OverviewPage::transactionClicked, this, &WalletView::transactionClicked);
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
- connect(overviewPage, &OverviewPage::transactionClicked, transactionView, static_cast<void (TransactionView::*)(const QModelIndex&)>(&TransactionView::focusTransaction));
+ connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
// Highlight transaction after send
- connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));
+ connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, qOverload<const uint256&>(&TransactionView::focusTransaction));
// Clicking on "Export" allows to export the transaction list
connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked);
@@ -273,7 +273,8 @@ void WalletView::backupWallet()
{
QString filename = GUIUtil::getSaveFileName(this,
tr("Backup Wallet"), QString(),
- tr("Wallet Data", "Name of wallet data file format") + QLatin1String(" (*.dat)"), nullptr);
+ //: Name of the wallet data file format.
+ tr("Wallet Data") + QLatin1String(" (*.dat)"), nullptr);
if (filename.isEmpty())
return;
@@ -330,7 +331,6 @@ void WalletView::showProgress(const QString &title, int nProgress)
progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100);
GUIUtil::PolishProgressDialog(progressDialog);
progressDialog->setWindowModality(Qt::ApplicationModal);
- progressDialog->setMinimumDuration(0);
progressDialog->setAutoClose(false);
progressDialog->setValue(0);
} else if (nProgress == 100) {
diff --git a/src/random.cpp b/src/random.cpp
index 9900825abb..174f4cef31 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -628,7 +628,7 @@ std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
if (requires_seed) RandomSeed();
std::vector<unsigned char> ret(len);
if (len > 0) {
- rng.Keystream(&ret[0], len);
+ rng.Keystream(ret.data(), len);
}
return ret;
}
diff --git a/src/rest.cpp b/src/rest.cpp
index aa97470ca7..747c7aea19 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -8,6 +8,7 @@
#include <core_io.h>
#include <httpserver.h>
#include <index/txindex.h>
+#include <node/blockstorage.h>
#include <node/context.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
@@ -106,6 +107,27 @@ static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
return node_context->mempool.get();
}
+/**
+ * Get the node context chainstatemanager.
+ *
+ * @param[in] req The HTTP request, whose status code will be set if node
+ * context chainstatemanager is not found.
+ * @returns Pointer to the chainstatemanager or nullptr if none found.
+ */
+static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
+{
+ auto node_context = util::AnyPtr<NodeContext>(context);
+ if (!node_context || !node_context->chainman) {
+ RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
+ strprintf("%s:%d (%s)\n"
+ "Internal bug detected: Chainman disabled or instance not found!\n"
+ "You may report this issue here: %s\n",
+ __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
+ return nullptr;
+ }
+ return node_context->chainman;
+}
+
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
{
const std::string::size_type pos = strReq.rfind('.');
@@ -180,14 +202,18 @@ static bool rest_headers(const std::any& context,
std::vector<const CBlockIndex *> headers;
headers.reserve(count);
{
+ ChainstateManager* maybe_chainman = GetChainman(context, req);
+ if (!maybe_chainman) return false;
+ ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
- tip = ::ChainActive().Tip();
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
+ CChain& active_chain = chainman.ActiveChain();
+ tip = active_chain.Tip();
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
+ while (pindex != nullptr && active_chain.Contains(pindex)) {
headers.push_back(pindex);
if (headers.size() == (unsigned long)count)
break;
- pindex = ::ChainActive().Next(pindex);
+ pindex = active_chain.Next(pindex);
}
}
@@ -231,7 +257,8 @@ static bool rest_headers(const std::any& context,
}
}
-static bool rest_block(HTTPRequest* req,
+static bool rest_block(const std::any& context,
+ HTTPRequest* req,
const std::string& strURIPart,
bool showTxDetails)
{
@@ -248,9 +275,12 @@ static bool rest_block(HTTPRequest* req,
CBlockIndex* pblockindex = nullptr;
CBlockIndex* tip = nullptr;
{
+ ChainstateManager* maybe_chainman = GetChainman(context, req);
+ if (!maybe_chainman) return false;
+ ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
- tip = ::ChainActive().Tip();
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ tip = chainman.ActiveChain().Tip();
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
@@ -297,12 +327,12 @@ static bool rest_block(HTTPRequest* req,
static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
{
- return rest_block(req, strURIPart, true);
+ return rest_block(context, req, strURIPart, true);
}
static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
{
- return rest_block(req, strURIPart, false);
+ return rest_block(context, req, strURIPart, false);
}
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
@@ -317,7 +347,8 @@ static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std:
switch (rf) {
case RetFormat::JSON: {
- JSONRPCRequest jsonRequest(context);
+ JSONRPCRequest jsonRequest;
+ jsonRequest.context = context;
jsonRequest.params = UniValue(UniValue::VARR);
UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
std::string strJSON = chainInfoObject.write() + "\n";
@@ -535,6 +566,9 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
std::string bitmapStringRepresentation;
std::vector<bool> hits;
bitmap.resize((vOutPoints.size() + 7) / 8);
+ ChainstateManager* maybe_chainman = GetChainman(context, req);
+ if (!maybe_chainman) return false;
+ ChainstateManager& chainman = *maybe_chainman;
{
auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) {
for (const COutPoint& vOutPoint : vOutPoints) {
@@ -550,12 +584,12 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
if (!mempool) return false;
// use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool->cs);
- CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
+ CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, *mempool);
process_utxos(viewMempool, *mempool);
} else {
LOCK(cs_main); // no need to lock mempool!
- process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
+ process_utxos(chainman.ActiveChainstate().CoinsTip(), CTxMemPool());
}
for (size_t i = 0; i < hits.size(); ++i) {
@@ -570,7 +604,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
// serialize data
// use exact same output as mentioned in Bip64
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
req->WriteHeader("Content-Type", "application/octet-stream");
@@ -580,7 +614,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
case RetFormat::HEX: {
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
- ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
+ ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
req->WriteHeader("Content-Type", "text/plain");
@@ -593,8 +627,8 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
// pack in some essentials
// use more or less the same output as mentioned in Bip64
- objGetUTXOResponse.pushKV("chainHeight", ::ChainActive().Height());
- objGetUTXOResponse.pushKV("chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex());
+ objGetUTXOResponse.pushKV("chainHeight", chainman.ActiveChain().Height());
+ objGetUTXOResponse.pushKV("chaintipHash", chainman.ActiveChain().Tip()->GetBlockHash().GetHex());
objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
UniValue utxos(UniValue::VARR);
@@ -637,11 +671,15 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
CBlockIndex* pblockindex = nullptr;
{
+ ChainstateManager* maybe_chainman = GetChainman(context, req);
+ if (!maybe_chainman) return false;
+ ChainstateManager& chainman = *maybe_chainman;
LOCK(cs_main);
- if (blockheight > ::ChainActive().Height()) {
+ const CChain& active_chain = chainman.ActiveChain();
+ if (blockheight > active_chain.Height()) {
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
}
- pblockindex = ::ChainActive()[blockheight];
+ pblockindex = active_chain[blockheight];
}
switch (rf) {
case RetFormat::BINARY: {
@@ -687,7 +725,7 @@ static const struct {
void StartREST(const std::any& context)
{
for (const auto& up : uri_prefixes) {
- auto handler = [&context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
+ auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
RegisterHTTPHandler(up.prefix, false, handler);
}
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e1501d7254..83c1975d38 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -14,6 +14,8 @@
#include <core_io.h>
#include <hash.h>
#include <index/blockfilterindex.h>
+#include <index/coinstatsindex.h>
+#include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
@@ -55,7 +57,7 @@ static Mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
-NodeContext& EnsureNodeContext(const std::any& context)
+NodeContext& EnsureAnyNodeContext(const std::any& context)
{
auto node_context = util::AnyPtr<NodeContext>(context);
if (!node_context) {
@@ -64,33 +66,46 @@ NodeContext& EnsureNodeContext(const std::any& context)
return *node_context;
}
-CTxMemPool& EnsureMemPool(const std::any& context)
+CTxMemPool& EnsureMemPool(const NodeContext& node)
{
- const NodeContext& node = EnsureNodeContext(context);
if (!node.mempool) {
throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
}
return *node.mempool;
}
-ChainstateManager& EnsureChainman(const std::any& context)
+CTxMemPool& EnsureAnyMemPool(const std::any& context)
+{
+ return EnsureMemPool(EnsureAnyNodeContext(context));
+}
+
+ChainstateManager& EnsureChainman(const NodeContext& node)
{
- const NodeContext& node = EnsureNodeContext(context);
if (!node.chainman) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
}
+ WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman)));
return *node.chainman;
}
-CBlockPolicyEstimator& EnsureFeeEstimator(const std::any& context)
+ChainstateManager& EnsureAnyChainman(const std::any& context)
+{
+ return EnsureChainman(EnsureAnyNodeContext(context));
+}
+
+CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node)
{
- NodeContext& node = EnsureNodeContext(context);
if (!node.fee_estimator) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled");
}
return *node.fee_estimator;
}
+CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context)
+{
+ return EnsureFeeEstimator(EnsureAnyNodeContext(context));
+}
+
/* Calculate the difficulty for a given block index.
*/
double GetDifficulty(const CBlockIndex* blockindex)
@@ -125,6 +140,33 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
return blockindex == tip ? 1 : -1;
}
+CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman) {
+ LOCK(::cs_main);
+ CChain& active_chain = chainman.ActiveChain();
+
+ if (param.isNum()) {
+ const int height{param.get_int()};
+ if (height < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
+ }
+ const int current_tip{active_chain.Height()};
+ if (height > current_tip) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
+ }
+
+ return active_chain[height];
+ } else {
+ const uint256 hash{ParseHashV(param, "hash_or_height")};
+ CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
+
+ if (!pindex) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+
+ return pindex;
+ }
+}
+
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex)
{
// Serialize passed information without accessing chain state of the active chain!
@@ -197,8 +239,9 @@ static RPCHelpMan getblockcount()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return ::ChainActive().Height();
+ return chainman.ActiveChain().Height();
},
};
}
@@ -216,8 +259,9 @@ static RPCHelpMan getbestblockhash()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return ::ChainActive().Tip()->GetBlockHash().GetHex();
+ return chainman.ActiveChain().Tip()->GetBlockHash().GetHex();
},
};
}
@@ -238,7 +282,7 @@ static RPCHelpMan waitfornewblock()
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
- {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -281,7 +325,7 @@ static RPCHelpMan waitforblock()
"\nReturns the current block on timeout or exit.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
- {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -328,7 +372,7 @@ static RPCHelpMan waitforblockheight()
"\nReturns the current block on timeout or exit.\n",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
- {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."},
+ {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -397,8 +441,9 @@ static RPCHelpMan getdifficulty()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return GetDifficulty(::ChainActive().Tip());
+ return GetDifficulty(chainman.ActiveChain().Tip());
},
};
}
@@ -541,8 +586,8 @@ static RPCHelpMan getrawmempool()
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
{
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
- {"mempool_sequence", RPCArg::Type::BOOL, /* default */ "false", "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
+ {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
},
{
RPCResult{"for verbose = false",
@@ -580,7 +625,7 @@ static RPCHelpMan getrawmempool()
include_mempool_sequence = request.params[1].get_bool();
}
- return MempoolToJSON(EnsureMemPool(request.context), fVerbose, include_mempool_sequence);
+ return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
},
};
}
@@ -591,7 +636,7 @@ static RPCHelpMan getmempoolancestors()
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
},
{
RPCResult{"for verbose = false",
@@ -615,7 +660,7 @@ static RPCHelpMan getmempoolancestors()
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -655,7 +700,7 @@ static RPCHelpMan getmempooldescendants()
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
},
{
RPCResult{"for verbose = false",
@@ -679,7 +724,7 @@ static RPCHelpMan getmempooldescendants()
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -731,7 +776,7 @@ static RPCHelpMan getmempoolentry()
{
uint256 hash = ParseHashV(request.params[0], "parameter 1");
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
@@ -762,13 +807,15 @@ static RPCHelpMan getblockhash()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
+ const CChain& active_chain = chainman.ActiveChain();
int nHeight = request.params[0].get_int();
- if (nHeight < 0 || nHeight > ::ChainActive().Height())
+ if (nHeight < 0 || nHeight > active_chain.Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
- CBlockIndex* pblockindex = ::ChainActive()[nHeight];
+ CBlockIndex* pblockindex = active_chain[nHeight];
return pblockindex->GetBlockHash().GetHex();
},
};
@@ -781,7 +828,7 @@ static RPCHelpMan getblockheader()
"If verbose is true, returns an Object with information about blockheader <hash>.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"verbose", RPCArg::Type::BOOL, /* default */ "true", "true for a json object, false for the hex-encoded data"},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
},
{
RPCResult{"for verbose = true",
@@ -821,9 +868,10 @@ static RPCHelpMan getblockheader()
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- tip = ::ChainActive().Tip();
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
+ tip = chainman.ActiveChain().Tip();
}
if (!pblockindex) {
@@ -882,7 +930,7 @@ static RPCHelpMan getblock()
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"verbosity|verbose", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
+ {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"},
},
{
RPCResult{"for verbosity = 0",
@@ -935,19 +983,21 @@ static RPCHelpMan getblock()
int verbosity = 1;
if (!request.params[1].isNull()) {
- if(request.params[1].isNum())
- verbosity = request.params[1].get_int();
- else
+ if (request.params[1].isBool()) {
verbosity = request.params[1].get_bool() ? 1 : 0;
+ } else {
+ verbosity = request.params[1].get_int();
+ }
}
CBlock block;
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- tip = ::ChainActive().Tip();
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
+ tip = chainman.ActiveChain().Tip();
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -987,7 +1037,10 @@ static RPCHelpMan pruneblockchain()
if (!fPruneMode)
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ CChain& active_chain = active_chainstate.m_chain;
int heightParam = request.params[0].get_int();
if (heightParam < 0)
@@ -997,7 +1050,7 @@ static RPCHelpMan pruneblockchain()
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
- CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
+ CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
@@ -1005,7 +1058,7 @@ static RPCHelpMan pruneblockchain()
}
unsigned int height = (unsigned int) heightParam;
- unsigned int chainHeight = (unsigned int) ::ChainActive().Height();
+ unsigned int chainHeight = (unsigned int) active_chain.Height();
if (chainHeight < Params().PruneAfterHeight())
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
else if (height > chainHeight)
@@ -1015,8 +1068,8 @@ static RPCHelpMan pruneblockchain()
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
- PruneBlockFilesManual(::ChainstateActive(), height);
- const CBlockIndex* block = ::ChainActive().Tip();
+ PruneBlockFilesManual(active_chainstate, height);
+ const CBlockIndex* block = active_chain.Tip();
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
@@ -1043,42 +1096,88 @@ static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
- "Note this call may take some time.\n",
+ "Note this call may take some time if you are not using coinstatsindex.\n",
{
- {"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'."},
+ {"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "The block hash or height of the target height (only available with coinstatsindex).", "", {"", "string or numeric"}},
+ {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
- {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::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
{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::NUM, "transactions", "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
+ {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
+ {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
+ {RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "prevout_spent", ""},
+ {RPCResult::Type::STR_AMOUNT, "coinbase", ""},
+ {RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", ""},
+ {RPCResult::Type::STR_AMOUNT, "unspendable", ""},
+ {RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
+ {
+ {RPCResult::Type::STR_AMOUNT, "genesis_block", ""},
+ {RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
+ {RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
+ }}
+ }},
}},
RPCExamples{
- HelpExampleCli("gettxoutsetinfo", "")
- + HelpExampleRpc("gettxoutsetinfo", "")
+ HelpExampleCli("gettxoutsetinfo", "") +
+ HelpExampleCli("gettxoutsetinfo", R"("none")") +
+ HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") +
+ HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") +
+ HelpExampleRpc("gettxoutsetinfo", "") +
+ HelpExampleRpc("gettxoutsetinfo", R"("none")") +
+ HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") +
+ HelpExampleRpc("gettxoutsetinfo", R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
UniValue ret(UniValue::VOBJ);
- CCoinsStats stats;
- ::ChainstateActive().ForceFlushStateToDisk();
-
+ CBlockIndex* pindex{nullptr};
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
+ CCoinsStats stats{hash_type};
+ stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
- CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB());
- NodeContext& node = EnsureNodeContext(request.context);
- if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) {
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ active_chainstate.ForceFlushStateToDisk();
+
+ CCoinsView* coins_view;
+ BlockManager* blockman;
+ {
+ LOCK(::cs_main);
+ coins_view = &active_chainstate.CoinsDB();
+ blockman = &active_chainstate.m_blockman;
+ pindex = blockman->LookupBlockIndex(coins_view->GetBestBlock());
+ }
+
+ if (!request.params[1].isNull()) {
+ if (!g_coin_stats_index) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
+ }
+
+ if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
+ }
+
+ pindex = ParseHashOrHeight(request.params[1], chainman);
+ }
+
+ if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
- ret.pushKV("transactions", (int64_t)stats.nTransactions);
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
@@ -1087,9 +1186,42 @@ static RPCHelpMan gettxoutsetinfo()
if (hash_type == CoinStatsHashType::MUHASH) {
ret.pushKV("muhash", stats.hashSerialized.GetHex());
}
- ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
+ if (!stats.index_used) {
+ ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
+ ret.pushKV("disk_size", stats.nDiskSize);
+ } else {
+ ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.block_unspendable_amount));
+
+ CCoinsStats prev_stats{hash_type};
+
+ if (pindex->nHeight > 0) {
+ GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev);
+ }
+
+ UniValue block_info(UniValue::VOBJ);
+ block_info.pushKV("prevout_spent", ValueFromAmount(stats.block_prevout_spent_amount - prev_stats.block_prevout_spent_amount));
+ block_info.pushKV("coinbase", ValueFromAmount(stats.block_coinbase_amount - prev_stats.block_coinbase_amount));
+ block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.block_new_outputs_ex_coinbase_amount - prev_stats.block_new_outputs_ex_coinbase_amount));
+ block_info.pushKV("unspendable", ValueFromAmount(stats.block_unspendable_amount - prev_stats.block_unspendable_amount));
+
+ UniValue unspendables(UniValue::VOBJ);
+ unspendables.pushKV("genesis_block", ValueFromAmount(stats.unspendables_genesis_block - prev_stats.unspendables_genesis_block));
+ unspendables.pushKV("bip30", ValueFromAmount(stats.unspendables_bip30 - prev_stats.unspendables_bip30));
+ unspendables.pushKV("scripts", ValueFromAmount(stats.unspendables_scripts - prev_stats.unspendables_scripts));
+ unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.unspendables_unclaimed_rewards - prev_stats.unspendables_unclaimed_rewards));
+ block_info.pushKV("unspendables", unspendables);
+
+ ret.pushKV("block_info", block_info);
+ }
} else {
+ if (g_coin_stats_index) {
+ const IndexSummary summary{g_coin_stats_index->GetSummary()};
+
+ if (!summary.synced) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to read UTXO set because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
+ }
+ }
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
return ret;
@@ -1104,7 +1236,7 @@ static RPCHelpMan gettxout()
{
{"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."},
+ {"include_mempool", RPCArg::Type::BOOL, RPCArg::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, "", ""},
@@ -1134,6 +1266,8 @@ static RPCHelpMan gettxout()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
UniValue ret(UniValue::VOBJ);
@@ -1146,10 +1280,11 @@ static RPCHelpMan gettxout()
fMempool = request.params[2].get_bool();
Coin coin;
- CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
if (fMempool) {
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
LOCK(mempool.cs);
CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
@@ -1161,7 +1296,7 @@ static RPCHelpMan gettxout()
}
}
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
+ const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0);
@@ -1184,9 +1319,9 @@ static RPCHelpMan verifychain()
return RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
- {"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL),
+ {"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
strprintf("How thorough the block verification is:\n - %s", Join(CHECKLEVEL_DOC, "\n- "))},
- {"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS), "The number of blocks to check."},
+ {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
},
RPCResult{
RPCResult::Type::BOOL, "", "Verified or not"},
@@ -1199,41 +1334,42 @@ static RPCHelpMan verifychain()
const int check_level(request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int());
const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()};
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ return CVerifyDB().VerifyDB(
+ active_chainstate, Params(), active_chainstate.CoinsTip(), check_level, check_depth);
},
};
}
-static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int softfork_height, int tip_height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
// For buried deployments.
// A buried deployment is one where the height of the activation has been hardcoded into
// the client implementation long after the consensus change has activated. See BIP 90.
// Buried deployments with activation height value of
// std::numeric_limits<int>::max() are disabled and thus hidden.
- if (height == std::numeric_limits<int>::max()) return;
+ if (softfork_height == std::numeric_limits<int>::max()) return;
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
// getblockchaininfo reports the softfork as active from when the chain height is
// one below the activation height
- rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height);
- rv.pushKV("height", height);
+ rv.pushKV("active", tip_height + 1 >= softfork_height);
+ rv.pushKV("height", softfork_height);
softforks.pushKV(name, rv);
}
-static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
// For BIP9 deployments.
- // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
- // A timeout value of 0 guarantees a softfork will never be activated.
- // This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
- if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
+ // Deployments that are never active are hidden.
+ if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
+ const ThresholdState thresholdState = VersionBitsState(active_chain_tip, consensusParams, id, versionbitscache);
switch (thresholdState) {
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
@@ -1247,12 +1383,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 = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache);
+ int64_t since_height = VersionBitsStateSinceHeight(active_chain_tip, consensusParams, id, versionbitscache);
bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id);
+ BIP9Stats statsStruct = VersionBitsStatistics(active_chain_tip, consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1260,6 +1396,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
statsUV.pushKV("possible", statsStruct.possible);
bip9.pushKV("statistics", statsUV);
}
+ bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
@@ -1306,6 +1443,7 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
+ {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
{RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)",
{
{RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
@@ -1327,18 +1465,22 @@ RPCHelpMan getblockchaininfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
- const CBlockIndex* tip = ::ChainActive().Tip();
+ const CBlockIndex* tip = active_chainstate.m_chain.Tip();
+ CHECK_NONFATAL(tip);
+ const int height = tip->nHeight;
UniValue obj(UniValue::VOBJ);
obj.pushKV("chain", Params().NetworkIDString());
- obj.pushKV("blocks", (int)::ChainActive().Height());
+ obj.pushKV("blocks", height);
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
- obj.pushKV("initialblockdownload", ::ChainstateActive().IsInitialBlockDownload());
+ obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
@@ -1361,13 +1503,13 @@ RPCHelpMan getblockchaininfo()
const Consensus::Params& consensusParams = Params().GetConsensus();
UniValue softforks(UniValue::VOBJ);
- BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height);
- BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height);
- BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height);
- BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight);
- BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
- BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
- BIP9SoftForkDescPushBack(softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
+ BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height, height);
+ BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height, height);
+ BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height, height);
+ BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight, height);
+ BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight, height);
+ BIP9SoftForkDescPushBack(tip, softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
+ BIP9SoftForkDescPushBack(tip, softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
obj.pushKV("softforks", softforks);
obj.pushKV("warnings", GetWarnings(false).original);
@@ -1418,8 +1560,9 @@ static RPCHelpMan getchaintips()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- ChainstateManager& chainman = EnsureChainman(request.context);
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
+ CChain& active_chain = chainman.ActiveChain();
/*
* Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them.
@@ -1433,7 +1576,7 @@ static RPCHelpMan getchaintips()
std::set<const CBlockIndex*> setPrevs;
for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
- if (!chainman.ActiveChain().Contains(item.second)) {
+ if (!active_chain.Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
@@ -1446,7 +1589,7 @@ static RPCHelpMan getchaintips()
}
// Always report the currently active tip.
- setTips.insert(chainman.ActiveChain().Tip());
+ setTips.insert(active_chain.Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
@@ -1455,11 +1598,11 @@ static RPCHelpMan getchaintips()
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
- const int branchLen = block->nHeight - chainman.ActiveChain().FindFork(block)->nHeight;
+ const int branchLen = block->nHeight - active_chain.FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
- if (chainman.ActiveChain().Contains(block)) {
+ if (active_chain.Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
@@ -1520,7 +1663,7 @@ static RPCHelpMan getmempoolinfo()
{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, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB 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"},
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
}},
@@ -1530,7 +1673,7 @@ static RPCHelpMan getmempoolinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- return MempoolInfoToJSON(EnsureMemPool(request.context));
+ return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
},
};
}
@@ -1554,16 +1697,17 @@ static RPCHelpMan preciousblock()
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CBlockIndex* pblockindex;
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
{
LOCK(cs_main);
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
BlockValidationState state;
- ::ChainstateActive().PreciousBlock(state, Params(), pblockindex);
+ chainman.ActiveChainstate().PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1591,18 +1735,19 @@ static RPCHelpMan invalidateblock()
uint256 hash(ParseHashV(request.params[0], "blockhash"));
BlockValidationState state;
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
CBlockIndex* pblockindex;
{
LOCK(cs_main);
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
- ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex);
+ chainman.ActiveChainstate().InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
- ::ChainstateActive().ActivateBestChain(state, Params());
+ chainman.ActiveChainstate().ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
@@ -1629,20 +1774,21 @@ static RPCHelpMan reconsiderblock()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
uint256 hash(ParseHashV(request.params[0], "blockhash"));
{
LOCK(cs_main);
- CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- ::ChainstateActive().ResetBlockFailureFlags(pblockindex);
+ chainman.ActiveChainstate().ResetBlockFailureFlags(pblockindex);
}
BlockValidationState state;
- ::ChainstateActive().ActivateBestChain(state, Params());
+ chainman.ActiveChainstate().ActivateBestChain(state, Params());
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1658,8 +1804,8 @@ static RPCHelpMan getchaintxstats()
return RPCHelpMan{"getchaintxstats",
"\nCompute statistics about the total number and rate of transactions in the chain.\n",
{
- {"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"},
- {"blockhash", RPCArg::Type::STR_HEX, /* default */ "chain tip", "The hash of the block that ends the window."},
+ {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{"one month"}, "Size of the window in number of blocks"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"chain tip"}, "The hash of the block that ends the window."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1679,20 +1825,21 @@ static RPCHelpMan getchaintxstats()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
if (request.params[1].isNull()) {
LOCK(cs_main);
- pindex = ::ChainActive().Tip();
+ pindex = chainman.ActiveChain().Tip();
} else {
uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
- pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ pindex = chainman.m_blockman.LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- if (!::ChainActive().Contains(pindex)) {
+ if (!chainman.ActiveChain().Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
}
}
@@ -1805,7 +1952,7 @@ static RPCHelpMan getblockstats()
"It won't work for some heights with pruning.\n",
{
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
- {"stats", RPCArg::Type::ARR, /* default */ "all values", "Values to plot (see result below)",
+ {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
{
{"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
{"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
@@ -1860,31 +2007,9 @@ static RPCHelpMan getblockstats()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
-
- CBlockIndex* pindex;
- if (request.params[0].isNum()) {
- const int height = request.params[0].get_int();
- const int current_tip = ::ChainActive().Height();
- if (height < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
- }
- if (height > current_tip) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
- }
-
- pindex = ::ChainActive()[height];
- } else {
- const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
- pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
- if (!pindex) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
- }
- if (!::ChainActive().Contains(pindex)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString()));
- }
- }
-
+ CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
CHECK_NONFATAL(pindex != nullptr);
std::set<std::string> stats;
@@ -2071,7 +2196,7 @@ static RPCHelpMan savemempool()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
if (!mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
@@ -2172,7 +2297,7 @@ static RPCHelpMan scantxoutset()
{"", 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])"},
+ {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
}},
},
"[scanobjects,...]"},
@@ -2261,15 +2386,17 @@ static RPCHelpMan scantxoutset()
int64_t count = 0;
std::unique_ptr<CCoinsViewCursor> pcursor;
CBlockIndex* tip;
+ NodeContext& node = EnsureAnyNodeContext(request.context);
{
+ ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
- ::ChainstateActive().ForceFlushStateToDisk();
- pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ active_chainstate.ForceFlushStateToDisk();
+ pcursor = std::unique_ptr<CCoinsViewCursor>(active_chainstate.CoinsDB().Cursor());
CHECK_NONFATAL(pcursor);
- tip = ::ChainActive().Tip();
+ tip = active_chainstate.m_chain.Tip();
CHECK_NONFATAL(tip);
}
- NodeContext& node = EnsureNodeContext(request.context);
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
result.pushKV("success", res);
result.pushKV("txouts", count);
@@ -2309,7 +2436,7 @@ static RPCHelpMan getblockfilter()
"\nRetrieve a BIP 157 content filter for a particular block.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
- {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
+ {"filtertype", RPCArg::Type::STR, RPCArg::Default{"basic"}, "The type name of the filter"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2342,8 +2469,9 @@ static RPCHelpMan getblockfilter()
const CBlockIndex* block_index;
bool block_was_connected;
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash);
+ block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
if (!block_index) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
@@ -2412,10 +2540,10 @@ static RPCHelpMan dumptxoutset()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const fs::path path = fsbridge::AbsPathJoin(GetDataDir(), request.params[0].get_str());
+ const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str());
// Write to a temporary path and then move into `path` on completion
// to avoid confusion due to an interruption.
- const fs::path temppath = fsbridge::AbsPathJoin(GetDataDir(), request.params[0].get_str() + ".incomplete");
+ const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), request.params[0].get_str() + ".incomplete");
if (fs::exists(path)) {
throw JSONRPCError(
@@ -2426,7 +2554,7 @@ static RPCHelpMan dumptxoutset()
FILE* file{fsbridge::fopen(temppath, "wb")};
CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile);
fs::rename(temppath, path);
@@ -2439,7 +2567,7 @@ static RPCHelpMan dumptxoutset()
UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile)
{
std::unique_ptr<CCoinsViewCursor> pcursor;
- CCoinsStats stats;
+ CCoinsStats stats{CoinStatsHashType::NONE};
CBlockIndex* tip;
{
@@ -2459,12 +2587,12 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil
chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
+ if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
pcursor = std::unique_ptr<CCoinsViewCursor>(chainstate.CoinsDB().Cursor());
- tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock);
+ tip = chainstate.m_blockman.LookupBlockIndex(stats.hashBlock);
CHECK_NONFATAL(tip);
}
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index cd04c9a10f..ffb6f03b47 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -56,10 +56,13 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
-NodeContext& EnsureNodeContext(const std::any& context);
-CTxMemPool& EnsureMemPool(const std::any& context);
-ChainstateManager& EnsureChainman(const std::any& context);
-CBlockPolicyEstimator& EnsureFeeEstimator(const std::any& context);
+NodeContext& EnsureAnyNodeContext(const std::any& context);
+CTxMemPool& EnsureMemPool(const NodeContext& node);
+CTxMemPool& EnsureAnyMemPool(const std::any& context);
+ChainstateManager& EnsureChainman(const NodeContext& node);
+ChainstateManager& EnsureAnyChainman(const std::any& context);
+CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node);
+CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context);
/**
* Helper to create UTXO snapshots given a chainstate and a file handle.
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 2b593cd10b..9c8582c7a3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -127,6 +127,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
+ { "gettxoutsetinfo", 1, "hash_or_height" },
+ { "gettxoutsetinfo", 2, "use_index"},
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
{ "send", 0, "outputs" },
diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp
new file mode 100644
index 0000000000..6ec2b1a07f
--- /dev/null
+++ b/src/rpc/external_signer.cpp
@@ -0,0 +1,76 @@
+// 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 <external_signer.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <util/strencodings.h>
+#include <rpc/protocol.h>
+
+#include <string>
+#include <vector>
+
+#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{
+ HelpExampleCli("enumeratesigners", "")
+ + HelpExampleRpc("enumeratesigners", "")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") throw JSONRPCError(RPC_MISC_ERROR, "Error: restart bitcoind with -signer=<cmd>");
+ const std::string chain = gArgs.GetChainName();
+ UniValue signers_res = UniValue::VARR;
+ try {
+ std::vector<ExternalSigner> signers;
+ ExternalSigner::Enumerate(command, signers, chain);
+ for (const 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 std::exception& e) {
+ throw JSONRPCError(RPC_MISC_ERROR, e.what());
+ }
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("signers", signers_res);
+ return result;
+ }
+ };
+}
+
+void RegisterSignerRPCCommands(CRPCTable &t)
+{
+// clang-format off
+static const CRPCCommand commands[] =
+{ // category actor (function)
+ // --------------------- ------------------------
+ { "signer", &enumeratesigners, },
+};
+// clang-format on
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
+}
+
+#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 72ad0df199..6826e6fd07 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -18,6 +18,7 @@
#include <pow.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
+#include <rpc/net.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
@@ -44,11 +45,12 @@
* or from the last difficulty change if 'lookup' is nonpositive.
* If 'height' is nonnegative, compute the estimate at the time when a given block was found.
*/
-static UniValue GetNetworkHashPS(int lookup, int height) {
- CBlockIndex *pb = ::ChainActive().Tip();
+static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) {
+ const CBlockIndex* pb = active_chain.Tip();
- if (height >= 0 && height < ::ChainActive().Height())
- pb = ::ChainActive()[height];
+ if (height >= 0 && height < active_chain.Height()) {
+ pb = active_chain[height];
+ }
if (pb == nullptr || !pb->nHeight)
return 0;
@@ -61,7 +63,7 @@ static UniValue GetNetworkHashPS(int lookup, int height) {
if (lookup > pb->nHeight)
lookup = pb->nHeight;
- CBlockIndex *pb0 = pb;
+ const CBlockIndex* pb0 = pb;
int64_t minTime = pb0->GetBlockTime();
int64_t maxTime = minTime;
for (int i = 0; i < lookup; i++) {
@@ -88,8 +90,8 @@ static RPCHelpMan getnetworkhashps()
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
{
- {"nblocks", RPCArg::Type::NUM, /* default */ "120", "The number of blocks, or -1 for blocks since last difficulty change."},
- {"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."},
+ {"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of blocks, or -1 for blocks since last difficulty change."},
+ {"height", RPCArg::Type::NUM, RPCArg::Default{-1}, "To estimate at the time of the given height."},
},
RPCResult{
RPCResult::Type::NUM, "", "Hashes per second estimated"},
@@ -99,8 +101,9 @@ static RPCHelpMan getnetworkhashps()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
+ return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1, chainman.ActiveChain());
},
};
}
@@ -111,7 +114,8 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
{
LOCK(cs_main);
- IncrementExtraNonce(&block, ::ChainActive().Tip(), extra_nonce);
+ CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
+ IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
CChainParams chainparams(Params());
@@ -143,7 +147,8 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{ // Don't keep cs_main locked
LOCK(cs_main);
- nHeight = ::ChainActive().Height();
+ CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
+ nHeight = chainman.ActiveChain().Height();
nHeightEnd = nHeight+nGenerate;
}
unsigned int nExtraNonce = 0;
@@ -210,7 +215,7 @@ static RPCHelpMan generatetodescriptor()
{
{"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
+ {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -231,8 +236,9 @@ static RPCHelpMan generatetodescriptor()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
- const CTxMemPool& mempool = EnsureMemPool(request.context);
- ChainstateManager& chainman = EnsureChainman(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+ ChainstateManager& chainman = EnsureChainman(node);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
},
@@ -253,7 +259,7 @@ static RPCHelpMan generatetoaddress()
{
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
+ {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -276,8 +282,9 @@ static RPCHelpMan generatetoaddress()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
- const CTxMemPool& mempool = EnsureMemPool(request.context);
- ChainstateManager& chainman = EnsureChainman(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+ ChainstateManager& chainman = EnsureChainman(node);
CScript coinbase_script = GetScriptForDestination(destination);
@@ -325,7 +332,8 @@ static RPCHelpMan generateblock()
coinbase_script = GetScriptForDestination(destination);
}
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
std::vector<CTransactionRef> txs;
const auto raw_txs_or_txids = request.params[1].get_array();
@@ -354,11 +362,12 @@ static RPCHelpMan generateblock()
CChainParams chainparams(Params());
CBlock block;
+ ChainstateManager& chainman = EnsureChainman(node);
{
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(::ChainstateActive(), empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(chainman.ActiveChainstate(), empty_mempool, chainparams).CreateNewBlock(coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -369,13 +378,13 @@ static RPCHelpMan generateblock()
// Add transactions
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
- RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
+ RegenerateCommitments(block, chainman);
{
LOCK(cs_main);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
+ if (!TestBlockValidity(state, chainparams, chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
}
}
@@ -384,7 +393,7 @@ static RPCHelpMan generateblock()
uint64_t max_tries{DEFAULT_MAX_TRIES};
unsigned int extra_nonce{0};
- if (!GenerateBlock(EnsureChainman(request.context), block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
+ if (!GenerateBlock(chainman, block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
}
@@ -418,14 +427,17 @@ static RPCHelpMan getmininginfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+ ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CChain& active_chain = chainman.ActiveChain();
UniValue obj(UniValue::VOBJ);
- obj.pushKV("blocks", (int)::ChainActive().Height());
+ obj.pushKV("blocks", active_chain.Height());
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
- obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
+ obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip()));
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
@@ -467,7 +479,7 @@ static RPCHelpMan prioritisetransaction()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
}
- EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
+ EnsureAnyMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true;
},
};
@@ -513,7 +525,7 @@ static RPCHelpMan getblocktemplate()
" 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",
+ {"template_request", RPCArg::Type::OBJ, RPCArg::Default{UniValue::VOBJ}, "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",
@@ -588,12 +600,16 @@ static RPCHelpMan getblocktemplate()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main);
std::string strMode = "template";
UniValue lpval = NullUniValue;
std::set<std::string> setClientRules;
int64_t nMaxVersionPreVB = -1;
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+ CChain& active_chain = active_chainstate.m_chain;
if (!request.params[0].isNull())
{
const UniValue& oparam = request.params[0].get_obj();
@@ -619,7 +635,7 @@ static RPCHelpMan getblocktemplate()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
@@ -628,12 +644,12 @@ static RPCHelpMan getblocktemplate()
return "duplicate-inconclusive";
}
- CBlockIndex* const pindexPrev = ::ChainActive().Tip();
+ CBlockIndex* const pindexPrev = active_chain.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
BlockValidationState state;
- TestBlockValidity(state, Params(), ::ChainstateActive(), block, pindexPrev, false, true);
+ TestBlockValidity(state, Params(), active_chainstate, block, pindexPrev, false, true);
return BIP22ValidationResult(state);
}
@@ -655,22 +671,19 @@ static RPCHelpMan getblocktemplate()
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
-
if (!Params().IsTestChain()) {
- if (node.connman->GetNodeCount(ConnectionDirection::Both) == 0) {
+ const CConnman& connman = EnsureConnman(node);
+ if (connman.GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
}
- if (::ChainstateActive().IsInitialBlockDownload()) {
+ if (active_chainstate.IsInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
}
}
static unsigned int nTransactionsUpdatedLast;
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
if (!lpval.isNull())
{
@@ -690,7 +703,7 @@ static RPCHelpMan getblocktemplate()
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
- hashWatchedChain = ::ChainActive().Tip()->GetBlockHash();
+ hashWatchedChain = active_chain.Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
@@ -735,7 +748,7 @@ static RPCHelpMan getblocktemplate()
static CBlockIndex* pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
- if (pindexPrev != ::ChainActive().Tip() ||
+ if (pindexPrev != active_chain.Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
@@ -743,12 +756,12 @@ static RPCHelpMan getblocktemplate()
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
- CBlockIndex* pindexPrevNew = ::ChainActive().Tip();
+ CBlockIndex* pindexPrevNew = active_chain.Tip();
nStart = GetTime();
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(::ChainstateActive(), mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler(active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
@@ -884,7 +897,7 @@ static RPCHelpMan getblocktemplate()
result.pushKV("transactions", transactions);
result.pushKV("coinbaseaux", aux);
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
- result.pushKV("longpollid", ::ChainActive().Tip()->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast));
+ result.pushKV("longpollid", active_chain.Tip()->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast));
result.pushKV("target", hashTarget.GetHex());
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
result.pushKV("mutable", aMutable);
@@ -945,7 +958,7 @@ static RPCHelpMan submitblock()
"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."},
+ {"dummy", RPCArg::Type::STR, RPCArg::DefaultHint{"ignored"}, "dummy value, for compatibility with BIP22. This value is ignored."},
},
{
RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""},
@@ -967,10 +980,11 @@ static RPCHelpMan submitblock()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase");
}
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
uint256 hash = block.GetHash();
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
return "duplicate";
@@ -983,7 +997,7 @@ static RPCHelpMan submitblock()
{
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
if (pindex) {
UpdateUncommittedBlockStructures(block, pindex, Params().GetConsensus());
}
@@ -992,7 +1006,7 @@ static RPCHelpMan submitblock()
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
- bool accepted = EnsureChainman(request.context).ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
+ bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
@@ -1025,15 +1039,16 @@ static RPCHelpMan submitheader()
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed");
}
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
{
LOCK(cs_main);
- if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) {
+ if (!chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) {
throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first");
}
}
BlockValidationState state;
- EnsureChainman(request.context).ProcessNewBlockHeaders({h}, state, Params());
+ chainman.ProcessNewBlockHeaders({h}, state, Params());
if (state.IsValid()) return NullUniValue;
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
@@ -1052,7 +1067,7 @@ static RPCHelpMan estimatesmartfee()
"in BIP 141 (witness data is discounted).\n",
{
{"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "conservative", "The fee estimate mode.\n"
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"conservative"}, "The fee estimate mode.\n"
" Whether to return a more conservative estimate which also satisfies\n"
" a longer history. A conservative estimate potentially returns a\n"
" higher feerate and is more likely to be sufficient for the desired\n"
@@ -1063,7 +1078,7 @@ static RPCHelpMan estimatesmartfee()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"},
+ {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},
{RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)",
{
{RPCResult::Type::STR, "", "error"},
@@ -1082,7 +1097,7 @@ static RPCHelpMan estimatesmartfee()
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context);
+ CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
@@ -1123,7 +1138,7 @@ static RPCHelpMan estimaterawfee()
"defined in BIP 141 (witness data is discounted).\n",
{
{"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
- {"threshold", RPCArg::Type::NUM, /* default */ "0.95", "The proportion of transactions in a given feerate range that must have been\n"
+ {"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
" confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
" lower buckets."},
},
@@ -1132,7 +1147,7 @@ static RPCHelpMan estimaterawfee()
{
{RPCResult::Type::OBJ, "short", /* optional */ true, "estimate for short time horizon",
{
- {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
{RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
{RPCResult::Type::OBJ, "pass", /* optional */ true, "information about the lowest range of feerates to succeed in meeting the threshold",
@@ -1170,7 +1185,7 @@ static RPCHelpMan estimaterawfee()
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context);
+ CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 1df5c51718..ab239fe79c 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -5,8 +5,12 @@
#include <httpserver.h>
#include <index/blockfilterindex.h>
+#include <index/coinstatsindex.h>
#include <index/txindex.h>
#include <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
#include <key_io.h>
#include <node/context.h>
#include <outputtype.h>
@@ -90,7 +94,7 @@ static RPCHelpMan createmultisig()
{
{"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
}},
- {"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -420,7 +424,7 @@ static RPCHelpMan mockscheduler()
// check params are valid values
RPCTypeCheck(request.params, {UniValue::VNUM});
int64_t delta_seconds = request.params[0].get_int64();
- if ((delta_seconds <= 0) || (delta_seconds > 3600)) {
+ if (delta_seconds <= 0 || delta_seconds > 3600) {
throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
}
@@ -475,7 +479,7 @@ static RPCHelpMan getmemoryinfo()
return RPCHelpMan{"getmemoryinfo",
"Returns an object containing information about memory usage.\n",
{
- {"mode", RPCArg::Type::STR, /* default */ "\"stats\"", "determines what kind of information is returned.\n"
+ {"mode", RPCArg::Type::STR, RPCArg::Default{"stats"}, "determines what kind of information is returned.\n"
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
" - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."},
},
@@ -628,7 +632,7 @@ static RPCHelpMan echo(const std::string& name)
{"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
{"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
},
- RPCResult{RPCResult::Type::NONE, "", "Returns whatever was passed in"},
+ RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -644,6 +648,43 @@ static RPCHelpMan echo(const std::string& name)
static RPCHelpMan echo() { return echo("echo"); }
static RPCHelpMan echojson() { return echo("echojson"); }
+static RPCHelpMan echoipc()
+{
+ return RPCHelpMan{
+ "echoipc",
+ "\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n"
+ "This command is for testing.\n",
+ {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}},
+ RPCResult{RPCResult::Type::STR, "echo", "The echoed string."},
+ RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
+ HelpExampleRpc("echo", "\"Hello world\"")},
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ std::unique_ptr<interfaces::Echo> echo;
+ if (interfaces::Ipc* ipc = Assert(EnsureAnyNodeContext(request.context).init)->ipc()) {
+ // Spawn a new bitcoin-node process and call makeEcho to get a
+ // client pointer to a interfaces::Echo instance running in
+ // that process. This is just for testing. A slightly more
+ // realistic test spawning a different executable instead of
+ // the same executable would add a new bitcoin-echo executable,
+ // and spawn bitcoin-echo below instead of bitcoin-node. But
+ // using bitcoin-node avoids the need to build and install a
+ // new executable just for this one test.
+ auto init = ipc->spawnProcess("bitcoin-node");
+ echo = init->makeEcho();
+ ipc->addCleanup(*echo, [init = init.release()] { delete init; });
+ } else {
+ // IPC support is not available because this is a bitcoind
+ // process not a bitcoind-node process, so just create a local
+ // interfaces::Echo object and return it so the `echoipc` RPC
+ // method will work, and the python test calling `echoipc`
+ // can expect the same result.
+ echo = interfaces::MakeEcho();
+ }
+ return echo->echo(request.params[0].get_str());
+ },
+ };
+}
+
static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
{
UniValue ret_summary(UniValue::VOBJ);
@@ -689,6 +730,10 @@ static RPCHelpMan getindexinfo()
result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name));
}
+ if (g_coin_stats_index) {
+ result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name));
+ }
+
ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
});
@@ -719,6 +764,7 @@ static const CRPCCommand commands[] =
{ "hidden", &mockscheduler, },
{ "hidden", &echo, },
{ "hidden", &echojson, },
+ { "hidden", &echoipc, },
};
// clang-format on
for (const auto& c : commands) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 96533a50c8..3013c76825 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -28,6 +28,8 @@
#include <version.h>
#include <warnings.h>
+#include <optional>
+
#include <univalue.h>
const std::vector<std::string> CONNECTION_TYPE_DOC{
@@ -39,6 +41,22 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{
"feeler (short-lived automatic connection for testing addresses)"
};
+CConnman& EnsureConnman(const NodeContext& node)
+{
+ if (!node.connman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+ return *node.connman;
+}
+
+PeerManager& EnsurePeerman(const NodeContext& node)
+{
+ if (!node.peerman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+ return *node.peerman;
+}
+
static RPCHelpMan getconnectioncount()
{
return RPCHelpMan{"getconnectioncount",
@@ -53,11 +71,10 @@ static RPCHelpMan getconnectioncount()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CConnman& connman = EnsureConnman(node);
- return (int)node.connman->GetNodeCount(ConnectionDirection::Both);
+ return (int)connman.GetNodeCount(ConnectionDirection::Both);
},
};
}
@@ -76,13 +93,11 @@ static RPCHelpMan ping()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if (!node.peerman) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ PeerManager& peerman = EnsurePeerman(node);
// Request that each node send a ping during next message processing pass
- node.peerman->SendPings();
+ peerman.SendPings();
return NullUniValue;
},
};
@@ -146,7 +161,7 @@ static RPCHelpMan getpeerinfo()
"When a message type is not listed in this json object, the bytes sent are 0.\n"
"Only known message types can appear as keys in the object."}
}},
- {RPCResult::Type::OBJ, "bytesrecv_per_msg", "",
+ {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "",
{
{RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n"
"When a message type is not listed in this json object, the bytes received are 0.\n"
@@ -165,20 +180,19 @@ static RPCHelpMan getpeerinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman || !node.peerman) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CConnman& connman = EnsureConnman(node);
+ const PeerManager& peerman = EnsurePeerman(node);
std::vector<CNodeStats> vstats;
- node.connman->GetNodeStats(vstats);
+ connman.GetNodeStats(vstats);
UniValue ret(UniValue::VARR);
for (const CNodeStats& stats : vstats) {
UniValue obj(UniValue::VOBJ);
CNodeStateStats statestats;
- bool fStateStats = node.peerman->GetNodeStateStats(stats.nodeid, statestats);
+ bool fStateStats = peerman.GetNodeStateStats(stats.nodeid, statestats);
obj.pushKV("id", stats.nodeid);
obj.pushKV("addr", stats.addrName);
if (stats.addrBind.IsValid()) {
@@ -265,7 +279,9 @@ static RPCHelpMan addnode()
"\nAttempts to add or remove a node from the addnode list.\n"
"Or try a connection to a node once.\n"
"Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
- "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n",
+ "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n" +
+ strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
+ " and are counted separately from the -maxconnections limit.\n",
{
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The node (see getpeerinfo for nodes)"},
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
@@ -285,28 +301,29 @@ static RPCHelpMan addnode()
self.ToString());
}
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CConnman& connman = EnsureConnman(node);
std::string strNode = request.params[0].get_str();
if (strCommand == "onetry")
{
CAddress addr;
- node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), ConnectionType::MANUAL);
+ connman.OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), ConnectionType::MANUAL);
return NullUniValue;
}
if (strCommand == "add")
{
- if(!node.connman->AddNode(strNode))
+ if (!connman.AddNode(strNode)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
+ }
}
else if(strCommand == "remove")
{
- if(!node.connman->RemoveAddedNode(strNode))
+ if (!connman.RemoveAddedNode(strNode)) {
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node could not be removed. It has not been added previously.");
+ }
}
return NullUniValue;
@@ -350,12 +367,10 @@ static RPCHelpMan addconnection()
throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString());
}
- NodeContext& node = EnsureNodeContext(request.context);
- if (!node.connman) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled.");
- }
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CConnman& connman = EnsureConnman(node);
- const bool success = node.connman->AddConnection(address, conn_type);
+ const bool success = connman.AddConnection(address, conn_type);
if (!success) {
throw JSONRPCError(RPC_CLIENT_NODE_CAPACITY_REACHED, "Error: Already at capacity for specified connection type.");
}
@@ -376,8 +391,8 @@ static RPCHelpMan disconnectnode()
"\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
"\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
{
- {"address", RPCArg::Type::STR, /* default */ "fallback to nodeid", "The IP address/port of the node"},
- {"nodeid", RPCArg::Type::NUM, /* default */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"},
+ {"address", RPCArg::Type::STR, RPCArg::DefaultHint{"fallback to nodeid"}, "The IP address/port of the node"},
+ {"nodeid", RPCArg::Type::NUM, RPCArg::DefaultHint{"fallback to address"}, "The node ID (see getpeerinfo for node IDs)"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -388,9 +403,8 @@ static RPCHelpMan disconnectnode()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CConnman& connman = EnsureConnman(node);
bool success;
const UniValue &address_arg = request.params[0];
@@ -398,11 +412,11 @@ static RPCHelpMan disconnectnode()
if (!address_arg.isNull() && id_arg.isNull()) {
/* handle disconnect-by-address */
- success = node.connman->DisconnectNode(address_arg.get_str());
+ success = connman.DisconnectNode(address_arg.get_str());
} else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
/* handle disconnect-by-id */
NodeId nodeid = (NodeId) id_arg.get_int64();
- success = node.connman->DisconnectNode(nodeid);
+ success = connman.DisconnectNode(nodeid);
} else {
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
}
@@ -422,7 +436,7 @@ static RPCHelpMan getaddednodeinfo()
"\nReturns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
{
- {"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
+ {"node", RPCArg::Type::STR, RPCArg::DefaultHint{"all nodes"}, "If provided, return information about this specific node, otherwise all nodes are returned."},
},
RPCResult{
RPCResult::Type::ARR, "", "",
@@ -448,11 +462,10 @@ static RPCHelpMan getaddednodeinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CConnman& connman = EnsureConnman(node);
- std::vector<AddedNodeInfo> vInfo = node.connman->GetAddedNodeInfo();
+ std::vector<AddedNodeInfo> vInfo = connman.GetAddedNodeInfo();
if (!request.params[0].isNull()) {
bool found = false;
@@ -519,22 +532,21 @@ static RPCHelpMan getnettotals()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CConnman& connman = EnsureConnman(node);
UniValue obj(UniValue::VOBJ);
- obj.pushKV("totalbytesrecv", node.connman->GetTotalBytesRecv());
- obj.pushKV("totalbytessent", node.connman->GetTotalBytesSent());
+ obj.pushKV("totalbytesrecv", connman.GetTotalBytesRecv());
+ obj.pushKV("totalbytessent", connman.GetTotalBytesSent());
obj.pushKV("timemillis", GetTimeMillis());
UniValue outboundLimit(UniValue::VOBJ);
- outboundLimit.pushKV("timeframe", count_seconds(node.connman->GetMaxOutboundTimeframe()));
- outboundLimit.pushKV("target", node.connman->GetMaxOutboundTarget());
- outboundLimit.pushKV("target_reached", node.connman->OutboundTargetReached(false));
- outboundLimit.pushKV("serve_historical_blocks", !node.connman->OutboundTargetReached(true));
- outboundLimit.pushKV("bytes_left_in_cycle", node.connman->GetOutboundTargetBytesLeft());
- outboundLimit.pushKV("time_left_in_cycle", count_seconds(node.connman->GetMaxOutboundTimeLeftInCycle()));
+ outboundLimit.pushKV("timeframe", count_seconds(connman.GetMaxOutboundTimeframe()));
+ outboundLimit.pushKV("target", connman.GetMaxOutboundTarget());
+ outboundLimit.pushKV("target_reached", connman.OutboundTargetReached(false));
+ outboundLimit.pushKV("serve_historical_blocks", !connman.OutboundTargetReached(true));
+ outboundLimit.pushKV("bytes_left_in_cycle", connman.GetOutboundTargetBytesLeft());
+ outboundLimit.pushKV("time_left_in_cycle", count_seconds(connman.GetMaxOutboundTimeLeftInCycle()));
obj.pushKV("uploadtarget", outboundLimit);
return obj;
},
@@ -593,8 +605,8 @@ static RPCHelpMan getnetworkinfo()
{RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"},
}},
}},
- {RPCResult::Type::NUM, "relayfee", "minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB"},
- {RPCResult::Type::NUM, "incrementalfee", "minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::NUM, "relayfee", "minimum relay fee rate for transactions in " + CURRENCY_UNIT + "/kvB"},
+ {RPCResult::Type::NUM, "incrementalfee", "minimum fee rate increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::ARR, "localaddresses", "list of local addresses",
{
{RPCResult::Type::OBJ, "", "",
@@ -618,7 +630,7 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("version", CLIENT_VERSION);
obj.pushKV("subversion", strSubVersion);
obj.pushKV("protocolversion",PROTOCOL_VERSION);
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
if (node.connman) {
ServiceFlags services = node.connman->GetLocalServices();
obj.pushKV("localservices", strprintf("%016x", services));
@@ -663,8 +675,8 @@ static RPCHelpMan setban()
{
{"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"},
- {"bantime", RPCArg::Type::NUM, /* default */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
- {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME},
+ {"bantime", RPCArg::Type::NUM, RPCArg::Default{0}, "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
+ {"absolute", RPCArg::Type::BOOL, RPCArg::Default{false}, "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -680,7 +692,7 @@ static RPCHelpMan setban()
if (strCommand != "add" && strCommand != "remove") {
throw std::runtime_error(help.ToString());
}
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
@@ -749,9 +761,11 @@ static RPCHelpMan listbanned()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "address", ""},
- {RPCResult::Type::NUM_TIME, "banned_until", ""},
- {RPCResult::Type::NUM_TIME, "ban_created", ""},
+ {RPCResult::Type::STR, "address", "The IP/Subnet of the banned node"},
+ {RPCResult::Type::NUM_TIME, "ban_created", "The " + UNIX_EPOCH_TIME + " the ban was created"},
+ {RPCResult::Type::NUM_TIME, "banned_until", "The " + UNIX_EPOCH_TIME + " the ban expires"},
+ {RPCResult::Type::NUM_TIME, "ban_duration", "The ban duration, in seconds"},
+ {RPCResult::Type::NUM_TIME, "time_remaining", "The time remaining until the ban expires, in seconds"},
}},
}},
RPCExamples{
@@ -760,13 +774,14 @@ static RPCHelpMan listbanned()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
if(!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
banmap_t banMap;
node.banman->GetBanned(banMap);
+ const int64_t current_time{GetTime()};
UniValue bannedAddresses(UniValue::VARR);
for (const auto& entry : banMap)
@@ -774,8 +789,10 @@ static RPCHelpMan listbanned()
const CBanEntry& banEntry = entry.second;
UniValue rec(UniValue::VOBJ);
rec.pushKV("address", entry.first.ToString());
- rec.pushKV("banned_until", banEntry.nBanUntil);
rec.pushKV("ban_created", banEntry.nCreateTime);
+ rec.pushKV("banned_until", banEntry.nBanUntil);
+ rec.pushKV("ban_duration", (banEntry.nBanUntil - banEntry.nCreateTime));
+ rec.pushKV("time_remaining", (banEntry.nBanUntil - current_time));
bannedAddresses.push_back(rec);
}
@@ -797,7 +814,7 @@ static RPCHelpMan clearbanned()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
@@ -820,14 +837,12 @@ static RPCHelpMan setnetworkactive()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if (!node.connman) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CConnman& connman = EnsureConnman(node);
- node.connman->SetNetworkActive(request.params[0].get_bool());
+ connman.SetNetworkActive(request.params[0].get_bool());
- return node.connman->GetNetworkActive();
+ return connman.GetNetworkActive();
},
};
}
@@ -835,42 +850,46 @@ static RPCHelpMan setnetworkactive()
static RPCHelpMan getnodeaddresses()
{
return RPCHelpMan{"getnodeaddresses",
- "\nReturn known addresses which can potentially be used to find new nodes in the network\n",
+ "\nReturn known addresses, which can potentially be used to find new nodes in the network.\n",
{
- {"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."},
+ {"count", RPCArg::Type::NUM, RPCArg::Default{1}, "The maximum number of addresses to return. Specify 0 to return all known addresses."},
+ {"network", RPCArg::Type::STR, RPCArg::DefaultHint{"all networks"}, "Return only addresses of the specified network. Can be one of: " + Join(GetNetworkNames(), ", ") + "."},
},
RPCResult{
RPCResult::Type::ARR, "", "",
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"},
- {RPCResult::Type::NUM, "services", "The services offered"},
+ {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"},
+ {RPCResult::Type::NUM, "services", "The services offered by the node"},
{RPCResult::Type::STR, "address", "The address of the node"},
- {RPCResult::Type::NUM, "port", "The port of the node"},
+ {RPCResult::Type::NUM, "port", "The port number of the node"},
+ {RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") the node connected through"},
}},
}
},
RPCExamples{
HelpExampleCli("getnodeaddresses", "8")
- + HelpExampleRpc("getnodeaddresses", "8")
+ + HelpExampleCli("getnodeaddresses", "4 \"i2p\"")
+ + HelpExampleCli("-named getnodeaddresses", "network=onion count=12")
+ + HelpExampleRpc("getnodeaddresses", "8")
+ + HelpExampleRpc("getnodeaddresses", "4, \"i2p\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
- if (!node.connman) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CConnman& connman = EnsureConnman(node);
- int count = 1;
- if (!request.params[0].isNull()) {
- count = request.params[0].get_int();
- if (count < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
- }
+ const int count{request.params[0].isNull() ? 1 : request.params[0].get_int()};
+ if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
+
+ const std::optional<Network> network{request.params[1].isNull() ? std::nullopt : std::optional<Network>{ParseNetwork(request.params[1].get_str())}};
+ if (network == NET_UNROUTABLE) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Network not recognized: %s", request.params[1].get_str()));
}
+
// returns a shuffled list of CAddress
- std::vector<CAddress> vAddr = node.connman->GetAddresses(count, /* max_pct */ 0);
+ const std::vector<CAddress> vAddr{connman.GetAddresses(count, /* max_pct */ 0, network)};
UniValue ret(UniValue::VARR);
for (const CAddress& addr : vAddr) {
@@ -879,6 +898,7 @@ static RPCHelpMan getnodeaddresses()
obj.pushKV("services", (uint64_t)addr.nServices);
obj.pushKV("address", addr.ToStringIP());
obj.pushKV("port", addr.GetPort());
+ obj.pushKV("network", GetNetworkName(addr.GetNetClass()));
ret.push_back(obj);
}
return ret;
@@ -906,31 +926,27 @@ static RPCHelpMan addpeeraddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
if (!node.addrman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled");
}
- UniValue obj(UniValue::VOBJ);
-
- std::string addr_string = request.params[0].get_str();
- uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
+ const std::string& addr_string{request.params[0].get_str()};
+ const uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
+ UniValue obj(UniValue::VOBJ);
CNetAddr net_addr;
- if (!LookupHost(addr_string, net_addr, false)) {
- obj.pushKV("success", false);
- return obj;
- }
- CAddress address = CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK|NODE_WITNESS));
- address.nTime = GetAdjustedTime();
- // The source address is set equal to the address. This is equivalent to the peer
- // announcing itself.
- if (!node.addrman->Add(address, address)) {
- obj.pushKV("success", false);
- return obj;
+ bool success{false};
+
+ if (LookupHost(addr_string, net_addr, false)) {
+ CAddress address{{net_addr, port}, ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
+ address.nTime = GetAdjustedTime();
+ // The source address is set equal to the address. This is equivalent to the peer
+ // announcing itself.
+ if (node.addrman->Add(address, address)) success = true;
}
- obj.pushKV("success", true);
+ obj.pushKV("success", success);
return obj;
},
};
diff --git a/src/rpc/net.h b/src/rpc/net.h
new file mode 100644
index 0000000000..7a4d8440ba
--- /dev/null
+++ b/src/rpc/net.h
@@ -0,0 +1,15 @@
+// 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_RPC_NET_H
+#define BITCOIN_RPC_NET_H
+
+class CConnman;
+class PeerManager;
+struct NodeContext;
+
+CConnman& EnsureConnman(const NodeContext& node);
+PeerManager& EnsurePeerman(const NodeContext& node);
+
+#endif // BITCOIN_RPC_NET_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 7932bd2915..339d711ac9 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,10 +10,12 @@
#include <index/txindex.h>
#include <key_io.h>
#include <merkleblock.h>
+#include <node/blockstorage.h>
#include <node/coin.h>
#include <node/context.h>
#include <node/psbt.h>
#include <node/transaction.h>
+#include <policy/packages.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -40,7 +42,7 @@
#include <univalue.h>
-static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, CChainState& active_chainstate)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
@@ -53,10 +55,10 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
LOCK(cs_main);
entry.pushKV("blockhash", hashBlock.GetHex());
- CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
+ CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock);
if (pindex) {
- if (::ChainActive().Contains(pindex)) {
- entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight);
+ if (active_chainstate.m_chain.Contains(pindex)) {
+ entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight);
entry.pushKV("time", pindex->GetBlockTime());
entry.pushKV("blocktime", pindex->GetBlockTime());
}
@@ -84,7 +86,7 @@ static RPCHelpMan getrawtransaction()
"If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If false, return a string, otherwise return a json object"},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If false, return a string, otherwise return a json object"},
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The block in which to look for the transaction"},
},
{
@@ -157,7 +159,8 @@ static RPCHelpMan getrawtransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const NodeContext& node = EnsureNodeContext(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
@@ -178,11 +181,11 @@ static RPCHelpMan getrawtransaction()
LOCK(cs_main);
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
- blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash);
+ blockindex = chainman.m_blockman.LookupBlockIndex(blockhash);
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
- in_active_chain = ::ChainActive().Contains(blockindex);
+ in_active_chain = chainman.ActiveChain().Contains(blockindex);
}
bool f_txindex_ready = false;
@@ -215,7 +218,7 @@ static RPCHelpMan getrawtransaction()
UniValue result(UniValue::VOBJ);
if (blockindex) result.pushKV("in_active_chain", in_active_chain);
- TxToJSON(*tx, hash_block, result);
+ TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate());
return result;
},
};
@@ -257,21 +260,23 @@ static RPCHelpMan gettxoutproof()
CBlockIndex* pblockindex = nullptr;
uint256 hashBlock;
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
if (!request.params[1].isNull()) {
LOCK(cs_main);
hashBlock = ParseHashV(request.params[1], "blockhash");
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
} else {
LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) {
- const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx);
+ const Coin& coin = AccessByTxid(active_chainstate.CoinsTip(), tx);
if (!coin.IsSpent()) {
- pblockindex = ::ChainActive()[coin.nHeight];
+ pblockindex = active_chainstate.m_chain[coin.nHeight];
break;
}
}
@@ -290,7 +295,7 @@ static RPCHelpMan gettxoutproof()
if (!tx || hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
}
- pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
+ pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
}
@@ -348,10 +353,11 @@ static RPCHelpMan verifytxoutproof()
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
return res;
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
- if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
+ const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
+ if (!pindex || !chainman.ActiveChain().Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -382,7 +388,7 @@ static RPCHelpMan createrawtransaction()
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
- {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"},
},
},
},
@@ -392,7 +398,7 @@ static RPCHelpMan createrawtransaction()
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
},
@@ -404,8 +410,8 @@ static RPCHelpMan createrawtransaction()
},
},
},
- {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125-replaceable.\n"
+ {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125-replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
@@ -444,7 +450,7 @@ static RPCHelpMan decoderawtransaction()
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
- {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ {"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
"If iswitness is not present, heuristic tests will be used in decoding.\n"
"If true, only witness deserialization will be tried.\n"
"If false, only non-witness deserialization will be tried.\n"
@@ -675,10 +681,11 @@ static RPCHelpMan combinerawtransaction()
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- const CTxMemPool& mempool = EnsureMemPool(request.context);
- LOCK(cs_main);
- LOCK(mempool.cs);
- CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+ ChainstateManager& chainman = EnsureChainman(node);
+ LOCK2(cs_main, mempool.cs);
+ CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@@ -746,7 +753,7 @@ static RPCHelpMan signrawtransactionwithkey()
},
},
},
- {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of:\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of:\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -802,7 +809,7 @@ static RPCHelpMan signrawtransactionwithkey()
for (const CTxIn& txin : mtx.vin) {
coins[txin.prevout]; // Create empty map entry keyed by prevout.
}
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
FindCoins(node, coins);
// Parse the prevtxs array
@@ -826,9 +833,9 @@ static RPCHelpMan sendrawtransaction()
"\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
- {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()),
+ {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
- "/kB.\nSet to 0 to accept any fee rate.\n"},
+ "/kvB.\nSet to 0 to accept any fee rate.\n"},
},
RPCResult{
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
@@ -865,7 +872,7 @@ static RPCHelpMan sendrawtransaction()
std::string err_string;
AssertLockNotHeld(cs_main);
- NodeContext& node = EnsureNodeContext(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
@@ -879,8 +886,11 @@ static RPCHelpMan sendrawtransaction()
static RPCHelpMan testmempoolaccept()
{
return RPCHelpMan{"testmempoolaccept",
- "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
- "\nThis checks if the transaction violates the consensus or policy rules.\n"
+ "\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
+ "\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
+ "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
+ "\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n"
+ "\nThis checks if transactions violate the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
{"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.\n"
@@ -889,17 +899,21 @@ static RPCHelpMan testmempoolaccept()
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
},
- {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"},
},
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
- "Length is exactly one for now.",
+ "Returns results for each transaction in the same order they were passed in.\n"
+ "It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
- {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
+ {RPCResult::Type::STR, "package-error", "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
+ {RPCResult::Type::BOOL, "allowed", "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate."
+ "If not present, the tx was not fully validated due to a failure in another tx in the list."},
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
{RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)",
{
@@ -926,59 +940,86 @@ static RPCHelpMan testmempoolaccept()
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
});
- if (request.params[0].get_array().size() != 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
+ const UniValue raw_transactions = request.params[0].get_array();
+ if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
}
- CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
- }
- CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
-
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate(AmountFromValue(request.params[1]));
- CTxMemPool& mempool = EnsureMemPool(request.context);
- int64_t virtual_size = GetVirtualTransactionSize(*tx);
- CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
-
- UniValue result(UniValue::VARR);
- UniValue result_0(UniValue::VOBJ);
- result_0.pushKV("txid", tx->GetHash().GetHex());
- result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
-
- 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 (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);
+ std::vector<CTransactionRef> txns;
+ for (const auto& rawtx : raw_transactions.getValues()) {
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, rawtx.get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
+ "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
}
- result.push_back(std::move(result_0));
- } else {
- 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");
+ txns.emplace_back(MakeTransactionRef(std::move(mtx)));
+ }
+
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CTxMemPool& mempool = EnsureMemPool(node);
+ CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
+ const PackageMempoolAcceptResult package_result = [&] {
+ LOCK(::cs_main);
+ if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
+ return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
+ AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true));
+ }();
+
+ UniValue rpc_result(UniValue::VARR);
+ // We will check transaction fees we iterate through txns in order. If any transaction fee
+ // exceeds maxfeerate, we will keave the rest of the validation results blank, because it
+ // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
+ // not be submitted.
+ bool exit_early{false};
+ for (const auto& tx : txns) {
+ UniValue result_inner(UniValue::VOBJ);
+ result_inner.pushKV("txid", tx->GetHash().GetHex());
+ result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
+ if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
+ result_inner.pushKV("package-error", package_result.m_state.GetRejectReason());
+ }
+ auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
+ if (exit_early || it == package_result.m_tx_results.end()) {
+ // Validation unfinished. Just return the txid and wtxid.
+ rpc_result.push_back(result_inner);
+ continue;
+ }
+ const auto& tx_result = it->second;
+ if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ const CAmount fee = tx_result.m_base_fees.value();
+ // Check that fee does not exceed maximum fee
+ const int64_t virtual_size = GetVirtualTransactionSize(*tx);
+ const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
+ if (max_raw_tx_fee && fee > max_raw_tx_fee) {
+ result_inner.pushKV("allowed", false);
+ result_inner.pushKV("reject-reason", "max-fee-exceeded");
+ exit_early = true;
+ } else {
+ // Only return the fee and vsize if the transaction would pass ATMP.
+ // These can be used to calculate the feerate.
+ result_inner.pushKV("allowed", true);
+ result_inner.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_inner.pushKV("fees", fees);
+ }
} else {
- result_0.pushKV("reject-reason", state.GetRejectReason());
+ result_inner.pushKV("allowed", false);
+ const TxValidationState state = tx_result.m_state;
+ if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
+ result_inner.pushKV("reject-reason", "missing-inputs");
+ } else {
+ result_inner.pushKV("reject-reason", state.GetRejectReason());
+ }
}
- result.push_back(std::move(result_0));
+ rpc_result.push_back(result_inner);
}
- return result;
+ return rpc_result;
},
};
}
@@ -1353,7 +1394,7 @@ static RPCHelpMan finalizepsbt()
"Implements the Finalizer and Extractor roles.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
- {"extract", RPCArg::Type::BOOL, /* default */ "true", "If true and the transaction is complete,\n"
+ {"extract", RPCArg::Type::BOOL, RPCArg::Default{true}, "If true and the transaction is complete,\n"
" extract and return the complete transaction in normal network serialization instead of the PSBT."},
},
RPCResult{
@@ -1415,7 +1456,7 @@ static RPCHelpMan createpsbt()
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
- {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
+ {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"},
},
},
},
@@ -1425,7 +1466,7 @@ static RPCHelpMan createpsbt()
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT},
},
@@ -1437,8 +1478,8 @@ static RPCHelpMan createpsbt()
},
},
},
- {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "false", "Marks this transaction as BIP125 replaceable.\n"
+ {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."},
},
RPCResult{
@@ -1490,9 +1531,9 @@ static RPCHelpMan converttopsbt()
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of a raw transaction"},
- {"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion\n"
+ {"permitsigdata", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, any signatures in the input will be discarded and conversion\n"
" will continue. If false, RPC will fail if any signatures are present."},
- {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ {"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
"If iswitness is not present, heuristic tests will be used in decoding.\n"
"If true, only witness deserialization will be tried.\n"
"If false, only non-witness deserialization will be tried.\n"
@@ -1562,7 +1603,7 @@ static RPCHelpMan utxoupdatepsbt()
{"", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with an output descriptor and extra information", {
{"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
- {"range", RPCArg::Type::RANGE, "1000", "Up to what index HD chains should be explored (either end or [begin,end])"},
+ {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "Up to what index HD chains should be explored (either end or [begin,end])"},
}},
}},
},
@@ -1598,9 +1639,11 @@ static RPCHelpMan utxoupdatepsbt()
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
- const CTxMemPool& mempool = EnsureMemPool(request.context);
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
+ ChainstateManager& chainman = EnsureChainman(node);
LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
+ CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@@ -1772,7 +1815,7 @@ static RPCHelpMan analyzepsbt()
}},
}},
{RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"},
- {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled"},
+ {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kvB. Shown only if all UTXO slots in the PSBT have been filled"},
{RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"},
{RPCResult::Type::STR, "next", "Role of the next person that this psbt needs to go to"},
{RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"},
diff --git a/src/rpc/register.h b/src/rpc/register.h
index 374a1e3db8..6724203ffe 100644
--- a/src/rpc/register.h
+++ b/src/rpc/register.h
@@ -19,6 +19,8 @@ void RegisterMiscRPCCommands(CRPCTable &tableRPC);
void RegisterMiningRPCCommands(CRPCTable &tableRPC);
/** Register raw transaction RPC commands */
void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC);
+/** Register raw transaction RPC commands */
+void RegisterSignerRPCCommands(CRPCTable &tableRPC);
static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
{
@@ -27,6 +29,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
RegisterMiscRPCCommands(t);
RegisterMiningRPCCommands(t);
RegisterRawTransactionRPCCommands(t);
+#ifdef ENABLE_EXTERNAL_SIGNER
+ RegisterSignerRPCCommands(t);
+#endif // ENABLE_EXTERNAL_SIGNER
}
#endif // BITCOIN_RPC_REGISTER_H
diff --git a/src/rpc/request.h b/src/rpc/request.h
index e1569673f6..bb091efea8 100644
--- a/src/rpc/request.h
+++ b/src/rpc/request.h
@@ -35,18 +35,7 @@ public:
std::string URI;
std::string authUser;
std::string peerAddr;
- const std::any& context;
-
- explicit JSONRPCRequest(const std::any& 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 std::any& context)
- : id(other.id), strMethod(other.strMethod), params(other.params), mode(other.mode), URI(other.URI),
- authUser(other.authUser), peerAddr(other.peerAddr), context(context)
- {
- }
+ std::any context;
void parse(const UniValue& valRequest);
};
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 2f05c8842f..cf80b08b96 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -135,10 +135,11 @@ static RPCHelpMan help()
return RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
- {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
+ {"command", RPCArg::Type::STR, RPCArg::DefaultHint{"all commands"}, "The command to get help on"},
},
- RPCResult{
- RPCResult::Type::STR, "", "The help text"
+ {
+ RPCResult{RPCResult::Type::STR, "", "The help text"},
+ RPCResult{RPCResult::Type::ANY, "", ""},
},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 44e58cb75f..2059628b54 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -74,12 +74,12 @@ void RPCTypeCheckObj(const UniValue& o,
}
}
-CAmount AmountFromValue(const UniValue& value)
+CAmount AmountFromValue(const UniValue& value, int decimals)
{
if (!value.isNum() && !value.isStr())
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
CAmount amount;
- if (!ParseFixedPoint(value.getValStr(), 8, &amount))
+ if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
if (!MoneyRange(amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
@@ -113,17 +113,80 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
+namespace {
+
+/**
+ * Quote an argument for shell.
+ *
+ * @note This is intended for help, not for security-sensitive purposes.
+ */
+std::string ShellQuote(const std::string& s)
+{
+ std::string result;
+ result.reserve(s.size() * 2);
+ for (const char ch: s) {
+ if (ch == '\'') {
+ result += "'\''";
+ } else {
+ result += ch;
+ }
+ }
+ return "'" + result + "'";
+}
+
+/**
+ * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
+ *
+ * @note This is intended for help, not for security-sensitive purposes.
+ */
+std::string ShellQuoteIfNeeded(const std::string& s)
+{
+ for (const char ch: s) {
+ if (ch == ' ' || ch == '\'' || ch == '"') {
+ return ShellQuote(s);
+ }
+ }
+
+ return s;
+}
+
+}
+
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
}
+std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
+{
+ std::string result = "> bitcoin-cli -named " + methodname;
+ for (const auto& argpair: args) {
+ const auto& value = argpair.second.isStr()
+ ? argpair.second.get_str()
+ : argpair.second.write();
+ result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
+ }
+ result += "\n";
+ return result;
+}
+
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
{
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
"\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
}
+std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
+{
+ UniValue params(UniValue::VOBJ);
+ for (const auto& param: args) {
+ params.pushKV(param.first, param.second);
+ }
+
+ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
+ "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
+}
+
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
{
@@ -168,16 +231,12 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
if ((int)pubkeys.size() < required) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required));
}
- if (pubkeys.size() > 16) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Number of keys involved in the multisignature address creation > 16\nReduce the number");
+ if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG));
}
script_out = GetScriptForMultisig(required, pubkeys);
- if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
- }
-
// Check if any keys are uncompressed. If so, the type is legacy
for (const CPubKey& pk : pubkeys) {
if (!pk.IsCompressed()) {
@@ -186,6 +245,10 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
}
}
+ if (type == OutputType::LEGACY && script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
+ }
+
// Make the address
CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
@@ -238,6 +301,16 @@ public:
return obj;
}
+ UniValue operator()(const WitnessV1Taproot& tap) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", true);
+ obj.pushKV("iswitness", true);
+ obj.pushKV("witness_version", 1);
+ obj.pushKV("witness_program", HexStr(tap));
+ return obj;
+ }
+
UniValue operator()(const WitnessUnknown& id) const
{
UniValue obj(UniValue::VOBJ);
@@ -435,6 +508,33 @@ RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RP
for (const std::string& name : names) {
CHECK_NONFATAL(named_args.insert(name).second);
}
+ // Default value type should match argument type only when defined
+ if (arg.m_fallback.index() == 2) {
+ const RPCArg::Type type = arg.m_type;
+ switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {
+ case UniValue::VOBJ:
+ CHECK_NONFATAL(type == RPCArg::Type::OBJ);
+ break;
+ case UniValue::VARR:
+ CHECK_NONFATAL(type == RPCArg::Type::ARR);
+ break;
+ case UniValue::VSTR:
+ CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);
+ break;
+ case UniValue::VNUM:
+ CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);
+ break;
+ case UniValue::VBOOL:
+ CHECK_NONFATAL(type == RPCArg::Type::BOOL);
+ break;
+ case UniValue::VNULL:
+ // Null values are accepted in all arguments
+ break;
+ default:
+ CHECK_NONFATAL(false);
+ break;
+ }
+ }
}
}
@@ -442,6 +542,7 @@ std::string RPCResults::ToDescriptionString() const
{
std::string result;
for (const auto& r : m_results) {
+ if (r.m_type == RPCResult::Type::ANY) continue; // for testing only
if (r.m_cond.empty()) {
result += "\nResult:\n";
} else {
@@ -459,7 +560,7 @@ std::string RPCExamples::ToDescriptionString() const
return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
}
-UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request)
+UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
{
if (request.mode == JSONRPCRequest::GET_ARGS) {
return GetArgMap();
@@ -471,7 +572,9 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request)
if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
throw std::runtime_error(ToString());
}
- return m_fun(*this, request);
+ const UniValue ret = m_fun(*this, request);
+ CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); }));
+ return ret;
}
bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
@@ -580,7 +683,7 @@ std::string RPCArg::GetName() const
bool RPCArg::IsOptional() const
{
- if (m_fallback.index() == 1) {
+ if (m_fallback.index() != 0) {
return true;
} else {
return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback);
@@ -628,7 +731,9 @@ std::string RPCArg::ToDescriptionString() const
} // no default case, so the compiler can warn about missing cases
}
if (m_fallback.index() == 1) {
- ret += ", optional, default=" + std::get<std::string>(m_fallback);
+ ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback);
+ } else if (m_fallback.index() == 2) {
+ ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
} else {
switch (std::get<RPCArg::Optional>(m_fallback)) {
case RPCArg::Optional::OMITTED: {
@@ -677,6 +782,9 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
sections.PushSection({indent + "..." + maybe_separator, m_description});
return;
}
+ case Type::ANY: {
+ CHECK_NONFATAL(false); // Only for testing
+ }
case Type::NONE: {
sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
return;
@@ -742,6 +850,42 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
CHECK_NONFATAL(false);
}
+bool RPCResult::MatchesType(const UniValue& result) const
+{
+ switch (m_type) {
+ case Type::ELISION: {
+ return false;
+ }
+ case Type::ANY: {
+ return true;
+ }
+ case Type::NONE: {
+ return UniValue::VNULL == result.getType();
+ }
+ case Type::STR:
+ case Type::STR_HEX: {
+ return UniValue::VSTR == result.getType();
+ }
+ case Type::NUM:
+ case Type::STR_AMOUNT:
+ case Type::NUM_TIME: {
+ return UniValue::VNUM == result.getType();
+ }
+ case Type::BOOL: {
+ return UniValue::VBOOL == result.getType();
+ }
+ case Type::ARR_FIXED:
+ case Type::ARR: {
+ return UniValue::VARR == result.getType();
+ }
+ case Type::OBJ_DYN:
+ case Type::OBJ: {
+ return UniValue::VOBJ == result.getType();
+ }
+ } // no default case, so the compiler can warn about missing cases
+ CHECK_NONFATAL(false);
+}
+
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 94c2d2d626..d43ee33b0f 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -72,14 +72,25 @@ void RPCTypeCheckObj(const UniValue& o,
* Utilities: convert hex-encoded Values
* (throws error if not hex).
*/
-extern uint256 ParseHashV(const UniValue& v, std::string strName);
-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);
+uint256 ParseHashV(const UniValue& v, std::string strName);
+uint256 ParseHashO(const UniValue& o, std::string strKey);
+std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
+std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-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);
+/**
+ * Validate and return a CAmount from a UniValue number or string.
+ *
+ * @param[in] value UniValue number or string to parse.
+ * @param[in] decimals Number of significant digits (default: 8).
+ * @returns a CAmount if the various checks pass.
+ */
+CAmount AmountFromValue(const UniValue& value, int decimals = 8);
+
+using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
+std::string HelpExampleCli(const std::string& methodname, const std::string& args);
+std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args);
+std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
+std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args);
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in);
@@ -141,7 +152,9 @@ struct RPCArg {
*/
OMITTED,
};
- using Fallback = std::variant<Optional, /* default value for optional args */ std::string>;
+ using DefaultHint = std::string;
+ using Default = UniValue;
+ using Fallback = std::variant<Optional, /* hint for default value */ DefaultHint, /* default constant value */ Default>;
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
const bool m_hidden;
@@ -167,7 +180,7 @@ struct RPCArg {
m_oneline_description{std::move(oneline_description)},
m_type_str{std::move(type_str)}
{
- CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
+ CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ && type != Type::OBJ_USER_KEYS);
}
RPCArg(
@@ -187,7 +200,7 @@ struct RPCArg {
m_oneline_description{std::move(oneline_description)},
m_type_str{std::move(type_str)}
{
- CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ);
+ CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ || type == Type::OBJ_USER_KEYS);
}
bool IsOptional() const;
@@ -223,6 +236,7 @@ struct RPCResult {
NUM,
BOOL,
NONE,
+ ANY, //!< Special type to disable type checks (for testing only)
STR_AMOUNT, //!< Special string to represent a floating point amount
STR_HEX, //!< Special string with only hex chars
OBJ_DYN, //!< Special dictionary with keys that are not literals
@@ -295,6 +309,8 @@ struct RPCResult {
std::string ToStringObj() const;
/** Return the description string, including the result type. */
std::string ToDescriptionString() const;
+ /** Check whether the result JSON type matches. */
+ bool MatchesType(const UniValue& result) const;
};
struct RPCResults {
@@ -333,7 +349,7 @@ 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);
+ UniValue HandleRequest(const JSONRPCRequest& request) const;
std::string ToString() const;
/** Return the named args that need to be converted from string to another JSON type */
UniValue GetArgMap() const;
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index b3ee23f139..02ada969a4 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -5,6 +5,7 @@
#include <scheduler.h>
#include <random.h>
+#include <util/time.h>
#include <assert.h>
#include <functional>
@@ -80,7 +81,7 @@ void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::tim
void CScheduler::MockForward(std::chrono::seconds delta_seconds)
{
- assert(delta_seconds.count() > 0 && delta_seconds < std::chrono::hours{1});
+ assert(delta_seconds > 0s && delta_seconds <= 1h);
{
LOCK(newTaskMutex);
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 76609f01a7..a9aa6a0060 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -92,7 +92,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
set_error(err, bitcoinconsensus_ERR_OK);
PrecomputedTransactionData txdata(tx);
- return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), nullptr);
+ return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr);
} catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
}
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 30399dca51..51cf8a7d62 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -241,9 +241,10 @@ public:
class ConstPubkeyProvider final : public PubkeyProvider
{
CPubKey m_pubkey;
+ bool m_xonly;
public:
- ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey) : PubkeyProvider(exp_index), m_pubkey(pubkey) {}
+ ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly = false) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {}
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override
{
key = m_pubkey;
@@ -254,7 +255,7 @@ public:
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
- std::string ToString() const override { return HexStr(m_pubkey); }
+ std::string ToString() const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
@@ -481,34 +482,36 @@ class DescriptorImpl : public Descriptor
const std::string m_name;
protected:
- //! The sub-descriptor argument (nullptr for everything but SH and WSH).
+ //! The sub-descriptor arguments (empty for everything but SH and WSH).
//! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
//! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
- const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
+ //! Subdescriptors can only ever generate a single script.
+ const std::vector<std::unique_ptr<DescriptorImpl>> m_subdescriptor_args;
//! Return a serialization of anything except pubkey and script arguments, to be prepended to those.
virtual std::string ToStringExtra() const { return ""; }
/** A helper function to construct the scripts for this descriptor.
*
- * This function is invoked once for every CScript produced by evaluating
- * m_subdescriptor_arg, or just once in case m_subdescriptor_arg is nullptr.
-
+ * This function is invoked once by ExpandHelper.
+ *
* @param pubkeys The evaluations of the m_pubkey_args field.
- * @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr).
+ * @param scripts The evaluations of m_subdescriptor_args (one for each m_subdescriptor_args element).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
- * The script arguments to this function are automatically added, as is the origin info of the provided pubkeys.
+ * The origin info of the provided pubkeys is automatically added.
* @return A vector with scriptPubKeys for this descriptor.
*/
- virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
+ virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, Span<const CScript> scripts, FlatSigningProvider& out) const = 0;
public:
- DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_arg(std::move(script)) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args() {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::vector<std::unique_ptr<DescriptorImpl>> scripts, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(std::move(scripts)) {}
bool IsSolvable() const override
{
- if (m_subdescriptor_arg) {
- if (!m_subdescriptor_arg->IsSolvable()) return false;
+ for (const auto& arg : m_subdescriptor_args) {
+ if (!arg->IsSolvable()) return false;
}
return true;
}
@@ -518,12 +521,24 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pubkey->IsRange()) return true;
}
- if (m_subdescriptor_arg) {
- if (m_subdescriptor_arg->IsRange()) return true;
+ for (const auto& arg : m_subdescriptor_args) {
+ if (arg->IsRange()) return true;
}
return false;
}
+ virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const
+ {
+ size_t pos = 0;
+ for (const auto& scriptarg : m_subdescriptor_args) {
+ if (pos++) ret += ",";
+ std::string tmp;
+ if (!scriptarg->ToStringHelper(arg, tmp, priv, normalized)) return false;
+ ret += std::move(tmp);
+ }
+ return true;
+ }
+
bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const
{
std::string extra = ToStringExtra();
@@ -541,13 +556,10 @@ public:
}
ret += std::move(tmp);
}
- if (m_subdescriptor_arg) {
- if (pos++) ret += ",";
- std::string tmp;
- if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv, normalized)) return false;
- ret += std::move(tmp);
- }
- out = std::move(ret) + ")";
+ std::string subscript;
+ if (!ToStringSubScriptHelper(arg, subscript, priv, normalized)) return false;
+ if (pos && subscript.size()) ret += ',';
+ out = std::move(ret) + std::move(subscript) + ")";
return true;
}
@@ -577,17 +589,20 @@ public:
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
entries.reserve(m_pubkey_args.size());
- // Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
+ // Construct temporary data in `entries`, `subscripts`, and `subprovider` to avoid producing output in case of failure.
for (const auto& p : m_pubkey_args) {
entries.emplace_back();
if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second, read_cache, write_cache)) return false;
}
std::vector<CScript> subscripts;
- if (m_subdescriptor_arg) {
- FlatSigningProvider subprovider;
- if (!m_subdescriptor_arg->ExpandHelper(pos, arg, read_cache, subscripts, subprovider, write_cache)) return false;
- out = Merge(out, subprovider);
+ FlatSigningProvider subprovider;
+ for (const auto& subarg : m_subdescriptor_args) {
+ std::vector<CScript> outscripts;
+ if (!subarg->ExpandHelper(pos, arg, read_cache, outscripts, subprovider, write_cache)) return false;
+ assert(outscripts.size() == 1);
+ subscripts.emplace_back(std::move(outscripts[0]));
}
+ out = Merge(std::move(out), std::move(subprovider));
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
@@ -595,17 +610,8 @@ public:
pubkeys.push_back(entry.first);
out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
- if (m_subdescriptor_arg) {
- for (const auto& subscript : subscripts) {
- out.scripts.emplace(CScriptID(subscript), subscript);
- std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
- for (auto& addscript : addscripts) {
- output_scripts.push_back(std::move(addscript));
- }
- }
- } else {
- output_scripts = MakeScripts(pubkeys, nullptr, out);
- }
+
+ output_scripts = MakeScripts(pubkeys, MakeSpan(subscripts), out);
return true;
}
@@ -626,38 +632,42 @@ public:
if (!p->GetPrivKey(pos, provider, key)) continue;
out.keys.emplace(key.GetPubKey().GetID(), key);
}
- if (m_subdescriptor_arg) {
- FlatSigningProvider subprovider;
- m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
- out = Merge(out, subprovider);
+ for (const auto& arg : m_subdescriptor_args) {
+ arg->ExpandPrivate(pos, provider, out);
}
}
std::optional<OutputType> GetOutputType() const override { return std::nullopt; }
};
+static std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) {
+ if (std::holds_alternative<PKHash>(dest) ||
+ std::holds_alternative<ScriptHash>(dest)) {
+ return OutputType::LEGACY;
+ }
+ if (std::holds_alternative<WitnessV0KeyHash>(dest) ||
+ std::holds_alternative<WitnessV0ScriptHash>(dest) ||
+ std::holds_alternative<WitnessV1Taproot>(dest) ||
+ std::holds_alternative<WitnessUnknown>(dest)) {
+ return OutputType::BECH32;
+ }
+ return std::nullopt;
+}
+
/** A parsed addr(A) descriptor. */
class AddressDescriptor final : public DescriptorImpl
{
const CTxDestination m_destination;
protected:
std::string ToStringExtra() const override { return EncodeDestination(m_destination); }
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); }
public:
- AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
+ AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, "addr"), m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
std::optional<OutputType> GetOutputType() const override
{
- switch (m_destination.index()) {
- case 1 /* PKHash */:
- case 2 /* ScriptHash */: return OutputType::LEGACY;
- case 3 /* WitnessV0ScriptHash */:
- case 4 /* WitnessV0KeyHash */:
- case 5 /* WitnessUnknown */: return OutputType::BECH32;
- case 0 /* CNoDestination */:
- default: return std::nullopt;
- }
+ return OutputTypeFromDestination(m_destination);
}
bool IsSingleType() const final { return true; }
};
@@ -668,24 +678,16 @@ class RawDescriptor final : public DescriptorImpl
const CScript m_script;
protected:
std::string ToStringExtra() const override { return HexStr(m_script); }
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript>, FlatSigningProvider&) const override { return Vector(m_script); }
public:
- RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
+ RawDescriptor(CScript script) : DescriptorImpl({}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
std::optional<OutputType> GetOutputType() const override
{
CTxDestination dest;
ExtractDestination(m_script, dest);
- switch (dest.index()) {
- case 1 /* PKHash */:
- case 2 /* ScriptHash */: return OutputType::LEGACY;
- case 3 /* WitnessV0ScriptHash */:
- case 4 /* WitnessV0KeyHash */:
- case 5 /* WitnessUnknown */: return OutputType::BECH32;
- case 0 /* CNoDestination */:
- default: return std::nullopt;
- }
+ return OutputTypeFromDestination(dest);
}
bool IsSingleType() const final { return true; }
};
@@ -693,10 +695,20 @@ public:
/** A parsed pk(P) descriptor. */
class PKDescriptor final : public DescriptorImpl
{
+private:
+ const bool m_xonly;
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override
+ {
+ if (m_xonly) {
+ CScript script = CScript() << ToByteVector(XOnlyPubKey(keys[0])) << OP_CHECKSIG;
+ return Vector(std::move(script));
+ } else {
+ return Vector(GetScriptForRawPubKey(keys[0]));
+ }
+ }
public:
- PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pk") {}
+ PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {}
bool IsSingleType() const final { return true; }
};
@@ -704,14 +716,14 @@ public:
class PKHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
return Vector(GetScriptForDestination(PKHash(id)));
}
public:
- PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {}
+ PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pkh") {}
std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; }
bool IsSingleType() const final { return true; }
};
@@ -720,14 +732,14 @@ public:
class WPKHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
return Vector(GetScriptForDestination(WitnessV0KeyHash(id)));
}
public:
- WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {}
+ WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "wpkh") {}
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
bool IsSingleType() const final { return true; }
};
@@ -736,7 +748,7 @@ public:
class ComboDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider& out) const override
{
std::vector<CScript> ret;
CKeyID id = keys[0].GetID();
@@ -752,7 +764,7 @@ protected:
return ret;
}
public:
- ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "combo") {}
+ ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "combo") {}
bool IsSingleType() const final { return false; }
};
@@ -763,7 +775,7 @@ class MultisigDescriptor final : public DescriptorImpl
const bool m_sorted;
protected:
std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override {
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override {
if (m_sorted) {
std::vector<CPubKey> sorted_keys(keys);
std::sort(sorted_keys.begin(), sorted_keys.end());
@@ -772,7 +784,7 @@ protected:
return Vector(GetScriptForMultisig(m_threshold, keys));
}
public:
- MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), {}, sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
+ MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
bool IsSingleType() const final { return true; }
};
@@ -780,14 +792,19 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(ScriptHash(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override
+ {
+ auto ret = Vector(GetScriptForDestination(ScriptHash(scripts[0])));
+ if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]);
+ return ret;
+ }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
std::optional<OutputType> GetOutputType() const override
{
- assert(m_subdescriptor_arg);
- if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
+ assert(m_subdescriptor_args.size() == 1);
+ if (m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT;
return OutputType::LEGACY;
}
bool IsSingleType() const final { return true; }
@@ -797,21 +814,78 @@ public:
class WSHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, Span<const CScript> scripts, FlatSigningProvider& out) const override
+ {
+ auto ret = Vector(GetScriptForDestination(WitnessV0ScriptHash(scripts[0])));
+ if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]);
+ return ret;
+ }
public:
WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
bool IsSingleType() const final { return true; }
};
+/** A parsed tr(...) descriptor. */
+class TRDescriptor final : public DescriptorImpl
+{
+ std::vector<int> m_depths;
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
+ {
+ TaprootBuilder builder;
+ assert(m_depths.size() == scripts.size());
+ for (size_t pos = 0; pos < m_depths.size(); ++pos) {
+ builder.Add(m_depths[pos], scripts[pos], TAPROOT_LEAF_TAPSCRIPT);
+ }
+ if (!builder.IsComplete()) return {};
+ assert(keys.size() == 1);
+ XOnlyPubKey xpk(keys[0]);
+ if (!xpk.IsFullyValid()) return {};
+ builder.Finalize(xpk);
+ return Vector(GetScriptForDestination(builder.GetOutput()));
+ }
+ bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override
+ {
+ if (m_depths.empty()) return true;
+ std::vector<bool> path;
+ for (size_t pos = 0; pos < m_depths.size(); ++pos) {
+ if (pos) ret += ',';
+ while ((int)path.size() <= m_depths[pos]) {
+ if (path.size()) ret += '{';
+ path.push_back(false);
+ }
+ std::string tmp;
+ if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false;
+ ret += std::move(tmp);
+ while (!path.empty() && path.back()) {
+ if (path.size() > 1) ret += '}';
+ path.pop_back();
+ }
+ if (!path.empty()) path.back() = true;
+ }
+ return true;
+ }
+public:
+ TRDescriptor(std::unique_ptr<PubkeyProvider> internal_key, std::vector<std::unique_ptr<DescriptorImpl>> descs, std::vector<int> depths) :
+ DescriptorImpl(Vector(std::move(internal_key)), std::move(descs), "tr"), m_depths(std::move(depths))
+ {
+ assert(m_subdescriptor_args.size() == m_depths.size());
+ }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
+ bool IsSingleType() const final { return true; }
+};
+
////////////////////////////////////////////////////////////////////////////
// Parser //
////////////////////////////////////////////////////////////////////////////
enum class ParseScriptContext {
- TOP,
- P2SH,
- P2WSH,
+ TOP, //!< Top-level context (script goes directly in scriptPubKey)
+ P2SH, //!< Inside sh() (script becomes P2SH redeemScript)
+ P2WPKH, //!< Inside wpkh() (no script, pubkey only)
+ P2WSH, //!< Inside wsh() (script becomes v0 witness script)
+ P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf)
};
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
@@ -838,10 +912,11 @@ enum class ParseScriptContext {
}
/** Parse a public key that excludes origin information. */
-std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
using namespace spanparsing;
+ bool permit_uncompressed = ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH;
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
if (str.size() == 0) {
@@ -859,6 +934,13 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
error = "Uncompressed keys are not allowed";
return nullptr;
}
+ } else if (data.size() == 32 && ctx == ParseScriptContext::P2TR) {
+ unsigned char fullkey[33] = {0x02};
+ std::copy(data.begin(), data.end(), fullkey + 1);
+ pubkey.Set(std::begin(fullkey), std::end(fullkey));
+ if (pubkey.IsFullyValid()) {
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true);
+ }
}
error = strprintf("Pubkey '%s' is invalid", str);
return nullptr;
@@ -899,7 +981,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
}
/** Parse a public key including origin information (if enabled). */
-std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
+std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
using namespace spanparsing;
@@ -908,7 +990,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c
error = "Multiple ']' characters found for a single pubkey";
return nullptr;
}
- if (origin_split.size() == 1) return ParsePubkeyInner(key_exp_index, origin_split[0], permit_uncompressed, out, error);
+ if (origin_split.size() == 1) return ParsePubkeyInner(key_exp_index, origin_split[0], ctx, out, error);
if (origin_split[0].empty() || origin_split[0][0] != '[') {
error = strprintf("Key origin start '[ character expected but not found, got '%c' instead",
origin_split[0].empty() ? /** empty, implies split char */ ']' : origin_split[0][0]);
@@ -930,37 +1012,43 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c
assert(fpr_bytes.size() == 4);
std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
if (!ParseKeyPath(slash_split, info.path, error)) return nullptr;
- auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], permit_uncompressed, out, error);
+ auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, error);
if (!provider) return nullptr;
return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider));
}
/** Parse a script in a particular context. */
-std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
+std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
using namespace spanparsing;
auto expr = Expr(sp);
bool sorted_multi = false;
if (Func("pk", expr)) {
- auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error);
+ auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
- return std::make_unique<PKDescriptor>(std::move(pubkey));
+ ++key_exp_index;
+ return std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR);
}
- if (Func("pkh", expr)) {
- auto pubkey = ParsePubkey(key_exp_index, expr, ctx != ParseScriptContext::P2WSH, out, error);
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && Func("pkh", expr)) {
+ auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
+ ++key_exp_index;
return std::make_unique<PKHDescriptor>(std::move(pubkey));
+ } else if (Func("pkh", expr)) {
+ error = "Can only have pkh at top level, in sh(), or in wsh()";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
- auto pubkey = ParsePubkey(key_exp_index, expr, true, out, error);
+ auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
+ ++key_exp_index;
return std::make_unique<ComboDescriptor>(std::move(pubkey));
- } else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) {
- error = "Cannot have combo in non-top level";
+ } else if (Func("combo", expr)) {
+ error = "Can only have combo() at top level";
return nullptr;
}
- if ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr)) {
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr))) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -975,14 +1063,14 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
return nullptr;
}
auto arg = Expr(expr);
- auto pk = ParsePubkey(key_exp_index, arg, ctx != ParseScriptContext::P2WSH, out, error);
+ auto pk = ParsePubkey(key_exp_index, arg, ctx, out, error);
if (!pk) return nullptr;
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
key_exp_index++;
}
- if (providers.empty() || providers.size() > 16) {
- error = strprintf("Cannot have %u keys in multisig; must have between 1 and 16 keys, inclusive", providers.size());
+ if (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG) {
+ error = strprintf("Cannot have %u keys in multisig; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTISIG);
return nullptr;
} else if (thres < 1) {
error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
@@ -998,35 +1086,40 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
}
}
if (ctx == ParseScriptContext::P2SH) {
+ // This limits the maximum number of compressed pubkeys to 15.
if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) {
error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE);
return nullptr;
}
}
return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
+ } else if (Func("sortedmulti", expr) || Func("multi", expr)) {
+ error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()";
+ return nullptr;
}
- if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
- auto pubkey = ParsePubkey(key_exp_index, expr, false, out, error);
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) {
+ auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
if (!pubkey) return nullptr;
+ key_exp_index++;
return std::make_unique<WPKHDescriptor>(std::move(pubkey));
- } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) {
- error = "Cannot have wpkh within wsh";
+ } else if (Func("wpkh", expr)) {
+ error = "Can only have wpkh() at top level or inside sh()";
return nullptr;
}
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 std::make_unique<SHDescriptor>(std::move(desc));
- } else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) {
- error = "Cannot have sh in non-top level";
+ } else if (Func("sh", expr)) {
+ error = "Can only have sh() at top level";
return nullptr;
}
- if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) {
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wsh", expr)) {
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error);
if (!desc || expr.size()) return nullptr;
return std::make_unique<WSHDescriptor>(std::move(desc));
- } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) {
- error = "Cannot have wsh within wsh";
+ } else if (Func("wsh", expr)) {
+ error = "Can only have wsh() at top level or inside sh()";
return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
@@ -1036,6 +1129,70 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
return nullptr;
}
return std::make_unique<AddressDescriptor>(std::move(dest));
+ } else if (Func("addr", expr)) {
+ error = "Can only have addr() at top level";
+ return nullptr;
+ }
+ if (ctx == ParseScriptContext::TOP && Func("tr", expr)) {
+ auto arg = Expr(expr);
+ auto internal_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
+ if (!internal_key) return nullptr;
+ ++key_exp_index;
+ std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
+ std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
+ if (expr.size()) {
+ if (!Const(",", expr)) {
+ error = strprintf("tr: expected ',', got '%c'", expr[0]);
+ return nullptr;
+ }
+ /** The path from the top of the tree to what we're currently processing.
+ * branches[i] == false: left branch in the i'th step from the top; true: right branch.
+ */
+ std::vector<bool> branches;
+ // Loop over all provided scripts. In every iteration exactly one script will be processed.
+ // Use a do-loop because inside this if-branch we expect at least one script.
+ do {
+ // First process all open braces.
+ while (Const("{", expr)) {
+ branches.push_back(false); // new left branch
+ if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT) {
+ error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT);
+ return nullptr;
+ }
+ }
+ // Process the actual script expression.
+ auto sarg = Expr(expr);
+ subscripts.emplace_back(ParseScript(key_exp_index, sarg, ParseScriptContext::P2TR, out, error));
+ if (!subscripts.back()) return nullptr;
+ depths.push_back(branches.size());
+ // Process closing braces; one is expected for every right branch we were in.
+ while (branches.size() && branches.back()) {
+ if (!Const("}", expr)) {
+ error = strprintf("tr(): expected '}' after script expression");
+ return nullptr;
+ }
+ branches.pop_back(); // move up one level after encountering '}'
+ }
+ // If after that, we're at the end of a left branch, expect a comma.
+ if (branches.size() && !branches.back()) {
+ if (!Const(",", expr)) {
+ error = strprintf("tr(): expected ',' after script expression");
+ return nullptr;
+ }
+ branches.back() = true; // And now we're in a right branch.
+ }
+ } while (branches.size());
+ // After we've explored a whole tree, we must be at the end of the expression.
+ if (expr.size()) {
+ error = strprintf("tr(): expected ')' after script expression");
+ return nullptr;
+ }
+ }
+ assert(TaprootBuilder::ValidDepths(depths));
+ return std::make_unique<TRDescriptor>(std::move(internal_key), std::move(subscripts), std::move(depths));
+ } else if (Func("tr", expr)) {
+ error = "Can only have tr at top level";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
@@ -1045,6 +1202,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t key_exp_index, Span<const c
}
auto bytes = ParseHex(str);
return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
+ } else if (Func("raw", expr)) {
+ error = "Can only have raw() at top level";
+ return nullptr;
}
if (ctx == ParseScriptContext::P2SH) {
error = "A function is needed within P2SH";
@@ -1073,7 +1233,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
TxoutType txntype = Solver(script, data);
if (txntype == TxoutType::PUBKEY) {
- CPubKey pubkey(data[0].begin(), data[0].end());
+ CPubKey pubkey(data[0]);
if (pubkey.IsValid()) {
return std::make_unique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
@@ -1097,7 +1257,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (txntype == TxoutType::MULTISIG) {
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
- CPubKey pubkey(data[i].begin(), data[i].end());
+ CPubKey pubkey(data[i]);
providers.push_back(InferPubkey(pubkey, ctx, provider));
}
return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers));
@@ -1174,7 +1334,8 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv
{
Span<const char> sp{descriptor};
if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
- auto ret = ParseScript(0, sp, ParseScriptContext::TOP, out, error);
+ uint32_t key_exp_index = 0;
+ auto ret = ParseScript(key_exp_index, sp, ParseScriptContext::TOP, out, error);
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
return nullptr;
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 20a4ce48b0..3c3c3ac1a8 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -225,7 +225,7 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co
return true;
}
-bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
+bool CheckMinimalPush(const valtype& data, opcodetype opcode) {
// Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal
assert(0 <= opcode && opcode <= OP_PUSHDATA4);
if (data.size() == 0) {
@@ -1484,12 +1484,23 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
-static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
-static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
-static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
+const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
+
+static bool HandleMissingData(MissingDataBehavior mdb)
+{
+ switch (mdb) {
+ case MissingDataBehavior::ASSERT_FAIL:
+ assert(!"Missing data");
+ break;
+ case MissingDataBehavior::FAIL:
+ return false;
+ }
+ assert(!"Unknown MissingDataBehavior value");
+}
template<typename T>
-bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache)
+bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb)
{
uint8_t ext_flag, key_version;
switch (sigversion) {
@@ -1509,7 +1520,9 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
assert(false);
}
assert(in_pos < tx_to.vin.size());
- assert(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready);
+ if (!(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready)) {
+ return HandleMissingData(mdb);
+ }
CHashWriter ss = HASHER_TAPSIGHASH;
@@ -1667,6 +1680,9 @@ bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vecto
int nHashType = vchSig.back();
vchSig.pop_back();
+ // Witness sighashes need the amount.
+ if (sigversion == SigVersion::WITNESS_V0 && amount < 0) return HandleMissingData(m_mdb);
+
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
if (!VerifyECDSASignature(vchSig, pubkey, sighash))
@@ -1696,7 +1712,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
}
uint256 sighash;
assert(this->txdata);
- if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata)) {
+ if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
@@ -1852,10 +1868,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
- // 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 internal pubkey, after correcting for parity.
- return q.CheckPayToContract(p, k, control[0] & 1);
+ return q.CheckTapTweak(p, k, control[0] & 1);
}
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
@@ -1873,7 +1887,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
const valtype& script_bytes = SpanPopBack(stack);
exec_script = CScript(script_bytes.begin(), script_bytes.end());
uint256 hash_exec_script;
- CSHA256().Write(&exec_script[0], exec_script.size()).Finalize(hash_exec_script.begin());
+ CSHA256().Write(exec_script.data(), exec_script.size()).Finalize(hash_exec_script.begin());
if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index b4c163c841..fa4ee83e04 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_INTERPRETER_H
#define BITCOIN_SCRIPT_INTERPRETER_H
+#include <hash.h>
#include <script/script_error.h>
#include <span.h>
#include <primitives/transaction.h>
@@ -218,6 +219,9 @@ static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32;
static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
+extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it.
+extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it.
+
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
@@ -247,11 +251,21 @@ public:
virtual ~BaseSignatureChecker() {}
};
+/** Enum to specify what *TransactionSignatureChecker's behavior should be
+ * when dealing with missing transaction data.
+ */
+enum class MissingDataBehavior
+{
+ ASSERT_FAIL, //!< Abort execution through assertion failure (for consensus code)
+ FAIL, //!< Just act as if the signature was invalid
+};
+
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
private:
const T* txTo;
+ const MissingDataBehavior m_mdb;
unsigned int nIn;
const CAmount amount;
const PrecomputedTransactionData* txdata;
@@ -261,8 +275,8 @@ protected:
virtual bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const;
public:
- GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
- GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
+ GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
+ GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
@@ -272,12 +286,42 @@ public:
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
+class DeferringSignatureChecker : public BaseSignatureChecker
+{
+protected:
+ BaseSignatureChecker& m_checker;
+
+public:
+ DeferringSignatureChecker(BaseSignatureChecker& checker) : m_checker(checker) {}
+
+ bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ {
+ return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion);
+ }
+
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
+ {
+ return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror);
+ }
+
+ bool CheckLockTime(const CScriptNum& nLockTime) const override
+ {
+ return m_checker.CheckLockTime(nLockTime);
+ }
+ bool CheckSequence(const CScriptNum& nSequence) const override
+ {
+ return m_checker.CheckSequence(nSequence);
+ }
+};
+
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
+bool CheckMinimalPush(const std::vector<unsigned char>& data, opcodetype opcode);
+
int FindAndDelete(CScript& script, const CScript& b);
#endif // BITCOIN_SCRIPT_INTERPRETER_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index c6d898a25a..65867c1c14 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -53,14 +53,14 @@ public:
ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
{
CSHA256 hasher = m_salted_hasher_ecdsa;
- hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
+ hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin());
}
void
ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
{
CSHA256 hasher = m_salted_hasher_schnorr;
- hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
+ hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
}
bool
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index bf0ba38c2d..7b6b91c963 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -27,7 +27,7 @@ private:
bool store;
public:
- CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
+ CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn) {}
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index dba5ce621a..da0092f9e3 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -14,7 +14,7 @@
typedef std::vector<unsigned char> valtype;
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL) {}
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
@@ -26,6 +26,9 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
+ // Signing for witness scripts needs the amount.
+ if (sigversion == SigVersion::WITNESS_V0 && amount < 0) return false;
+
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
if (!key.Sign(hash, vchSig))
return false;
@@ -164,7 +167,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
return true;
case TxoutType::WITNESS_V0_SCRIPTHASH:
- CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
+ CRIPEMD160().Write(vSolutions[0].data(), vSolutions[0].size()).Finalize(h160.begin());
if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
@@ -250,17 +253,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
}
namespace {
-class SignatureExtractorChecker final : public BaseSignatureChecker
+class SignatureExtractorChecker final : public DeferringSignatureChecker
{
private:
SignatureData& sigdata;
- BaseSignatureChecker& checker;
public:
- SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
+ SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : DeferringSignatureChecker(checker), sigdata(sigdata) {}
+
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
{
- if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) {
+ if (m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) {
CPubKey pubkey(vchPubKey);
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
return true;
@@ -292,7 +295,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
Stacks stack(data);
// Get signatures
- MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
+ MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue, MissingDataBehavior::FAIL);
SignatureExtractorChecker extractor_checker(data, tx_checker);
if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
data.complete = true;
@@ -499,7 +502,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
}
ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 700155c8d4..a4b11cc0a9 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -6,8 +6,11 @@
#include <script/standard.h>
#include <crypto/sha256.h>
+#include <hash.h>
#include <pubkey.h>
+#include <script/interpreter.h>
#include <script/script.h>
+#include <util/strencodings.h>
#include <string>
@@ -88,21 +91,53 @@ static constexpr bool IsSmallInteger(opcodetype opcode)
return opcode >= OP_1 && opcode <= OP_16;
}
-static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
+static constexpr bool IsPushdataOp(opcodetype opcode)
+{
+ return opcode > OP_FALSE && opcode <= OP_PUSHDATA4;
+}
+
+static constexpr bool IsValidMultisigKeyCount(int n_keys)
+{
+ return n_keys > 0 && n_keys <= MAX_PUBKEYS_PER_MULTISIG;
+}
+
+static bool GetMultisigKeyCount(opcodetype opcode, valtype data, int& count)
+{
+ if (IsSmallInteger(opcode)) {
+ count = CScript::DecodeOP_N(opcode);
+ return IsValidMultisigKeyCount(count);
+ }
+
+ if (IsPushdataOp(opcode)) {
+ if (!CheckMinimalPush(data, opcode)) return false;
+ try {
+ count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
+ return IsValidMultisigKeyCount(count);
+ } catch (const scriptnum_error&) {
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
+ int num_keys;
+
CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
- if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false;
- required = CScript::DecodeOP_N(opcode);
+ if (!script.GetOp(it, opcode, data) || !GetMultisigKeyCount(opcode, data, required_sigs)) return false;
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
pubkeys.emplace_back(std::move(data));
}
- if (!IsSmallInteger(opcode)) return false;
- unsigned int keys = CScript::DecodeOP_N(opcode);
- if (pubkeys.size() != keys || keys < required) return false;
+ if (!GetMultisigKeyCount(opcode, data, num_keys)) return false;
+
+ if (pubkeys.size() != static_cast<unsigned long>(num_keys) || num_keys < required_sigs) return false;
+
return (it + 1 == script.end());
}
@@ -123,15 +158,14 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
- vSolutionsRet.push_back(witnessprogram);
+ vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V0_KEYHASH;
}
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
- vSolutionsRet.push_back(witnessprogram);
+ vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V0_SCRIPTHASH;
}
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
- vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V1_TAPROOT;
}
@@ -163,12 +197,12 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
return TxoutType::PUBKEYHASH;
}
- unsigned int required;
+ int required;
std::vector<std::vector<unsigned char>> keys;
if (MatchMultisig(scriptPubKey, required, keys)) {
- vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
+ vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
- vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
+ vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
return TxoutType::MULTISIG;
}
@@ -210,8 +244,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = hash;
return true;
}
- case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT: {
+ WitnessV1Taproot tap;
+ std::copy(vSolutions[0].begin(), vSolutions[0].end(), tap.begin());
+ addressRet = tap;
+ return true;
+ }
+ case TxoutType::WITNESS_UNKNOWN: {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -297,6 +336,11 @@ public:
return CScript() << OP_0 << ToByteVector(id);
}
+ CScript operator()(const WitnessV1Taproot& tap) const
+ {
+ return CScript() << OP_1 << ToByteVector(tap);
+ }
+
CScript operator()(const WitnessUnknown& id) const
{
return CScript() << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
@@ -318,13 +362,110 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
{
CScript script;
- script << CScript::EncodeOP_N(nRequired);
+ script << nRequired;
for (const CPubKey& key : keys)
script << ToByteVector(key);
- script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
+ script << keys.size() << OP_CHECKMULTISIG;
+
return script;
}
bool IsValidDestination(const CTxDestination& dest) {
return dest.index() != 0;
}
+
+/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b)
+{
+ NodeInfo ret;
+ /* Lexicographically sort a and b's hash, and compute parent hash. */
+ if (a.hash < b.hash) {
+ ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
+ } else {
+ ret.hash = (CHashWriter(HASHER_TAPBRANCH) << b.hash << a.hash).GetSHA256();
+ }
+ return ret;
+}
+
+void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
+{
+ assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT);
+ /* We cannot insert a leaf at a lower depth while a deeper branch is unfinished. Doing
+ * so would mean the Add() invocations do not correspond to a DFS traversal of a
+ * binary tree. */
+ if ((size_t)depth + 1 < m_branch.size()) {
+ m_valid = false;
+ return;
+ }
+ /* As long as an entry in the branch exists at the specified depth, combine it and propagate up.
+ * The 'node' variable is overwritten here with the newly combined node. */
+ while (m_valid && m_branch.size() > (size_t)depth && m_branch[depth].has_value()) {
+ node = Combine(std::move(node), std::move(*m_branch[depth]));
+ m_branch.pop_back();
+ if (depth == 0) m_valid = false; /* Can't propagate further up than the root */
+ --depth;
+ }
+ if (m_valid) {
+ /* Make sure the branch is big enough to place the new node. */
+ if (m_branch.size() <= (size_t)depth) m_branch.resize((size_t)depth + 1);
+ assert(!m_branch[depth].has_value());
+ m_branch[depth] = std::move(node);
+ }
+}
+
+/*static*/ bool TaprootBuilder::ValidDepths(const std::vector<int>& depths)
+{
+ std::vector<bool> branch;
+ for (int depth : depths) {
+ // This inner loop corresponds to effectively the same logic on branch
+ // as what Insert() performs on the m_branch variable. Instead of
+ // storing a NodeInfo object, just remember whether or not there is one
+ // at that depth.
+ if (depth < 0 || (size_t)depth > TAPROOT_CONTROL_MAX_NODE_COUNT) return false;
+ if ((size_t)depth + 1 < branch.size()) return false;
+ while (branch.size() > (size_t)depth && branch[depth]) {
+ branch.pop_back();
+ if (depth == 0) return false;
+ --depth;
+ }
+ if (branch.size() <= (size_t)depth) branch.resize((size_t)depth + 1);
+ assert(!branch[depth]);
+ branch[depth] = true;
+ }
+ // And this check corresponds to the IsComplete() check on m_branch.
+ return branch.size() == 0 || (branch.size() == 1 && branch[0]);
+}
+
+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
+{
+ assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
+ if (!IsValid()) return *this;
+ /* Construct NodeInfo object with leaf hash. */
+ NodeInfo node;
+ node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ /* Insert into the branch. */
+ Insert(std::move(node), depth);
+ return *this;
+}
+
+TaprootBuilder& TaprootBuilder::AddOmitted(int depth, const uint256& hash)
+{
+ if (!IsValid()) return *this;
+ /* Construct NodeInfo object with the hash directly, and insert it into the branch. */
+ NodeInfo node;
+ node.hash = hash;
+ Insert(std::move(node), depth);
+ return *this;
+}
+
+TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key)
+{
+ /* Can only call this function when IsComplete() is true. */
+ assert(IsComplete());
+ m_internal_key = internal_key;
+ auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash);
+ assert(ret.has_value());
+ std::tie(m_output_key, std::ignore) = *ret;
+ return *this;
+}
+
+WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; }
diff --git a/src/script/standard.h b/src/script/standard.h
index f2bf4a8af3..d7ea5cef27 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,8 +6,10 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
+#include <pubkey.h>
#include <script/interpreter.h>
#include <uint256.h>
+#include <util/hash_type.h>
#include <string>
#include <variant>
@@ -18,70 +20,6 @@ class CKeyID;
class CScript;
struct ScriptHash;
-template<typename HashType>
-class BaseHash
-{
-protected:
- HashType m_hash;
-
-public:
- BaseHash() : m_hash() {}
- explicit BaseHash(const HashType& in) : m_hash(in) {}
-
- unsigned char* begin()
- {
- return m_hash.begin();
- }
-
- const unsigned char* begin() const
- {
- return m_hash.begin();
- }
-
- unsigned char* end()
- {
- return m_hash.end();
- }
-
- const unsigned char* end() const
- {
- return m_hash.end();
- }
-
- operator std::vector<unsigned char>() const
- {
- return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
- }
-
- std::string ToString() const
- {
- return m_hash.ToString();
- }
-
- bool operator==(const BaseHash<HashType>& other) const noexcept
- {
- return m_hash == other.m_hash;
- }
-
- bool operator!=(const BaseHash<HashType>& other) const noexcept
- {
- return !(m_hash == other.m_hash);
- }
-
- bool operator<(const BaseHash<HashType>& other) const noexcept
- {
- return m_hash < other.m_hash;
- }
-
- size_t size() const
- {
- return m_hash.size();
- }
-
- unsigned char* data() { return m_hash.data(); }
- const unsigned char* data() const { return m_hash.data(); }
-};
-
/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
class CScriptID : public BaseHash<uint160>
{
@@ -176,6 +114,12 @@ struct WitnessV0KeyHash : public BaseHash<uint160>
};
CKeyID ToKeyID(const WitnessV0KeyHash& key_hash);
+struct WitnessV1Taproot : public XOnlyPubKey
+{
+ WitnessV1Taproot() : XOnlyPubKey() {}
+ explicit WitnessV1Taproot(const XOnlyPubKey& xpk) : XOnlyPubKey(xpk) {}
+};
+
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
{
@@ -205,11 +149,11 @@ struct WitnessUnknown
* * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
- * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
- * (taproot outputs do not require their own type as long as no wallet support exists)
+ * * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR)
+ * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
-using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>;
+using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@@ -265,4 +209,82 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
+/** Utility class to construct Taproot outputs from internal key and script tree. */
+class TaprootBuilder
+{
+private:
+ /** Information associated with a node in the Merkle tree. */
+ struct NodeInfo
+ {
+ /** Merkle hash of this node. */
+ uint256 hash;
+ };
+ /** Whether the builder is in a valid state so far. */
+ bool m_valid = true;
+
+ /** The current state of the builder.
+ *
+ * For each level in the tree, one NodeInfo object may be present. m_branch[0]
+ * is information about the root; further values are for deeper subtrees being
+ * explored.
+ *
+ * For every right branch taken to reach the position we're currently
+ * working in, there will be a (non-nullopt) entry in m_branch corresponding
+ * to the left branch at that level.
+ *
+ * For example, imagine this tree: - N0 -
+ * / \
+ * N1 N2
+ * / \ / \
+ * A B C N3
+ * / \
+ * D E
+ *
+ * Initially, m_branch is empty. After processing leaf A, it would become
+ * {nullopt, nullopt, A}. When processing leaf B, an entry at level 2 already
+ * exists, and it would thus be combined with it to produce a level 1 one,
+ * resulting in {nullopt, N1}. Adding C and D takes us to {nullopt, N1, C}
+ * and {nullopt, N1, C, D} respectively. When E is processed, it is combined
+ * with D, and then C, and then N1, to produce the root, resulting in {N0}.
+ *
+ * This structure allows processing with just O(log n) overhead if the leaves
+ * are computed on the fly.
+ *
+ * As an invariant, there can never be nullopt entries at the end. There can
+ * also not be more than 128 entries (as that would mean more than 128 levels
+ * in the tree). The depth of newly added entries will always be at least
+ * equal to the current size of m_branch (otherwise it does not correspond
+ * to a depth-first traversal of a tree). m_branch is only empty if no entries
+ * have ever be processed. m_branch having length 1 corresponds to being done.
+ */
+ std::vector<std::optional<NodeInfo>> m_branch;
+
+ XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing.
+ XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
+
+ /** Combine information about a parent Merkle tree node from its child nodes. */
+ static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b);
+ /** Insert information about a node at a certain depth, and propagate information up. */
+ void Insert(NodeInfo&& node, int depth);
+
+public:
+ /** Add a new script at a certain depth in the tree. Add() operations must be called
+ * in depth-first traversal order of binary tree. */
+ TaprootBuilder& Add(int depth, const CScript& script, int leaf_version);
+ /** Like Add(), but for a Merkle node with a given hash to the tree. */
+ TaprootBuilder& AddOmitted(int depth, const uint256& hash);
+ /** Finalize the construction. Can only be called when IsComplete() is true.
+ internal_key.IsFullyValid() must be true. */
+ TaprootBuilder& Finalize(const XOnlyPubKey& internal_key);
+
+ /** Return true if so far all input was valid. */
+ bool IsValid() const { return m_valid; }
+ /** Return whether there were either no leaves, or the leaves form a Huffman tree. */
+ bool IsComplete() const { return m_valid && (m_branch.size() == 0 || (m_branch.size() == 1 && m_branch[0].has_value())); }
+ /** Compute scriptPubKey (after Finalize()). */
+ WitnessV1Taproot GetOutput();
+ /** Check if a list of depths is legal (will lead to IsComplete()). */
+ static bool ValidDepths(const std::vector<int>& depths);
+};
+
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/serialize.h b/src/serialize.h
index d9ca984f9c..edf10440c6 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -122,34 +122,6 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
s.read((char*)&obj, 8);
return le64toh(obj);
}
-inline uint64_t ser_double_to_uint64(double x)
-{
- uint64_t tmp;
- std::memcpy(&tmp, &x, sizeof(x));
- static_assert(sizeof(tmp) == sizeof(x), "double and uint64_t assumed to have the same size");
- return tmp;
-}
-inline uint32_t ser_float_to_uint32(float x)
-{
- uint32_t tmp;
- std::memcpy(&tmp, &x, sizeof(x));
- static_assert(sizeof(tmp) == sizeof(x), "float and uint32_t assumed to have the same size");
- return tmp;
-}
-inline double ser_uint64_to_double(uint64_t y)
-{
- double tmp;
- std::memcpy(&tmp, &y, sizeof(y));
- static_assert(sizeof(tmp) == sizeof(y), "double and uint64_t assumed to have the same size");
- return tmp;
-}
-inline float ser_uint32_to_float(uint32_t y)
-{
- float tmp;
- std::memcpy(&tmp, &y, sizeof(y));
- static_assert(sizeof(tmp) == sizeof(y), "float and uint32_t assumed to have the same size");
- return tmp;
-}
/////////////////////////////////////////////////////////////////
@@ -234,8 +206,6 @@ template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_wri
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
-template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); }
-template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); }
template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); }
template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); }
template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
@@ -252,18 +222,12 @@ template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a =
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
-template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); }
-template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); }
template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); }
template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); }
template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); }
-template<typename Stream> inline void Serialize(Stream& s, bool a) { char f=a; ser_writedata8(s, f); }
-template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; }
-
-
-
-
+template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); }
+template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; }
/**
diff --git a/src/shutdown.cpp b/src/shutdown.cpp
index 2fc195e2d1..35faf3c412 100644
--- a/src/shutdown.cpp
+++ b/src/shutdown.cpp
@@ -6,7 +6,9 @@
#include <shutdown.h>
#include <logging.h>
+#include <node/ui_interface.h>
#include <util/tokenpipe.h>
+#include <warnings.h>
#include <config/bitcoin-config.h>
@@ -16,6 +18,18 @@
#include <condition_variable>
#endif
+bool AbortNode(const std::string& strMessage, bilingual_str user_message)
+{
+ SetMiscWarning(Untranslated(strMessage));
+ LogPrintf("*** %s\n", strMessage);
+ if (user_message.empty()) {
+ user_message = _("A fatal internal error occurred, see debug.log for details");
+ }
+ AbortError(user_message);
+ StartShutdown();
+ return false;
+}
+
static std::atomic<bool> fRequestShutdown(false);
#ifdef WIN32
/** On windows it is possible to simply use a condition variable. */
diff --git a/src/shutdown.h b/src/shutdown.h
index b2fbdb8cfb..ff56c6bd87 100644
--- a/src/shutdown.h
+++ b/src/shutdown.h
@@ -6,6 +6,11 @@
#ifndef BITCOIN_SHUTDOWN_H
#define BITCOIN_SHUTDOWN_H
+#include <util/translation.h> // For bilingual_str
+
+/** Abort with a message */
+bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{});
+
/** Initialize shutdown state. This must be called before using either StartShutdown(),
* AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe.
*/
diff --git a/src/signet.cpp b/src/signet.cpp
index fb41e43752..1ba8502287 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -139,7 +139,9 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
const CScript& scriptSig = signet_txs->m_to_sign.vin[0].scriptSig;
const CScriptWitness& witness = signet_txs->m_to_sign.vin[0].scriptWitness;
- TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs->m_to_spend.vout[0].nValue);
+ PrecomputedTransactionData txdata;
+ txdata.Init(signet_txs->m_to_sign, {signet_txs->m_to_spend.vout[0]});
+ TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
if (!VerifyScript(scriptSig, signet_txs->m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
diff --git a/src/streams.h b/src/streams.h
index e78da31cbc..31407287ae 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -167,7 +167,7 @@ public:
}
template<typename T>
- VectorReader& operator>>(T& obj)
+ VectorReader& operator>>(T&& obj)
{
// Unserialize from this stream
::Unserialize(*this, obj);
diff --git a/src/sync.h b/src/sync.h
index 53213c2089..146c228592 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -56,7 +56,7 @@ std::string LocksHeld();
template <typename MutexType>
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
template <typename MutexType>
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs);
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
void DeleteLock(void* cs);
bool LockStackEmpty();
@@ -74,7 +74,7 @@ inline void CheckLastCritical(void* cs, std::string& lockname, const char* guard
template <typename MutexType>
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
template <typename MutexType>
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) {}
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index d438537606..49b40924e0 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -12,6 +12,7 @@
#include <boost/test/unit_test.hpp>
+#include <optional>
#include <string>
class CAddrManTest : public CAddrMan
@@ -392,7 +393,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
// Test: Sanity check, GetAddr should never return anything if addrman
// is empty.
BOOST_CHECK_EQUAL(addrman.size(), 0U);
- std::vector<CAddress> vAddr1 = addrman.GetAddr(/* max_addresses */ 0, /* max_pct */0);
+ std::vector<CAddress> vAddr1 = addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt);
BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
@@ -415,15 +416,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
BOOST_CHECK(addrman.Add(addr4, source2));
BOOST_CHECK(addrman.Add(addr5, source1));
- BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0).size(), 5U);
+ BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt).size(), 5U);
// Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down.
- BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23).size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt).size(), 1U);
// Test: Ensure GetAddr works with new and tried addresses.
addrman.Good(CAddress(addr1, NODE_NONE));
addrman.Good(CAddress(addr2, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0).size(), 5U);
- BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23).size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0, /* network */ std::nullopt).size(), 5U);
+ BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt).size(), 1U);
// Test: Ensure GetAddr still returns 23% when addrman has many addrs.
for (unsigned int i = 1; i < (8 * 256); i++) {
@@ -438,7 +439,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
if (i % 8 == 0)
addrman.Good(addr);
}
- std::vector<CAddress> vAddr = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23);
+ std::vector<CAddress> vAddr = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt);
size_t percent23 = (addrman.size() * 23) / 100;
BOOST_CHECK_EQUAL(vAddr.size(), percent23);
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index b523173a45..3779e7b964 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,15 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <support/lockedpool.h>
#include <util/system.h>
-#include <test/util/setup_common.h>
-
+#include <limits>
#include <memory>
+#include <stdexcept>
+#include <utility>
+#include <vector>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(allocator_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(allocator_tests)
BOOST_AUTO_TEST_CASE(arena_tests)
{
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 1a39498899..77b7758a17 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -4,11 +4,12 @@
#include <amount.h>
#include <policy/feerate.h>
-#include <test/util/setup_common.h>
+
+#include <limits>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(amount_tests)
BOOST_AUTO_TEST_CASE(MoneyRangeTest)
{
@@ -83,7 +84,7 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK(CFeeRate(CAmount(26), 789) == CFeeRate(32));
BOOST_CHECK(CFeeRate(CAmount(27), 789) == CFeeRate(34));
// Maximum size in bytes, should not crash
- CFeeRate(MAX_MONEY, std::numeric_limits<size_t>::max() >> 1).GetFeePerK();
+ CFeeRate(MAX_MONEY, std::numeric_limits<uint32_t>::max()).GetFeePerK();
}
BOOST_AUTO_TEST_CASE(BinaryOperatorTest)
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index a135c93786..a00888aae6 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -3,19 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <arith_uint256.h>
-#include <test/util/setup_common.h>
#include <uint256.h>
#include <boost/test/unit_test.hpp>
#include <cmath>
+#include <cstdint>
#include <iomanip>
#include <limits>
#include <sstream>
-#include <stdint.h>
#include <string>
+#include <vector>
-BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(arith_uint256_tests)
/// Convert vector to arith_uint256, via uint256 blob
static inline arith_uint256 arith_uint256V(const std::vector<unsigned char>& vch)
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 3b44564ddb..22853555e2 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_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 <test/util/setup_common.h>
#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
@@ -10,7 +9,7 @@
using namespace std::literals;
-BOOST_FIXTURE_TEST_SUITE(base32_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(base32_tests)
BOOST_AUTO_TEST_CASE(base32_testvectors)
{
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index dd760fe999..a090cb340b 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -16,7 +16,7 @@
using namespace std::literals;
-extern UniValue read_json(const std::string& jsondata);
+UniValue read_json(const std::string& jsondata);
BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup)
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index 714fccffaa..9d1dfd46f1 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_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 <test/util/setup_common.h>
#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
@@ -10,7 +9,7 @@
using namespace std::literals;
-BOOST_FIXTURE_TEST_SUITE(base64_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(base64_tests)
BOOST_AUTO_TEST_CASE(base64_testvectors)
{
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index 2651e46430..c0344b3cbb 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -3,12 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
-#include <test/util/setup_common.h>
#include <test/util/str.h>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
+#include <string>
+
+BOOST_AUTO_TEST_SUITE(bech32_tests)
BOOST_AUTO_TEST_CASE(bech32_testvectors_valid)
{
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 32329eb510..fb16c92647 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2020 The Bitcoin Core developers
+// Copyright (c) 2013-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.
@@ -87,6 +87,18 @@ TestVector test3 =
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
0);
+TestVector test4 =
+ TestVector("3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678")
+ ("xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa",
+ "xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv",
+ 0x80000000)
+ ("xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m",
+ "xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G",
+ 0x80000001)
+ ("xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt",
+ "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1",
+ 0);
+
static void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
@@ -135,4 +147,8 @@ BOOST_AUTO_TEST_CASE(bip32_test3) {
RunTest(test3);
}
+BOOST_AUTO_TEST_CASE(bip32_test4) {
+ RunTest(test4);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 9903ba75cb..2f532ef598 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// BlockUntilSyncedToCurrentChain should return false before index is started.
BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
- filter_index.Start();
+ BOOST_REQUIRE(filter_index.Start(::ChainstateActive()));
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 2dbca4e8b6..4e75e74d77 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -3,11 +3,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/byteswap.h>
-#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(bswap_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(bswap_tests)
BOOST_AUTO_TEST_CASE(bswap_tests)
{
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
new file mode 100644
index 0000000000..106fcd2a33
--- /dev/null
+++ b/src/test/coinstatsindex_tests.cpp
@@ -0,0 +1,79 @@
+// 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 <index/coinstatsindex.h>
+#include <test/util/setup_common.h>
+#include <util/time.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+
+
+BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
+
+BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
+{
+ CoinStatsIndex coin_stats_index{1 << 20, true};
+
+ CCoinsStats coin_stats{CoinStatsHashType::MUHASH};
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = ChainActive().Tip();
+ }
+
+ // CoinStatsIndex should not be found before it is started.
+ BOOST_CHECK(!coin_stats_index.LookUpStats(block_index, coin_stats));
+
+ // BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
+ // is started.
+ BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
+
+ BOOST_REQUIRE(coin_stats_index.Start(::ChainstateActive()));
+
+ // Allow the CoinStatsIndex to catch up with the block index that is syncing
+ // in a background thread.
+ const auto timeout = GetTime<std::chrono::seconds>() + 120s;
+ while (!coin_stats_index.BlockUntilSyncedToCurrentChain()) {
+ BOOST_REQUIRE(timeout > GetTime<std::chrono::milliseconds>());
+ UninterruptibleSleep(100ms);
+ }
+
+ // Check that CoinStatsIndex works for genesis block.
+ const CBlockIndex* genesis_block_index;
+ {
+ LOCK(cs_main);
+ genesis_block_index = ChainActive().Genesis();
+ }
+ BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
+
+ // Check that CoinStatsIndex updates with new blocks.
+ coin_stats_index.LookUpStats(block_index, coin_stats);
+
+ const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
+ std::vector<CMutableTransaction> noTxns;
+ CreateAndProcessBlock(noTxns, script_pub_key);
+
+ // Let the CoinStatsIndex to catch up again.
+ BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain());
+
+ CCoinsStats new_coin_stats{CoinStatsHashType::MUHASH};
+ const CBlockIndex* new_block_index;
+ {
+ LOCK(cs_main);
+ new_block_index = ChainActive().Tip();
+ }
+ coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
+
+ BOOST_CHECK(block_index != new_block_index);
+
+ // Shutdown sequence (c.f. Shutdown() in init.cpp)
+ coin_stats_index.Stop();
+
+ // Rest of shutdown sequence and destructors happen in ~TestingSetup()
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/compilerbug_tests.cpp b/src/test/compilerbug_tests.cpp
index b68bc279e1..a9cec624ae 100644
--- a/src/test/compilerbug_tests.cpp
+++ b/src/test/compilerbug_tests.cpp
@@ -3,9 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
-#include <test/util/setup_common.h>
-BOOST_FIXTURE_TEST_SUITE(compilerbug_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(compilerbug_tests)
#if defined(__GNUC__)
// This block will also be built under clang, which is fine (as it supports noinline)
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index 4ddbc8338e..b7ba46bd30 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -72,14 +72,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_ckey_id)
CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK_EQUAL(script.size(), 25U);
- std::vector<unsigned char> out;
+ CompressedScript out;
bool done = CompressScript(script, out);
BOOST_CHECK_EQUAL(done, true);
// Check compressed script
BOOST_CHECK_EQUAL(out.size(), 21U);
BOOST_CHECK_EQUAL(out[0], 0x00);
- BOOST_CHECK_EQUAL(memcmp(&out[1], &script[3], 20), 0); // compare the 20 relevant chars of the CKeyId in the script
+ BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 3, 20), 0); // compare the 20 relevant chars of the CKeyId in the script
}
BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id)
@@ -89,14 +89,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id)
script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK_EQUAL(script.size(), 23U);
- std::vector<unsigned char> out;
+ CompressedScript out;
bool done = CompressScript(script, out);
BOOST_CHECK_EQUAL(done, true);
// Check compressed script
BOOST_CHECK_EQUAL(out.size(), 21U);
BOOST_CHECK_EQUAL(out[0], 0x01);
- BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 20), 0); // compare the 20 relevant chars of the CScriptId in the script
+ BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 20), 0); // compare the 20 relevant chars of the CScriptId in the script
}
BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id)
@@ -107,14 +107,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id)
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33)
BOOST_CHECK_EQUAL(script.size(), 35U);
- std::vector<unsigned char> out;
+ CompressedScript out;
bool done = CompressScript(script, out);
BOOST_CHECK_EQUAL(done, true);
// Check compressed script
BOOST_CHECK_EQUAL(out.size(), 33U);
- BOOST_CHECK_EQUAL(memcmp(&out[0], &script[1], 1), 0);
- BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 32), 0); // compare the 32 chars of the compressed CPubKey
+ BOOST_CHECK_EQUAL(memcmp(out.data(), script.data() + 1, 1), 0);
+ BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 32), 0); // compare the 32 chars of the compressed CPubKey
}
BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
@@ -124,13 +124,13 @@ BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65)
BOOST_CHECK_EQUAL(script.size(), 67U); // 1 char code + 65 char pubkey + OP_CHECKSIG
- std::vector<unsigned char> out;
+ CompressedScript out;
bool done = CompressScript(script, out);
BOOST_CHECK_EQUAL(done, true);
// Check compressed script
BOOST_CHECK_EQUAL(out.size(), 33U);
- BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 32), 0); // first 32 chars of CPubKey are copied into out[1:]
+ BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 32), 0); // first 32 chars of CPubKey are copied into out[1:]
BOOST_CHECK_EQUAL(out[0], 0x04 | (script[65] & 0x01)); // least significant bit (lsb) of last char of pubkey is mapped into out[0]
}
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 7358b246b6..edec5f0a31 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -33,7 +33,7 @@ static void TestVector(const Hasher &h, const In &in, const Out &out) {
hash.resize(out.size());
{
// Test that writing the whole input string at once works.
- Hasher(h).Write((unsigned char*)&in[0], in.size()).Finalize(&hash[0]);
+ Hasher(h).Write((const uint8_t*)in.data(), in.size()).Finalize(hash.data());
BOOST_CHECK(hash == out);
}
for (int i=0; i<32; i++) {
@@ -42,15 +42,15 @@ static void TestVector(const Hasher &h, const In &in, const Out &out) {
size_t pos = 0;
while (pos < in.size()) {
size_t len = InsecureRandRange((in.size() - pos + 1) / 2 + 1);
- hasher.Write((unsigned char*)&in[pos], len);
+ hasher.Write((const uint8_t*)in.data() + pos, len);
pos += len;
if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) {
// Test that writing the rest at once to a copy of a hasher works.
- Hasher(hasher).Write((unsigned char*)&in[pos], in.size() - pos).Finalize(&hash[0]);
+ Hasher(hasher).Write((const uint8_t*)in.data() + pos, in.size() - pos).Finalize(hash.data());
BOOST_CHECK(hash == out);
}
}
- hasher.Finalize(&hash[0]);
+ hasher.Finalize(hash.data());
BOOST_CHECK(hash == out);
}
}
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index c394356798..a47bc8f366 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -124,15 +124,15 @@
["CHECKLOCKTIMEVERIFY tests"],
["By-height locks, with argument just beyond tx nLockTime"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKLOCKTIMEVERIFY"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "CHECKLOCKTIMEVERIFY"],
["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 CHECKLOCKTIMEVERIFY"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "CHECKLOCKTIMEVERIFY"],
["Argument missing"],
@@ -142,11 +142,11 @@
"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Argument negative with by-blockheight nLockTime=0"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
["Argument negative with by-blocktime nLockTime=500,000,000"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "CHECKLOCKTIMEVERIFY"],
@@ -167,19 +167,19 @@
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "NONE"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "CHECKLOCKTIMEVERIFY"],
["Argument 2^32 with nLockTime=2^32-1"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000000001 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000000001 CHECKLOCKTIMEVERIFY"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "CHECKLOCKTIMEVERIFY"],
["Same, but with nLockTime=2^31-1"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKLOCKTIMEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKLOCKTIMEVERIFY"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "CHECKLOCKTIMEVERIFY"],
["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"],
@@ -201,15 +201,15 @@
["CHECKSEQUENCEVERIFY tests"],
["By-height locks, with argument just beyond txin.nSequence"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["By-time locks, with argument just beyond txin.nSequence (but within numerical boundaries)"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument missing"],
@@ -217,21 +217,21 @@
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument negative with by-blockheight txin.nSequence=0"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument negative with by-blocktime txin.nSequence=CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Argument/tx height/time mismatch, both versions"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]],
"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"],
@@ -249,7 +249,7 @@
["Failure due to insufficient tx.nVersion (<2)"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY 1"]],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"],
["Unknown witness program version (with DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"],
@@ -311,6 +311,10 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x34b6c399093e06cf9f0f7f660a1abcfe78fcf7b576f43993208edd9518a0ae9b", 1000]],
"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015101045102010100000000", "P2SH,WITNESS"],
+["P2WSH with an empty redeem should fail due to empty stack"],
+[[["3d4da21b04a67a54c8a58df1c53a0534b0a7f0864fb3d19abd43b8f6934e785f", 0, "0x00 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 1337]],
+"020000000001015f784e93f6b843bd9ad1b34f86f0a7b034053ac5f18da5c8547aa6041ba24d3d0000000000ffffffff013905000000000000220020e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855010000000000", "P2SH,WITNESS"],
+
["33 bytes push should be considered a witness scriptPubKey"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x21 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff", 1000]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 2727af5abd..b874f6f26c 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -62,10 +62,6 @@
["c76168ef1a272a4f176e55e73157ecfce040cfad16a5272f6296eb7089dca846", 1, "DUP HASH160 0x14 0x34fea2c5a75414fd945273ae2d029ce1f28dafcf EQUALVERIFY CHECKSIG"]],
"010000000390d31c6107013d754529d8818eff285fe40a3e7635f6930fec5d12eb02107a43010000006b483045022100f40815ae3c81a0dd851cc8d376d6fd226c88416671346a9033468cca2cdcc6c202204f764623903e6c4bed1b734b75d82c40f1725e4471a55ad4f51218f86130ac038321033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff3ff04a68e22bdd52e7c8cb848156d2d158bd5515b3c50adabc87d0ca2cd3482d010000006a4730440220598d263c107004008e9e26baa1e770be30fd31ee55ded1898f7c00da05a75977022045536bead322ca246779698b9c3df3003377090f41afeca7fb2ce9e328ec4af2832102b738b531def73020bd637f32935924cc88549c8206976226d968edd3a42fc2d7ffffffff46a8dc8970eb96622f27a516adcf40e0fcec5731e7556e174f2a271aef6861c7010000006b483045022100c5b90a777a9fdc90c208dbef7290d1fc1be651f47151ee4ccff646872a454cf90220640cfbc4550446968fbbe9d12528f3adf7d87b31541569c59e790db8a220482583210391332546e22bbe8fe3af54addfad6f8b83d05fa4f5e047593d4c07ae938795beffffffff028036be26000000001976a914ddfb29efad43a667465ac59ff14dc6442a1adfca88ac3d5cba01000000001976a914b64dde7a505a13ca986c40e86e984a8dc81368b688ac00000000", "NONE"],
-["An invalid P2SH Transaction"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
-"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", "LOW_S"],
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index a6080ad3dd..b6f5938892 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -26,9 +26,9 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
+ fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_obfuscate_true" : "dbwrapper_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
- char key = 'k';
+ uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_basic_data)
{
// Perform tests both obfuscated and non-obfuscated.
for (bool obfuscate : {false, true}) {
- fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false");
+ fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_1_obfuscate_true" : "dbwrapper_1_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), false, true, obfuscate);
uint256 res;
@@ -88,21 +88,21 @@ BOOST_AUTO_TEST_CASE(dbwrapper_basic_data)
BOOST_CHECK_EQUAL(res.ToString(), in_utxo.ToString());
//Simulate last block file number - "l"
- char key_last_blockfile_number = 'l';
+ uint8_t key_last_blockfile_number{'l'};
uint32_t lastblockfilenumber = InsecureRand32();
BOOST_CHECK(dbw.Write(key_last_blockfile_number, lastblockfilenumber));
BOOST_CHECK(dbw.Read(key_last_blockfile_number, res_uint_32));
BOOST_CHECK_EQUAL(lastblockfilenumber, res_uint_32);
//Simulate Is Reindexing - "R"
- char key_IsReindexing = 'R';
+ uint8_t key_IsReindexing{'R'};
bool isInReindexing = InsecureRandBool();
BOOST_CHECK(dbw.Write(key_IsReindexing, isInReindexing));
BOOST_CHECK(dbw.Read(key_IsReindexing, res_bool));
BOOST_CHECK_EQUAL(isInReindexing, res_bool);
//Simulate last block hash up to which UXTO covers - 'B'
- char key_lastblockhash_uxto = 'B';
+ uint8_t key_lastblockhash_uxto{'B'};
uint256 lastblock_hash = InsecureRand256();
BOOST_CHECK(dbw.Write(key_lastblockhash_uxto, lastblock_hash));
BOOST_CHECK(dbw.Read(key_lastblockhash_uxto, res));
@@ -126,14 +126,14 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
+ fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_batch_obfuscate_true" : "dbwrapper_batch_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
- char key = 'i';
+ uint8_t key{'i'};
uint256 in = InsecureRand256();
- char key2 = 'j';
+ uint8_t key2{'j'};
uint256 in2 = InsecureRand256();
- char key3 = 'k';
+ uint8_t key3{'k'};
uint256 in3 = InsecureRand256();
uint256 res;
@@ -162,14 +162,14 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
for (const bool obfuscate : {false, true}) {
- fs::path ph = GetDataDir() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
+ fs::path ph = m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_iterator_obfuscate_true" : "dbwrapper_iterator_obfuscate_false");
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
- char key = 'j';
+ uint8_t key{'j'};
uint256 in = InsecureRand256();
BOOST_CHECK(dbw.Write(key, in));
- char key2 = 'k';
+ uint8_t key2{'k'};
uint256 in2 = InsecureRand256();
BOOST_CHECK(dbw.Write(key2, in2));
@@ -178,7 +178,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Be sure to seek past the obfuscation key (if it exists)
it->Seek(key);
- char key_res;
+ uint8_t key_res;
uint256 val_res;
BOOST_REQUIRE(it->GetKey(key_res));
@@ -202,12 +202,12 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = GetDataDir() / "existing_data_no_obfuscate";
+ fs::path ph = m_args.GetDataDirBase() / "existing_data_no_obfuscate";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
- char key = 'k';
+ uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -243,12 +243,12 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
// We're going to share this fs::path between two wrappers
- fs::path ph = GetDataDir() / "existing_data_reindex";
+ fs::path ph = m_args.GetDataDirBase() / "existing_data_reindex";
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
std::unique_ptr<CDBWrapper> dbw = std::make_unique<CDBWrapper>(ph, (1 << 10), false, false, false);
- char key = 'k';
+ uint8_t key{'k'};
uint256 in = InsecureRand256();
uint256 res;
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- fs::path ph = GetDataDir() / "iterator_ordering";
+ fs::path ph = m_args.GetDataDirBase() / "iterator_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -334,7 +334,7 @@ struct StringContentsSerializer {
void Serialize(Stream& s) const
{
for (size_t i = 0; i < str.size(); i++) {
- s << str[i];
+ s << uint8_t(str[i]);
}
}
@@ -342,7 +342,7 @@ struct StringContentsSerializer {
void Unserialize(Stream& s)
{
str.clear();
- char c = 0;
+ uint8_t c{0};
while (true) {
try {
s >> c;
@@ -358,7 +358,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- fs::path ph = GetDataDir() / "iterator_string_ordering";
+ fs::path ph = m_args.GetDataDirBase() / "iterator_string_ordering";
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(unicodepath)
// On Windows this test will fail if the directory is created using
// the ANSI CreateDirectoryA call and the code page isn't UTF8.
// It will succeed if created with CreateDirectoryW.
- fs::path ph = GetDataDir() / "test_runner_₿_🏃_20191128_104644";
+ fs::path ph = m_args.GetDataDirBase() / "test_runner_₿_🏃_20191128_104644";
CDBWrapper dbw(ph, (1 << 20));
fs::path lockPath = ph / "LOCK";
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 7557d4618a..a56ce51acb 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -22,6 +22,7 @@
#include <test/util/setup_common.h>
+#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
@@ -115,7 +116,6 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1)); // should result in disconnect
}
BOOST_CHECK(dummyNode1.fDisconnect == true);
- SetMockTime(0);
peerLogic->FinalizeNode(dummyNode1);
}
@@ -208,53 +208,101 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
- auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
+ auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
+ CNetAddr tor_netaddr;
+ BOOST_REQUIRE(
+ tor_netaddr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
+ const CService tor_service{tor_netaddr, Params().GetDefaultPort()};
+
+ const std::array<CAddress, 3> addr{CAddress{ip(0xa0b0c001), NODE_NONE},
+ CAddress{ip(0xa0b0c002), NODE_NONE},
+ CAddress{tor_service, NODE_NONE}};
+
+ const CNetAddr other_addr{ip(0xa0b0ff01)}; // Not any of addr[].
+
+ std::array<CNode*, 3> nodes;
+
banman->ClearBanned();
- CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- 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;
- peerLogic->Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
+ nodes[0] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[0], /* nKeyedNetGroupIn */ 0,
+ /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "",
+ ConnectionType::INBOUND, /* inbound_onion */ false};
+ nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
+ peerLogic->InitializeNode(nodes[0]);
+ nodes[0]->fSuccessfullyConnected = true;
+ connman->AddNode(*nodes[0]);
+ peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
- LOCK(dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
+ LOCK(nodes[0]->cs_sendProcessing);
+ BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
}
- BOOST_CHECK(banman->IsDiscouraged(addr1));
- 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, /* nKeyedNetGroupIn */ 1, /* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
- dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
- peerLogic->InitializeNode(&dummyNode2);
- dummyNode2.fSuccessfullyConnected = true;
- peerLogic->Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
+ BOOST_CHECK(banman->IsDiscouraged(addr[0]));
+ BOOST_CHECK(nodes[0]->fDisconnect);
+ BOOST_CHECK(!banman->IsDiscouraged(other_addr)); // Different address, not discouraged
+
+ nodes[1] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[1], /* nKeyedNetGroupIn */ 1,
+ /* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "",
+ ConnectionType::INBOUND, /* inbound_onion */ false};
+ nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
+ peerLogic->InitializeNode(nodes[1]);
+ nodes[1]->fSuccessfullyConnected = true;
+ connman->AddNode(*nodes[1]);
+ peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
- LOCK(dummyNode2.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
+ LOCK(nodes[1]->cs_sendProcessing);
+ BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
}
- BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
- BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
- peerLogic->Misbehaving(dummyNode2.GetId(), 1, /* message */ ""); // 2 reaches discouragement threshold
+ // [0] is still discouraged/disconnected.
+ BOOST_CHECK(banman->IsDiscouraged(addr[0]));
+ BOOST_CHECK(nodes[0]->fDisconnect);
+ // [1] is not discouraged/disconnected yet.
+ BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
+ BOOST_CHECK(!nodes[1]->fDisconnect);
+ peerLogic->Misbehaving(nodes[1]->GetId(), 1, /* message */ ""); // [1] reaches discouragement threshold
{
- LOCK(dummyNode2.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
+ LOCK(nodes[1]->cs_sendProcessing);
+ BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
}
- BOOST_CHECK(banman->IsDiscouraged(addr1)); // Expect both 1 and 2
- BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
-
- peerLogic->FinalizeNode(dummyNode1);
- peerLogic->FinalizeNode(dummyNode2);
+ // Expect both [0] and [1] to be discouraged/disconnected now.
+ BOOST_CHECK(banman->IsDiscouraged(addr[0]));
+ BOOST_CHECK(nodes[0]->fDisconnect);
+ BOOST_CHECK(banman->IsDiscouraged(addr[1]));
+ BOOST_CHECK(nodes[1]->fDisconnect);
+
+ // Make sure non-IP peers are discouraged and disconnected properly.
+
+ nodes[2] = new CNode{id++, NODE_NETWORK, INVALID_SOCKET, addr[2], /* nKeyedNetGroupIn */ 1,
+ /* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "",
+ ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false};
+ nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
+ peerLogic->InitializeNode(nodes[2]);
+ nodes[2]->fSuccessfullyConnected = true;
+ connman->AddNode(*nodes[2]);
+ peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
+ {
+ LOCK(nodes[2]->cs_sendProcessing);
+ BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
+ }
+ BOOST_CHECK(banman->IsDiscouraged(addr[0]));
+ BOOST_CHECK(banman->IsDiscouraged(addr[1]));
+ BOOST_CHECK(banman->IsDiscouraged(addr[2]));
+ BOOST_CHECK(nodes[0]->fDisconnect);
+ BOOST_CHECK(nodes[1]->fDisconnect);
+ BOOST_CHECK(nodes[2]->fDisconnect);
+
+ for (CNode* node : nodes) {
+ peerLogic->FinalizeNode(*node);
+ }
+ connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
const CChainParams& chainparams = Params();
- auto banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index aecf955fee..36e2dac3ff 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <pubkey.h>
#include <script/descriptor.h>
#include <script/sign.h>
#include <script/standard.h>
@@ -24,7 +25,15 @@ void CheckUnparsable(const std::string& prv, const std::string& pub, const std::
auto parse_pub = Parse(pub, keys_pub, error);
BOOST_CHECK_MESSAGE(!parse_priv, prv);
BOOST_CHECK_MESSAGE(!parse_pub, pub);
- BOOST_CHECK(error == expected_error);
+ BOOST_CHECK_EQUAL(error, expected_error);
+}
+
+/** Check that the script is inferred as non-standard */
+void CheckInferRaw(const CScript& script)
+{
+ FlatSigningProvider dummy_provider;
+ std::unique_ptr<Descriptor> desc = InferDescriptor(script, dummy_provider);
+ BOOST_CHECK(desc->ToString().rfind("raw(", 0) == 0);
}
constexpr int DEFAULT = 0;
@@ -351,16 +360,17 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
- CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have 17 keys in multisig; must have between 1 and 16 keys, inclusive"); // Cannot have more than 16 keys in a multisig
-
+ CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "P2SH script is too large, 581 bytes is larger than 520 bytes"); // Cannot have more than 15 keys in a P2SH multisig, or we exceed maximum push size
+ Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32); // In P2WSH we can have up to 20 keys
+Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT); // Even if it's wrapped into P2SH
// Check for invalid nesting of structures
CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key
- CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have combo in non-top level"); // Old must be top level
+ CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have combo() at top level"); // Old must be top level
CheckUnparsable("wsh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2WSH"); // P2WSH needs a script, not a key
- CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Cannot have wpkh within wsh"); // Cannot embed witness inside witness
- CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2WSH
- CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have sh in non-top level"); // Cannot embed P2SH inside P2SH
- CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Cannot have wsh within wsh"); // Cannot embed P2WSH inside P2WSH
+ CheckUnparsable("wsh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have wpkh() at top level or inside sh()"); // Cannot embed witness inside witness
+ CheckUnparsable("wsh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have sh() at top level"); // Cannot embed P2SH inside P2WSH
+ CheckUnparsable("sh(sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have sh() at top level"); // Cannot embed P2SH inside P2SH
+ CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // 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", "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}});
@@ -376,6 +386,27 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckUnparsable("", "addr(asdf)", "Address is not valid"); // Invalid address
CheckUnparsable("", "raw(asdf)", "Raw script is not hex"); // Invalid script
CheckUnparsable("", "raw(Ü)#00000000", "Invalid characters in payload"); // Invalid chars
+
+ // A 2of4 but using a direct push rather than OP_2
+ CScript nonminimalmultisig;
+ CKey keys[4];
+ nonminimalmultisig << std::vector<unsigned char>{2};
+ for (int i = 0; i < 4; i++) {
+ keys[i].MakeNewKey(true);
+ nonminimalmultisig << ToByteVector(keys[i].GetPubKey());
+ }
+ nonminimalmultisig << 4 << OP_CHECKMULTISIG;
+ CheckInferRaw(nonminimalmultisig);
+
+ // A 2of4 but using a direct push rather than OP_4
+ nonminimalmultisig.clear();
+ nonminimalmultisig << 2;
+ for (int i = 0; i < 4; i++) {
+ keys[i].MakeNewKey(true);
+ nonminimalmultisig << ToByteVector(keys[i].GetPubKey());
+ }
+ nonminimalmultisig << std::vector<unsigned char>{4} << OP_CHECKMULTISIG;
+ CheckInferRaw(nonminimalmultisig);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 0c5c19113d..f4bc320f3c 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -14,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(flatfile_filename)
{
- const auto data_dir = GetDataDir();
+ const auto data_dir = m_args.GetDataDirBase();
FlatFilePos pos(456, 789);
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(flatfile_filename)
BOOST_AUTO_TEST_CASE(flatfile_open)
{
- const auto data_dir = GetDataDir();
+ const auto data_dir = m_args.GetDataDirBase();
FlatFileSeq seq(data_dir, "a", 16 * 1024);
std::string line1("A purely peer-to-peer version of electronic cash would allow online "
@@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(flatfile_open)
BOOST_AUTO_TEST_CASE(flatfile_allocate)
{
- const auto data_dir = GetDataDir();
+ const auto data_dir = m_args.GetDataDirBase();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
@@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate)
BOOST_AUTO_TEST_CASE(flatfile_flush)
{
- const auto data_dir = GetDataDir();
+ const auto data_dir = m_args.GetDataDirBase();
FlatFileSeq seq(data_dir, "a", 100);
bool out_of_space;
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index e52cd5230c..526a3c27be 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -13,7 +13,7 @@ BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(fsbridge_fstream)
{
- fs::path tmpfolder = GetDataDir();
+ fs::path tmpfolder = m_args.GetDataDirBase();
// tmpfile1 should be the same as tmpfile2
fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
fs::path tmpfile2 = tmpfolder / "fs_tests_₿_🏃";
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 0baf30aef6..98ae32a8d0 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -60,7 +60,10 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
},
[&] {
- (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ (void)addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
},
[&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 8bf484722c..759a70a857 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -32,13 +32,17 @@ void initialize_banman()
FUZZ_TARGET_INIT(banman, initialize_banman)
{
+ // The complexity is O(N^2), where N is the input size, because each call
+ // might call DumpBanlist (or other methods that are at least linear
+ // complexity of the input size).
+ int limit_max_ops{300};
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat";
+ const fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist.dat";
fs::remove(banlist_file);
{
BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
@@ -52,7 +56,6 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
[&] {
ban_man.ClearBanned();
},
- [] {},
[&] {
ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
},
@@ -72,7 +75,6 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
[&] {
ban_man.DumpBanlist();
},
- [] {},
[&] {
ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
});
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b21d2eae79..b509ee0b26 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -6,6 +6,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <coins.h>
+#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <key.h>
@@ -230,8 +231,14 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
// consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
return;
}
- (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
- assert(MoneyRange(tx_fee_out));
+ TxValidationState dummy;
+ if (!CheckTransaction(transaction, dummy)) {
+ // It is not allowed to call CheckTxInputs if CheckTransaction failed
+ return;
+ }
+ if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
+ assert(MoneyRange(tx_fee_out));
+ }
},
[&] {
const CTransaction transaction{random_mutable_transaction};
@@ -258,10 +265,10 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
(void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
},
[&] {
- CCoinsStats stats;
+ CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index e07f25dedf..bbec5943af 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -65,16 +65,19 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
connman.ForEachNode([](auto) {});
},
[&] {
- connman.ForEachNodeThen([](auto) {}, []() {});
- },
- [&] {
(void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); });
},
[&] {
- (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ (void)connman.GetAddresses(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* network */ std::nullopt);
},
[&] {
- (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ (void)connman.GetAddresses(
+ /* requestor */ random_node,
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegral<size_t>());
},
[&] {
(void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
diff --git a/src/test/fuzz/danger_link_all.sh b/src/test/fuzz/danger_link_all.sh
deleted file mode 100755
index 2ddd00c658..0000000000
--- a/src/test/fuzz/danger_link_all.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# 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.
-
-export LC_ALL=C.UTF-8
-
-set -e
-
-ROOT_DIR="$(git rev-parse --show-toplevel)"
-
-# Run only once (break make recursion)
-if [ -d "${ROOT_DIR}/lock_fuzz_link_all" ]; then
- exit
-fi
-mkdir "${ROOT_DIR}/lock_fuzz_link_all"
-
-echo "Linking each fuzz target separately."
-for FUZZING_HARNESS in $(PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 "${ROOT_DIR}/src/test/fuzz/fuzz" | sort -u); do
- echo "Building src/test/fuzz/${FUZZING_HARNESS} ..."
- git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
- sed -i "s/std::getenv(\"FUZZ\")/\"${FUZZING_HARNESS}\"/g" "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
- make
- mv "${ROOT_DIR}/src/test/fuzz/fuzz" "${ROOT_DIR}/src/test/fuzz/${FUZZING_HARNESS}"
-done
-git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
-rmdir "${ROOT_DIR}/lock_fuzz_link_all"
-echo "Successfully built all fuzz targets."
diff --git a/src/test/fuzz/fee_rate.cpp b/src/test/fuzz/fee_rate.cpp
index 2955213635..dff0e58000 100644
--- a/src/test/fuzz/fee_rate.cpp
+++ b/src/test/fuzz/fee_rate.cpp
@@ -20,8 +20,8 @@ FUZZ_TARGET(fee_rate)
const CFeeRate fee_rate{satoshis_per_k};
(void)fee_rate.GetFeePerK();
- const size_t bytes = fuzzed_data_provider.ConsumeIntegral<size_t>();
- if (!MultiplicationOverflow(static_cast<int64_t>(bytes), satoshis_per_k) && bytes <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ const auto bytes = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ if (!MultiplicationOverflow(int64_t{bytes}, satoshis_per_k)) {
(void)fee_rate.GetFee(bytes);
}
(void)fee_rate.ToString();
diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp
index d18a87d177..adef66a3ee 100644
--- a/src/test/fuzz/float.cpp
+++ b/src/test/fuzz/float.cpp
@@ -3,14 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <memusage.h>
-#include <serialize.h>
-#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <util/serfloat.h>
#include <version.h>
#include <cassert>
-#include <cstdint>
+#include <cmath>
+#include <limits>
FUZZ_TARGET(float)
{
@@ -19,24 +19,17 @@ FUZZ_TARGET(float)
{
const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>();
(void)memusage::DynamicUsage(d);
- assert(ser_uint64_to_double(ser_double_to_uint64(d)) == d);
- CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
- stream << d;
- double d_deserialized;
- stream >> d_deserialized;
- assert(d == d_deserialized);
- }
-
- {
- const float f = fuzzed_data_provider.ConsumeFloatingPoint<float>();
- (void)memusage::DynamicUsage(f);
- assert(ser_uint32_to_float(ser_float_to_uint32(f)) == f);
-
- CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
- stream << f;
- float f_deserialized;
- stream >> f_deserialized;
- assert(f == f_deserialized);
+ uint64_t encoded = EncodeDouble(d);
+ if constexpr (std::numeric_limits<double>::is_iec559) {
+ if (!std::isnan(d)) {
+ uint64_t encoded_in_memory;
+ std::copy((const unsigned char*)&d, (const unsigned char*)(&d + 1), (unsigned char*)&encoded_in_memory);
+ assert(encoded_in_memory == encoded);
+ }
+ }
+ double d_deserialized = DecodeDouble(encoded);
+ assert(std::isnan(d) == std::isnan(d_deserialized));
+ assert(std::isnan(d) || d == d_deserialized);
}
}
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 1fab46ff13..a33297e0ed 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -4,10 +4,16 @@
#include <test/fuzz/fuzz.h>
+#include <netaddress.h>
+#include <netbase.h>
#include <test/util/setup_common.h>
#include <util/check.h>
+#include <util/sock.h>
#include <cstdint>
+#include <exception>
+#include <memory>
+#include <string>
#include <unistd.h>
#include <vector>
@@ -29,13 +35,35 @@ static TypeTestOneInput* g_test_one_input{nullptr};
void initialize()
{
+ // Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
+ CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
+
+ // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
+ g_dns_lookup = [](const std::string& name, bool allow_lookup) {
+ if (allow_lookup) {
+ std::terminate();
+ }
+ return WrappedGetAddrInfo(name, false);
+ };
+
+ bool should_abort{false};
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);
+ should_abort = true;
+ }
+ if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
+ std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
+ std::ofstream out_stream(out_path, std::ios::binary);
+ for (const auto& t : FuzzTargets()) {
+ if (std::get<2>(t.second)) continue;
+ out_stream << t.first << std::endl;
+ }
+ should_abort = true;
}
+ Assert(!should_abort);
std::string_view fuzz_target{Assert(std::getenv("FUZZ"))};
const auto it = FuzzTargets().find(fuzz_target);
Assert(it != FuzzTargets().end());
diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp
index 345d68502a..fb6d23aca5 100644
--- a/src/test/fuzz/i2p.cpp
+++ b/src/test/fuzz/i2p.cpp
@@ -30,14 +30,14 @@ FUZZ_TARGET_INIT(i2p, initialize_i2p)
const CService sam_proxy;
CThreadInterrupt interrupt;
- i2p::sam::Session sess{GetDataDir() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
+ i2p::sam::Session sess{gArgs.GetDataDirNet() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
i2p::Connection conn;
if (sess.Listen(conn)) {
if (sess.Accept(conn)) {
try {
- conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
+ (void)conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
} catch (const std::runtime_error&) {
}
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 5bc99ddcb9..e9fa343896 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -122,10 +122,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
assert(dynamic_usage == incremental_dynamic_usage * i64s.size());
}
(void)MillisToTimeval(i64);
- const double d = ser_uint64_to_double(u64);
- assert(ser_double_to_uint64(d) == u64);
- const float f = ser_uint32_to_float(u32);
- assert(ser_float_to_uint32(f) == u32);
(void)SighashToStr(uch);
(void)SipHashUint256(u64, u64, u256);
(void)SipHashUint256Extra(u64, u64, u256, u32);
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 272f6415a9..20d8581312 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -58,27 +58,6 @@ FUZZ_TARGET_INIT(net, initialize_net)
}
},
[&] {
- if (node.m_addr_known == nullptr) {
- return;
- }
- const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
- if (!addr_opt) {
- return;
- }
- node.AddAddressKnown(*addr_opt);
- },
- [&] {
- if (node.m_addr_known == nullptr) {
- return;
- }
- const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
- if (!addr_opt) {
- return;
- }
- FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
- node.PushAddress(*addr_opt, fast_random_context);
- },
- [&] {
const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
if (!inv_opt) {
return;
@@ -110,7 +89,6 @@ FUZZ_TARGET_INIT(net, initialize_net)
const int ref_count = node.GetRefCount();
assert(ref_count >= 0);
(void)node.GetCommonVersion();
- (void)node.RelayAddrsWithConn();
const NetPermissionFlags net_permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
(void)node.HasPermission(net_permission_flags);
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index 544a33047b..6ea79464d0 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -25,7 +25,7 @@ FUZZ_TARGET(net_permissions)
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags);
assert(NetPermissions::HasFlag(net_whitebind_permissions.m_flags, net_permission_flags));
- (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, net_permission_flags);
+ (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, NetPermissionFlags::Implicit);
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
}
@@ -35,7 +35,7 @@ FUZZ_TARGET(net_permissions)
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags));
- (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, net_permission_flags);
+ (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, NetPermissionFlags::Implicit);
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
}
}
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
deleted file mode 100644
index 3a1fdaad8f..0000000000
--- a/src/test/fuzz/p2p_transport_deserializer.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.
-
-#include <chainparams.h>
-#include <net.h>
-#include <protocol.h>
-#include <test/fuzz/fuzz.h>
-
-#include <cassert>
-#include <cstdint>
-#include <limits>
-#include <optional>
-#include <vector>
-
-void initialize_p2p_transport_deserializer()
-{
- SelectParams(CBaseChainParams::REGTEST);
-}
-
-FUZZ_TARGET_INIT(p2p_transport_deserializer, initialize_p2p_transport_deserializer)
-{
- // Construct deserializer, with a dummy NodeId
- V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION};
- Span<const uint8_t> msg_bytes{buffer};
- while (msg_bytes.size() > 0) {
- const int handled = deserializer.Read(msg_bytes);
- if (handled < 0) {
- break;
- }
- if (deserializer.Complete()) {
- const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
- uint32_t out_err_raw_size{0};
- 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());
- assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size);
- assert(result->m_time == m_time);
- }
- }
- }
-}
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
new file mode 100644
index 0000000000..edee5aeef7
--- /dev/null
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -0,0 +1,85 @@
+// 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.
+
+#include <chainparams.h>
+#include <hash.h>
+#include <net.h>
+#include <netmessagemaker.h>
+#include <protocol.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <vector>
+
+void initialize_p2p_transport_serialization()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serialization)
+{
+ // Construct deserializer, with a dummy NodeId
+ V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION};
+ V1TransportSerializer serializer{};
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ auto checksum_assist = fuzzed_data_provider.ConsumeBool();
+ auto magic_bytes_assist = fuzzed_data_provider.ConsumeBool();
+ std::vector<uint8_t> mutable_msg_bytes;
+
+ auto header_bytes_remaining = CMessageHeader::HEADER_SIZE;
+ if (magic_bytes_assist) {
+ auto msg_start = Params().MessageStart();
+ for (size_t i = 0; i < CMessageHeader::MESSAGE_SIZE_SIZE; ++i) {
+ mutable_msg_bytes.push_back(msg_start[i]);
+ }
+ header_bytes_remaining -= CMessageHeader::MESSAGE_SIZE_SIZE;
+ }
+
+ if (checksum_assist) {
+ header_bytes_remaining -= CMessageHeader::CHECKSUM_SIZE;
+ }
+
+ auto header_random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(header_bytes_remaining);
+ mutable_msg_bytes.insert(mutable_msg_bytes.end(), header_random_bytes.begin(), header_random_bytes.end());
+ auto payload_bytes = fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
+
+ if (checksum_assist && mutable_msg_bytes.size() == CMessageHeader::CHECKSUM_OFFSET) {
+ CHash256 hasher;
+ unsigned char hsh[32];
+ hasher.Write(payload_bytes);
+ hasher.Finalize(hsh);
+ for (size_t i = 0; i < CMessageHeader::CHECKSUM_SIZE; ++i) {
+ mutable_msg_bytes.push_back(hsh[i]);
+ }
+ }
+
+ mutable_msg_bytes.insert(mutable_msg_bytes.end(), payload_bytes.begin(), payload_bytes.end());
+ Span<const uint8_t> msg_bytes{mutable_msg_bytes};
+ while (msg_bytes.size() > 0) {
+ const int handled = deserializer.Read(msg_bytes);
+ if (handled < 0) {
+ break;
+ }
+ if (deserializer.Complete()) {
+ const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
+ uint32_t out_err_raw_size{0};
+ 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 <= mutable_msg_bytes.size());
+ assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size);
+ assert(result->m_time == m_time);
+
+ std::vector<unsigned char> header;
+ auto msg = CNetMsgMaker{result->m_recv.GetVersion()}.Make(result->m_command, MakeUCharSpan(result->m_recv));
+ serializer.prepareForTransport(msg, header);
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/parse_iso8601.cpp b/src/test/fuzz/parse_iso8601.cpp
index dcb24ac127..a56f2aa48d 100644
--- a/src/test/fuzz/parse_iso8601.cpp
+++ b/src/test/fuzz/parse_iso8601.cpp
@@ -15,7 +15,7 @@ FUZZ_TARGET(parse_iso8601)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const int64_t random_time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const int64_t random_time = fuzzed_data_provider.ConsumeIntegral<int32_t>();
const std::string random_string = fuzzed_data_provider.ConsumeRemainingBytesAsString();
const std::string iso8601_datetime = FormatISO8601DateTime(random_time);
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 96e1cfa08f..7b99193ad0 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -100,7 +100,6 @@ void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE
g_setup->m_node.peerman->SendMessages(&p2p_node);
}
SyncWithValidationInterfaceQueue();
- LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 203c0ef8e1..11b236c9bd 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -80,6 +80,5 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
}
}
SyncWithValidationInterfaceQueue();
- LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index d1cc6f9c7e..6c62dd6e48 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <node/psbt.h>
@@ -9,6 +10,7 @@
#include <pubkey.h>
#include <script/script.h>
#include <streams.h>
+#include <util/check.h>
#include <version.h>
#include <cstdint>
@@ -23,10 +25,10 @@ void initialize_psbt()
FUZZ_TARGET_INIT(psbt, initialize_psbt)
{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
PartiallySignedTransaction psbt_mut;
- const std::string raw_psbt{buffer.begin(), buffer.end()};
std::string error;
- if (!DecodeRawPSBT(psbt_mut, raw_psbt, error)) {
+ if (!DecodeRawPSBT(psbt_mut, fuzzed_data_provider.ConsumeRandomLengthString(), error)) {
return;
}
const PartiallySignedTransaction psbt = psbt_mut;
@@ -49,6 +51,7 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt)
(void)PSBTInputSigned(input);
(void)input.IsNull();
}
+ (void)CountPSBTUnsignedInputs(psbt);
for (const PSBTOutput& output : psbt.outputs) {
(void)output.IsNull();
@@ -71,6 +74,20 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt)
const PartiallySignedTransaction psbt_from_tx{result};
}
+ PartiallySignedTransaction psbt_merge;
+ if (!DecodeRawPSBT(psbt_merge, fuzzed_data_provider.ConsumeRandomLengthString(), error)) {
+ psbt_merge = psbt;
+ }
+ psbt_mut = psbt;
+ (void)psbt_mut.Merge(psbt_merge);
+ psbt_mut = psbt;
+ (void)CombinePSBTs(psbt_mut, {psbt_mut, psbt_merge});
psbt_mut = psbt;
- (void)psbt_mut.Merge(psbt);
+ for (unsigned int i = 0; i < psbt_merge.tx->vin.size(); ++i) {
+ (void)psbt_mut.AddInput(psbt_merge.tx->vin[i], psbt_merge.inputs[i]);
+ }
+ for (unsigned int i = 0; i < psbt_merge.tx->vout.size(); ++i) {
+ Assert(psbt_mut.AddOutput(psbt_merge.tx->vout[i], psbt_merge.outputs[i]));
+ }
+ psbt_mut.unknown.insert(psbt_merge.unknown.begin(), psbt_merge.unknown.end());
}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
new file mode 100644
index 0000000000..9195cc4873
--- /dev/null
+++ b/src/test/fuzz/rpc.cpp
@@ -0,0 +1,359 @@
+// 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 <base58.h>
+#include <core_io.h>
+#include <key.h>
+#include <key_io.h>
+#include <node/context.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <psbt.h>
+#include <rpc/blockchain.h>
+#include <rpc/client.h>
+#include <rpc/request.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <span.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+#include <util/time.h>
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace {
+struct RPCFuzzTestingSetup : public TestingSetup {
+ RPCFuzzTestingSetup(const std::string& chain_name, const std::vector<const char*>& extra_args) : TestingSetup{chain_name, extra_args}
+ {
+ }
+
+ UniValue CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
+ {
+ JSONRPCRequest request;
+ request.context = &m_node;
+ request.strMethod = rpc_method;
+ request.params = RPCConvertValues(rpc_method, arguments);
+ return tableRPC.execute(request);
+ }
+
+ std::vector<std::string> GetRPCCommands() const
+ {
+ return tableRPC.listCommands();
+ }
+};
+
+RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
+std::string g_limit_to_rpc_command;
+
+// RPC commands which are not appropriate for fuzzing: such as RPC commands
+// reading or writing to a filename passed as an RPC parameter, RPC commands
+// resulting in network activity, etc.
+const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
+ "addconnection", // avoid DNS lookups
+ "addnode", // avoid DNS lookups
+ "addpeeraddress", // avoid DNS lookups
+ "analyzepsbt", // avoid signed integer overflow in CFeeRate::GetFee(unsigned long) (https://github.com/bitcoin/bitcoin/issues/20607)
+ "dumptxoutset", // avoid writing to disk
+ "dumpwallet", // avoid writing to disk
+ "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
+ "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
+ "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
+ "gettxoutproof", // avoid prohibitively slow execution
+ "importwallet", // avoid reading from disk
+ "loadwallet", // avoid reading from disk
+ "prioritisetransaction", // avoid signed integer overflow in CTxMemPool::PrioritiseTransaction(uint256 const&, long const&) (https://github.com/bitcoin/bitcoin/issues/20626)
+ "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
+ "setban", // avoid DNS lookups
+ "stop", // avoid shutdown state
+};
+
+// RPC commands which are safe for fuzzing.
+const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
+ "clearbanned",
+ "combinepsbt",
+ "combinerawtransaction",
+ "converttopsbt",
+ "createmultisig",
+ "createpsbt",
+ "createrawtransaction",
+ "decodepsbt",
+ "decoderawtransaction",
+ "decodescript",
+ "deriveaddresses",
+ "disconnectnode",
+ "echo",
+ "echojson",
+ "estimaterawfee",
+ "estimatesmartfee",
+ "finalizepsbt",
+ "generate",
+ "generateblock",
+ "getaddednodeinfo",
+ "getbestblockhash",
+ "getblock",
+ "getblockchaininfo",
+ "getblockcount",
+ "getblockfilter",
+ "getblockhash",
+ "getblockheader",
+ "getblockstats",
+ "getblocktemplate",
+ "getchaintips",
+ "getchaintxstats",
+ "getconnectioncount",
+ "getdescriptorinfo",
+ "getdifficulty",
+ "getindexinfo",
+ "getmemoryinfo",
+ "getmempoolancestors",
+ "getmempooldescendants",
+ "getmempoolentry",
+ "getmempoolinfo",
+ "getmininginfo",
+ "getnettotals",
+ "getnetworkhashps",
+ "getnetworkinfo",
+ "getnodeaddresses",
+ "getpeerinfo",
+ "getrawmempool",
+ "getrawtransaction",
+ "getrpcinfo",
+ "gettxout",
+ "gettxoutsetinfo",
+ "help",
+ "invalidateblock",
+ "joinpsbts",
+ "listbanned",
+ "logging",
+ "mockscheduler",
+ "ping",
+ "preciousblock",
+ "pruneblockchain",
+ "reconsiderblock",
+ "scantxoutset",
+ "sendrawtransaction",
+ "setmocktime",
+ "setnetworkactive",
+ "signmessagewithprivkey",
+ "signrawtransactionwithkey",
+ "submitblock",
+ "submitheader",
+ "syncwithvalidationinterfacequeue",
+ "testmempoolaccept",
+ "uptime",
+ "utxoupdatepsbt",
+ "validateaddress",
+ "verifychain",
+ "verifymessage",
+ "verifytxoutproof",
+ "waitforblock",
+ "waitforblockheight",
+ "waitfornewblock",
+};
+
+std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ const size_t max_string_length = 4096;
+ const size_t max_base58_bytes_length{64};
+ std::string r;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ // string argument
+ r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
+ },
+ [&] {
+ // base64 argument
+ r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // hex argument
+ r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // bool argument
+ r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
+ },
+ [&] {
+ // range argument
+ r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
+ },
+ [&] {
+ // integral argument (int64_t)
+ r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
+ },
+ [&] {
+ // integral argument (uint64_t)
+ r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ },
+ [&] {
+ // floating point argument
+ r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
+ },
+ [&] {
+ // tx destination argument
+ r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
+ },
+ [&] {
+ // uint160 argument
+ r = ConsumeUInt160(fuzzed_data_provider).ToString();
+ },
+ [&] {
+ // uint256 argument
+ r = ConsumeUInt256(fuzzed_data_provider).ToString();
+ },
+ [&] {
+ // base32 argument
+ r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // base58 argument
+ r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
+ },
+ [&] {
+ // base58 argument with checksum
+ r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
+ },
+ [&] {
+ // hex encoded block
+ std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
+ if (!opt_block) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_block;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // hex encoded block header
+ std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
+ if (!opt_block_header) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_block_header;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // hex encoded tx
+ std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!opt_tx) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, fuzzed_data_provider.ConsumeBool() ? PROTOCOL_VERSION : (PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)};
+ data_stream << *opt_tx;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // base64 encoded psbt
+ std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
+ if (!opt_psbt) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_psbt;
+ r = EncodeBase64({data_stream.begin(), data_stream.end()});
+ },
+ [&] {
+ // base58 encoded key
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
+ CKey key;
+ key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
+ if (!key.IsValid()) {
+ return;
+ }
+ r = EncodeSecret(key);
+ },
+ [&] {
+ // hex encoded pubkey
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
+ CKey key;
+ key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
+ if (!key.IsValid()) {
+ return;
+ }
+ r = HexStr(key.GetPubKey());
+ });
+ return r;
+}
+
+std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ std::vector<std::string> scalar_arguments;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
+ }
+ return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
+}
+
+std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
+}
+
+RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
+{
+ static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
+ SetRPCWarmupFinished();
+ return setup.get();
+}
+}; // namespace
+
+void initialize_rpc()
+{
+ rpc_testing_setup = InitializeRPCFuzzTestingSetup();
+ const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
+ for (const std::string& rpc_command : supported_rpc_commands) {
+ const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
+ const bool not_safe_for_fuzzing = std::find(RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end();
+ if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
+ std::cerr << "Error: RPC command \"" << rpc_command << "\" not found in RPC_COMMANDS_SAFE_FOR_FUZZING or RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
+ std::terminate();
+ }
+ if (safe_for_fuzzing && not_safe_for_fuzzing) {
+ std::cerr << "Error: RPC command \"" << rpc_command << "\" found in *both* RPC_COMMANDS_SAFE_FOR_FUZZING and RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
+ std::terminate();
+ }
+ }
+ const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
+ if (limit_to_rpc_command_env != nullptr) {
+ g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
+ }
+}
+
+FUZZ_TARGET_INIT(rpc, initialize_rpc)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+ const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
+ if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
+ return;
+ }
+ const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
+ if (!safe_for_fuzzing) {
+ return;
+ }
+ std::vector<std::string> arguments;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
+ }
+ try {
+ rpc_testing_setup->CallRPC(rpc_command, arguments);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index e87ae5b04b..b87bcf2ef5 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -43,7 +43,7 @@ FUZZ_TARGET_INIT(script, initialize_script)
if (!script_opt) return;
const CScript script{*script_opt};
- std::vector<unsigned char> compressed;
+ CompressedScript compressed;
if (CompressScript(script, compressed)) {
const unsigned int size = compressed[0];
compressed.erase(compressed.begin());
@@ -55,22 +55,45 @@ FUZZ_TARGET_INIT(script, initialize_script)
}
CTxDestination address;
- (void)ExtractDestination(script, address);
-
TxoutType type_ret;
std::vector<CTxDestination> addresses;
int required_ret;
- (void)ExtractDestinations(script, type_ret, addresses, required_ret);
-
- const FlatSigningProvider signing_provider;
- (void)InferDescriptor(script, signing_provider);
-
- (void)IsSegWitOutput(signing_provider, script);
-
- (void)IsSolvable(signing_provider, script);
+ bool extract_destinations_ret = ExtractDestinations(script, type_ret, addresses, required_ret);
+ bool extract_destination_ret = ExtractDestination(script, address);
+ if (!extract_destinations_ret) {
+ assert(!extract_destination_ret);
+ if (type_ret == TxoutType::MULTISIG) {
+ assert(addresses.empty() && required_ret == 0);
+ } else {
+ assert(type_ret == TxoutType::PUBKEY ||
+ type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA);
+ }
+ } else {
+ assert(required_ret >= 1 && required_ret <= 16);
+ assert((unsigned long)required_ret == addresses.size());
+ assert(type_ret == TxoutType::MULTISIG || required_ret == 1);
+ }
+ if (type_ret == TxoutType::NONSTANDARD || type_ret == TxoutType::NULL_DATA) {
+ assert(!extract_destinations_ret);
+ }
+ if (!extract_destination_ret) {
+ assert(type_ret == TxoutType::PUBKEY ||
+ type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA ||
+ type_ret == TxoutType::MULTISIG);
+ } else {
+ assert(address == addresses[0]);
+ }
+ if (type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA ||
+ type_ret == TxoutType::MULTISIG) {
+ assert(!extract_destination_ret);
+ }
TxoutType which_type;
bool is_standard_ret = IsStandard(script, which_type);
+ assert(type_ret == which_type);
if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA ||
@@ -87,6 +110,11 @@ FUZZ_TARGET_INIT(script, initialize_script)
which_type == TxoutType::NONSTANDARD);
}
+ const FlatSigningProvider signing_provider;
+ (void)InferDescriptor(script, signing_provider);
+ (void)IsSegWitOutput(signing_provider, script);
+ (void)IsSolvable(signing_provider, script);
+
(void)RecursiveDynamicUsage(script);
std::vector<std::vector<unsigned char>> solutions;
@@ -115,10 +143,12 @@ FUZZ_TARGET_INIT(script, initialize_script)
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ CompressedScript compressed_script;
+ compressed_script.assign(bytes.begin(), bytes.end());
// DecompressScript(..., ..., bytes) is not guaranteed to be defined if the bytes vector is too short
- if (bytes.size() >= 32) {
+ if (compressed_script.size() >= 32) {
CScript decompressed_script;
- DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), bytes);
+ DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), compressed_script);
}
}
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index 5f07acbcc7..a80338b965 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -133,8 +133,7 @@ unsigned int ParseScriptFlags(const std::string& str)
std::vector<std::string> words;
boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
- for (const std::string& word : words)
- {
+ for (const std::string& word : words) {
auto it = FLAG_NAMES.find(word);
if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word);
flags |= it->second;
@@ -161,7 +160,7 @@ void Test(const std::string& str)
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
// a subset of test_flags.
@@ -176,7 +175,7 @@ void Test(const std::string& str)
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
if ((flags & test_flags) == test_flags) {
@@ -186,15 +185,19 @@ void Test(const std::string& str)
}
}
-ECCVerifyHandle handle;
-
-} // namespace
+void test_init()
+{
+ static ECCVerifyHandle handle;
+}
-FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, FuzzFrameworkEmptyInitFun, /* hidden */ true)
+FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, test_init, /* 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);
try {
Test(str);
- } catch (const std::runtime_error&) {}
+ } catch (const std::runtime_error&) {
+ }
}
+
+} // namespace
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index 387f9c069c..1278dc87d4 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -41,6 +41,10 @@ FUZZ_TARGET_INIT(script_flags, initialize_script_flags)
for (unsigned i = 0; i < tx.vin.size(); ++i) {
CTxOut prevout;
ds >> prevout;
+ if (!MoneyRange(prevout.nValue)) {
+ // prevouts should be consensus-valid
+ prevout.nValue = 1;
+ }
spent_outputs.push_back(prevout);
}
PrecomputedTransactionData txdata;
@@ -48,7 +52,7 @@ FUZZ_TARGET_INIT(script_flags, initialize_script_flags)
for (unsigned i = 0; i < tx.vin.size(); ++i) {
const CTxOut& prevout = txdata.m_spent_outputs.at(i);
- const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata};
+ const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata, MissingDataBehavior::ASSERT_FAIL};
ScriptError serror;
const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
index eb1c808a88..4bc709ed35 100644
--- a/src/test/fuzz/script_ops.cpp
+++ b/src/test/fuzz/script_ops.cpp
@@ -14,55 +14,54 @@
FUZZ_TARGET(script_ops)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- CScript script = ConsumeScript(fuzzed_data_provider);
+ CScript script_mut = ConsumeScript(fuzzed_data_provider);
while (fuzzed_data_provider.remaining_bytes() > 0) {
CallOneOf(
fuzzed_data_provider,
[&] {
CScript s = ConsumeScript(fuzzed_data_provider);
- script = std::move(s);
+ script_mut = std::move(s);
},
[&] {
const CScript& s = ConsumeScript(fuzzed_data_provider);
- script = s;
+ script_mut = s;
},
[&] {
- script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ script_mut << fuzzed_data_provider.ConsumeIntegral<int64_t>();
},
[&] {
- script << ConsumeOpcodeType(fuzzed_data_provider);
+ script_mut << ConsumeOpcodeType(fuzzed_data_provider);
},
[&] {
- script << ConsumeScriptNum(fuzzed_data_provider);
+ script_mut << ConsumeScriptNum(fuzzed_data_provider);
},
[&] {
- script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ script_mut << ConsumeRandomLengthByteVector(fuzzed_data_provider);
},
[&] {
- script.clear();
- },
- [&] {
- (void)script.GetSigOpCount(false);
- (void)script.GetSigOpCount(true);
- (void)script.GetSigOpCount(script);
- (void)script.HasValidOps();
- (void)script.IsPayToScriptHash();
- (void)script.IsPayToWitnessScriptHash();
- (void)script.IsPushOnly();
- (void)script.IsUnspendable();
- {
- CScript::const_iterator pc = script.begin();
- opcodetype opcode;
- (void)script.GetOp(pc, opcode);
- std::vector<uint8_t> data;
- (void)script.GetOp(pc, opcode, data);
- (void)script.IsPushOnly(pc);
- }
- {
- int version;
- std::vector<uint8_t> program;
- (void)script.IsWitnessProgram(version, program);
- }
+ script_mut.clear();
});
}
+ const CScript& script = script_mut;
+ (void)script.GetSigOpCount(false);
+ (void)script.GetSigOpCount(true);
+ (void)script.GetSigOpCount(script);
+ (void)script.HasValidOps();
+ (void)script.IsPayToScriptHash();
+ (void)script.IsPayToWitnessScriptHash();
+ (void)script.IsPushOnly();
+ (void)script.IsUnspendable();
+ {
+ CScript::const_iterator pc = script.begin();
+ opcodetype opcode;
+ (void)script.GetOp(pc, opcode);
+ std::vector<uint8_t> data;
+ (void)script.GetOp(pc, opcode, data);
+ (void)script.IsPushOnly(pc);
+ }
+ {
+ int version;
+ std::vector<uint8_t> program;
+ (void)script.IsWitnessProgram(version, program);
+ }
}
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index 2c92b159a5..18de0e1960 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -49,67 +49,6 @@ FUZZ_TARGET(str_printf)
// Upstream bug report: https://github.com/c42f/tinyformat/issues/70
try {
- (void)strprintf(format_string, (signed char*)nullptr);
- (void)tinyformat::format(bilingual_string, (signed char*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (unsigned char*)nullptr);
- (void)tinyformat::format(bilingual_string, (unsigned char*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (void*)nullptr);
- (void)tinyformat::format(bilingual_string, (void*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (bool*)nullptr);
- (void)tinyformat::format(bilingual_string, (bool*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (float*)nullptr);
- (void)tinyformat::format(bilingual_string, (float*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (double*)nullptr);
- (void)tinyformat::format(bilingual_string, (double*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int16_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int16_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint16_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint16_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int32_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int32_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint32_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint32_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int64_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int64_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint64_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint64_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
-
- try {
CallOneOf(
fuzzed_data_provider,
[&] {
diff --git a/src/test/fuzz/timedata.cpp b/src/test/fuzz/timedata.cpp
index d7fa66298a..f7dc5f433e 100644
--- a/src/test/fuzz/timedata.cpp
+++ b/src/test/fuzz/timedata.cpp
@@ -15,10 +15,12 @@ FUZZ_TARGET(timedata)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const unsigned int max_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
+ // A max_size of 0 implies no limit, so cap the max number of insertions to avoid timeouts
+ auto max_to_insert = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 4000);
// Divide by 2 to avoid signed integer overflow in .median()
const int64_t initial_value = fuzzed_data_provider.ConsumeIntegral<int64_t>() / 2;
CMedianFilter<int64_t> median_filter{max_size, initial_value};
- while (fuzzed_data_provider.remaining_bytes() > 0) {
+ while (fuzzed_data_provider.remaining_bytes() > 0 && --max_to_insert >= 0) {
(void)median_filter.median();
assert(median_filter.size() > 0);
assert(static_cast<size_t>(median_filter.size()) == median_filter.sorted().size());
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index 17e4405a13..ff34cc87b2 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -61,8 +61,11 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
return;
}
- TxValidationState state_with_dupe_check;
- (void)CheckTransaction(tx, state_with_dupe_check);
+ {
+ TxValidationState state_with_dupe_check;
+ const bool res{CheckTransaction(tx, state_with_dupe_check)};
+ Assert(res == state_with_dupe_check.IsValid());
+ }
const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE};
std::string reason;
@@ -100,9 +103,6 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- TxToUniv(tx, /* hashBlock */ {}, /* include_addresses */ true, u);
- TxToUniv(tx, /* hashBlock */ {}, /* include_addresses */ false, u);
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
- TxToUniv(tx, u256_max, /* include_addresses */ true, u);
- TxToUniv(tx, u256_max, /* include_addresses */ false, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ZERO, /* include_addresses */ true, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ONE, /* include_addresses */ false, u);
}
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index f84d6702a7..bab34ea340 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
+#include <miner.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
@@ -20,8 +21,9 @@ std::vector<COutPoint> g_outpoints_coinbase_init_mature;
std::vector<COutPoint> g_outpoints_coinbase_init_immature;
struct MockedTxPool : public CTxMemPool {
- void RollingFeeUpdate()
+ void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = true;
}
@@ -34,7 +36,7 @@ void initialize_tx_pool()
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
CTxIn in = MineBlock(g_setup->m_node, P2WSH_OP_TRUE);
- // Remember the txids to avoid expensive disk acess later on
+ // Remember the txids to avoid expensive disk access later on
auto& outpoints = i < COINBASE_MATURITY ?
g_outpoints_coinbase_init_mature :
g_outpoints_coinbase_init_immature;
@@ -77,13 +79,44 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)));
}
+void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
+{
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ {
+ BlockAssembler::Options options;
+ options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
+ options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /* max */ COIN)};
+ auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), ::Params(), options};
+ auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
+ Assert(block_template->block.vtx.size() >= 1);
+ }
+ const auto info_all = tx_pool.infoAll();
+ if (!info_all.empty()) {
+ const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
+ WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, /* dummy */ MemPoolRemovalReason::BLOCK));
+ std::vector<uint256> all_txids;
+ tx_pool.queryHashes(all_txids);
+ assert(all_txids.size() < info_all.size());
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ }
+ SyncWithValidationInterfaceQueue();
+}
+
+void MockTime(FuzzedDataProvider& fuzzed_data_provider, const CChainState& chainstate)
+{
+ const auto time = ConsumeTime(fuzzed_data_provider,
+ chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
+ std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());
+ SetMockTime(time);
+}
+
FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
- SetMockTime(ConsumeTime(fuzzed_data_provider));
+ MockTime(fuzzed_data_provider, chainstate);
SetMempoolConstraints(*node.args, fuzzed_data_provider);
// All RBF-spendable outpoints
@@ -163,7 +196,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}();
if (fuzzed_data_provider.ConsumeBool()) {
- SetMockTime(ConsumeTime(fuzzed_data_provider));
+ MockTime(fuzzed_data_provider, chainstate);
}
if (fuzzed_data_provider.ConsumeBool()) {
SetMempoolConstraints(*node.args, fuzzed_data_provider);
@@ -186,6 +219,16 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
RegisterSharedValidationInterface(txr);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
::fRequireStandard = fuzzed_data_provider.ConsumeBool();
+
+ // Make sure ProcessNewPackage on one transaction works and always fully validates the transaction.
+ // The result is not guaranteed to be the same as what is returned by ATMP.
+ const auto result_package = WITH_LOCK(::cs_main,
+ return ProcessNewPackage(node.chainman->ActiveChainstate(), tx_pool, {tx}, true));
+ auto it = result_package.m_tx_results.find(tx->GetWitnessHash());
+ Assert(it != result_package.m_tx_results.end());
+ Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
+ it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);
+
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx_pool, tx, bypass_limits));
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
SyncWithValidationInterfaceQueue();
@@ -237,23 +280,17 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}
}
}
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
- const auto info_all = tx_pool.infoAll();
- if (!info_all.empty()) {
- const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
- WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, /* dummy */ MemPoolRemovalReason::BLOCK));
- std::vector<uint256> all_txids;
- tx_pool.queryHashes(all_txids);
- assert(all_txids.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
- }
- SyncWithValidationInterfaceQueue();
+ Finish(fuzzed_data_provider, tx_pool, chainstate);
}
FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
+ auto& chainstate = node.chainman->ActiveChainstate();
+
+ MockTime(fuzzed_data_provider, chainstate);
+ SetMempoolConstraints(*node.args, fuzzed_data_provider);
std::vector<uint256> txids;
for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
@@ -265,11 +302,29 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
txids.push_back(ConsumeUInt256(fuzzed_data_provider));
}
- CTxMemPool tx_pool{/* estimator */ nullptr, /* check_ratio */ 1};
+ CTxMemPool tx_pool_{/* estimator */ nullptr, /* check_ratio */ 1};
+ MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
while (fuzzed_data_provider.ConsumeBool()) {
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
+ if (fuzzed_data_provider.ConsumeBool()) {
+ MockTime(fuzzed_data_provider, chainstate);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ SetMempoolConstraints(*node.args, fuzzed_data_provider);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ tx_pool.RollingFeeUpdate();
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const auto& txid = fuzzed_data_provider.ConsumeBool() ?
+ mut_tx.GetHash() :
+ PickValue(fuzzed_data_provider, txids);
+ const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
+ tx_pool.PrioritiseTransaction(txid, delta);
+ }
+
const auto tx = MakeTransactionRef(mut_tx);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
::fRequireStandard = fuzzed_data_provider.ConsumeBool();
@@ -278,8 +333,7 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
if (accepted) {
txids.push_back(tx->GetHash());
}
-
- SyncWithValidationInterfaceQueue();
}
+ Finish(fuzzed_data_provider, tx_pool, chainstate);
}
} // namespace
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index d786ac1db1..bcf0b0ce72 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -5,8 +5,198 @@
#include <test/fuzz/util.h>
#include <test/util/script.h>
#include <util/rbf.h>
+#include <util/time.h>
#include <version.h>
+FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
+ : m_fuzzed_data_provider{fuzzed_data_provider}
+{
+ m_socket = fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET);
+}
+
+FuzzedSock::~FuzzedSock()
+{
+ // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
+ // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
+ // Avoid closing an arbitrary file descriptor (m_socket is just a random very high number which
+ // theoretically may concide with a real opened file descriptor).
+ Reset();
+}
+
+FuzzedSock& FuzzedSock::operator=(Sock&& other)
+{
+ assert(false && "Move of Sock into FuzzedSock not allowed.");
+ return *this;
+}
+
+void FuzzedSock::Reset()
+{
+ m_socket = INVALID_SOCKET;
+}
+
+ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
+{
+ 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 FuzzedSock::Recv(void* buf, size_t len, int flags) const
+{
+ // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
+ // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
+ // returning -1 and setting errno to EAGAIN repeatedly.
+ constexpr std::array recv_errnos{
+ ECONNREFUSED,
+ EAGAIN,
+ EBADF,
+ 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;
+ }
+ std::vector<uint8_t> random_bytes;
+ bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
+ if (m_peek_data.has_value()) {
+ // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
+ random_bytes.assign({m_peek_data.value()});
+ if ((flags & MSG_PEEK) == 0) {
+ m_peek_data.reset();
+ }
+ pad_to_len_bytes = false;
+ } else if ((flags & MSG_PEEK) != 0) {
+ // New call with `MSG_PEEK`.
+ random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
+ if (!random_bytes.empty()) {
+ m_peek_data = random_bytes[0];
+ pad_to_len_bytes = false;
+ }
+ } else {
+ 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 (pad_to_len_bytes) {
+ 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();
+}
+
+int FuzzedSock::Connect(const sockaddr*, socklen_t) const
+{
+ // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
+ // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
+ // returning -1 and setting errno to EAGAIN repeatedly.
+ constexpr std::array connect_errnos{
+ ECONNREFUSED,
+ EAGAIN,
+ ECONNRESET,
+ EHOSTUNREACH,
+ EINPROGRESS,
+ EINTR,
+ ENETUNREACH,
+ ETIMEDOUT,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
+ return -1;
+ }
+ return 0;
+}
+
+int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
+{
+ constexpr std::array getsockopt_errnos{
+ ENOMEM,
+ ENOBUFS,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
+ return -1;
+ }
+ if (opt_val == nullptr) {
+ return 0;
+ }
+ std::memcpy(opt_val,
+ ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
+ *opt_len);
+ return 0;
+}
+
+bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
+{
+ constexpr std::array wait_errnos{
+ EBADF,
+ EINTR,
+ EINVAL,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
+ return false;
+ }
+ if (occurred != nullptr) {
+ *occurred = m_fuzzed_data_provider.ConsumeBool() ? requested : 0;
+ }
+ return true;
+}
+
+bool FuzzedSock::IsConnected(std::string& errmsg) const
+{
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return true;
+ }
+ errmsg = "disconnected at random by the fuzzer";
+ return false;
+}
void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept
{
@@ -27,6 +217,19 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_v
}
}
+CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
+{
+ return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
+}
+
+int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min, const std::optional<int64_t>& max) noexcept
+{
+ // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) disables mocktime.
+ static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
+ static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
+ return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.value_or(time_min), max.value_or(time_max));
+}
+
CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in, const int max_num_out) noexcept
{
CMutableTransaction tx_mut;
@@ -78,13 +281,13 @@ CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, co
return ret;
}
-CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length, const bool maybe_p2wsh) noexcept
+CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length, const bool maybe_p2wsh) noexcept
{
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CScript r_script{b.begin(), b.end()};
if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) {
uint256 script_hash;
- CSHA256().Write(&r_script[0], r_script.size()).Finalize(script_hash.begin());
+ CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
r_script.clear();
r_script << OP_0 << ToByteVector(script_hash);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 50d3ac66e5..36b1d5035c 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -26,7 +26,6 @@
#include <test/util/net.h>
#include <txmempool.h>
#include <uint256.h>
-#include <util/time.h>
#include <version.h>
#include <algorithm>
@@ -58,18 +57,20 @@ auto& PickValue(FuzzedDataProvider& fuzzed_data_provider, Collection& col)
return *it;
}
-[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
- const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
+ const std::string s = max_length ?
+ fuzzed_data_provider.ConsumeRandomLengthString(*max_length) :
+ fuzzed_data_provider.ConsumeRandomLengthString();
return {s.begin(), s.end()};
}
-[[nodiscard]] inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length));
}
-[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
return CDataStream{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
}
@@ -96,7 +97,7 @@ template <typename T>
}
template <typename T>
-[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
@@ -122,24 +123,15 @@ template <typename WeakEnumType, size_t size>
return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
}
-[[nodiscard]] inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);
-}
+[[nodiscard]] CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max = std::nullopt) noexcept;
-[[nodiscard]] inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) is a no-op.
- static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
- static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
- return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max);
-}
+[[nodiscard]] int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min = std::nullopt, const std::optional<int64_t>& max = std::nullopt) noexcept;
[[nodiscard]] CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in = 10, const int max_num_out = 10) noexcept;
[[nodiscard]] CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size = 32) noexcept;
-[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096, const bool maybe_p2wsh = false) noexcept;
+[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt, const bool maybe_p2wsh = false) noexcept;
[[nodiscard]] uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept;
@@ -392,7 +384,7 @@ public:
[&] {
mode = "a+";
});
-#ifdef _GNU_SOURCE
+#if defined _GNU_SOURCE && !defined __ANDROID__
const cookie_io_functions_t io_hooks = {
FuzzedFileProvider::read,
FuzzedFileProvider::write,
@@ -521,8 +513,6 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe
WRITE_TO_STREAM_CASE(uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()),
WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()),
WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
- WRITE_TO_STREAM_CASE(float, fuzzed_data_provider.ConsumeFloatingPoint<float>()),
- WRITE_TO_STREAM_CASE(double, fuzzed_data_provider.ConsumeFloatingPoint<double>()),
WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)),
WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)));
} catch (const std::ios_base::failure&) {
@@ -553,8 +543,6 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
READ_FROM_STREAM_CASE(uint32_t),
READ_FROM_STREAM_CASE(int64_t),
READ_FROM_STREAM_CASE(uint64_t),
- READ_FROM_STREAM_CASE(float),
- READ_FROM_STREAM_CASE(double),
READ_FROM_STREAM_CASE(std::string),
READ_FROM_STREAM_CASE(std::vector<char>));
} catch (const std::ios_base::failure&) {
@@ -575,181 +563,25 @@ class FuzzedSock : public Sock
mutable std::optional<uint8_t> m_peek_data;
public:
- explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
- {
- m_socket = fuzzed_data_provider.ConsumeIntegral<SOCKET>();
- }
+ explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider);
- ~FuzzedSock() override
- {
- // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
- // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
- // Avoid closing an arbitrary file descriptor (m_socket is just a random number which
- // may concide with a real opened file descriptor).
- Reset();
- }
+ ~FuzzedSock() override;
- FuzzedSock& operator=(Sock&& other) override
- {
- assert(false && "Move of Sock into FuzzedSock not allowed.");
- return *this;
- }
+ FuzzedSock& operator=(Sock&& other) override;
- void Reset() override
- {
- m_socket = INVALID_SOCKET;
- }
+ void Reset() override;
- 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 Send(const void* data, size_t len, int flags) const override;
- ssize_t Recv(void* buf, size_t len, int flags) const override
- {
- // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
- // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
- // returning -1 and setting errno to EAGAIN repeatedly.
- constexpr std::array recv_errnos{
- ECONNREFUSED,
- EAGAIN,
- EBADF,
- 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;
- }
- std::vector<uint8_t> random_bytes;
- bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
- if (m_peek_data.has_value()) {
- // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
- random_bytes.assign({m_peek_data.value()});
- if ((flags & MSG_PEEK) == 0) {
- m_peek_data.reset();
- }
- pad_to_len_bytes = false;
- } else if ((flags & MSG_PEEK) != 0) {
- // New call with `MSG_PEEK`.
- random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
- if (!random_bytes.empty()) {
- m_peek_data = random_bytes[0];
- pad_to_len_bytes = false;
- }
- } else {
- 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 (pad_to_len_bytes) {
- 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();
- }
+ ssize_t Recv(void* buf, size_t len, int flags) const override;
- int Connect(const sockaddr*, socklen_t) const override
- {
- // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
- // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
- // returning -1 and setting errno to EAGAIN repeatedly.
- constexpr std::array connect_errnos{
- ECONNREFUSED,
- EAGAIN,
- ECONNRESET,
- EHOSTUNREACH,
- EINPROGRESS,
- EINTR,
- ENETUNREACH,
- ETIMEDOUT,
- };
- if (m_fuzzed_data_provider.ConsumeBool()) {
- SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
- return -1;
- }
- return 0;
- }
+ int Connect(const sockaddr*, socklen_t) const override;
- int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
- {
- constexpr std::array getsockopt_errnos{
- ENOMEM,
- ENOBUFS,
- };
- if (m_fuzzed_data_provider.ConsumeBool()) {
- SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
- return -1;
- }
- if (opt_val == nullptr) {
- return 0;
- }
- std::memcpy(opt_val,
- ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
- *opt_len);
- return 0;
- }
+ int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
- bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
- {
- return m_fuzzed_data_provider.ConsumeBool();
- }
+ bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
- bool IsConnected(std::string& errmsg) const override {
- if (m_fuzzed_data_provider.ConsumeBool()) {
- return true;
- }
- errmsg = "disconnected at random by the fuzzer";
- return false;
- }
+ bool IsConnected(std::string& errmsg) const override;
};
[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
new file mode 100644
index 0000000000..6f2bc081c6
--- /dev/null
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -0,0 +1,87 @@
+// 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 <chainparams.h>
+#include <consensus/validation.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/mining.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+namespace {
+
+const std::vector<std::shared_ptr<CBlock>>* g_chain;
+
+void initialize_chain()
+{
+ const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)};
+ static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
+ g_chain = &chain;
+}
+
+FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()};
+ const auto& node = setup->m_node;
+ auto& chainman{*node.chainman};
+
+ const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
+
+ Assert(!chainman.SnapshotBlockhash());
+
+ {
+ CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION};
+ const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span<const uint8_t>{file_data};
+ }
+
+ const auto ActivateFuzzedSnapshot{[&] {
+ CAutoFile infile{fsbridge::fopen(snapshot_path, "rb"), SER_DISK, CLIENT_VERSION};
+ SnapshotMetadata metadata;
+ try {
+ infile >> metadata;
+ } catch (const std::ios_base::failure&) {
+ return false;
+ }
+ return chainman.ActivateSnapshot(infile, metadata, /* in_memory */ true);
+ }};
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ for (const auto& block : *g_chain) {
+ BlockValidationState dummy;
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())};
+ Assert(processed);
+ const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
+ Assert(index);
+ }
+ }
+
+ if (ActivateFuzzedSnapshot()) {
+ LOCK(::cs_main);
+ Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
+ Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
+ *chainman.SnapshotBlockhash());
+ const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
+ int64_t chain_tx{};
+ for (const auto& block : *g_chain) {
+ Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
+ const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
+ const auto num_tx{Assert(index)->nTx};
+ Assert(num_tx == 1);
+ chain_tx += num_tx;
+ }
+ Assert(g_chain->size() == coinscache.GetCacheSize());
+ Assert(chain_tx == chainman.ActiveTip()->nChainTx);
+ } else {
+ Assert(!chainman.SnapshotBlockhash());
+ Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
+ }
+ // Snapshot should refuse to load a second time regardless of validity
+ Assert(!ActivateFuzzedSnapshot());
+}
+} // namespace
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index 88c1a1a9cb..9186821836 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -29,14 +29,16 @@ public:
const int64_t m_end;
const int m_period;
const int m_threshold;
+ const int m_min_activation_height;
const int m_bit;
- 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}
+ TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int bit)
+ : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, 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);
+ assert(0 <= m_min_activation_height);
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
@@ -44,6 +46,7 @@ public:
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; }
+ int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation_height; }
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); }
@@ -144,32 +147,27 @@ FUZZ_TARGET_INIT(versionbits, initialize)
// 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));
+ int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 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
+ start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
never_active_test = true;
}
+ timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
}
+ int min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
- TestConditionChecker checker(start_time, timeout, period, threshold, bit);
+ TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, bit);
// Early exit if the versions don't signal sensibly for the deployment
if (!checker.Condition(ver_signal)) return;
@@ -294,28 +292,35 @@ FUZZ_TARGET_INIT(versionbits, initialize)
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);
+ assert(current_block->GetMedianTimePast() < checker.m_end);
} 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);
+ if (exp_state == ThresholdState::LOCKED_IN) {
+ assert(current_block->nHeight + 1 < min_activation);
+ } else {
+ assert(exp_state == ThresholdState::STARTED);
+ assert(blocks_sig >= threshold);
+ }
break;
case ThresholdState::ACTIVE:
+ assert(always_active_test || min_activation <= current_block->nHeight + 1);
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);
+ assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
+ if (exp_state == ThresholdState::STARTED) {
+ assert(blocks_sig < threshold);
+ } else {
+ assert(exp_state == ThresholdState::FAILED);
+ }
break;
default:
assert(false);
@@ -326,26 +331,20 @@ FUZZ_TARGET_INIT(versionbits, initialize)
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
}
- // "always active" has additional restrictions
if (always_active_test) {
+ // "always active" has additional restrictions
assert(state == ThresholdState::ACTIVE);
assert(exp_state == ThresholdState::ACTIVE);
assert(since == 0);
+ } else if (never_active_test) {
+ // "never active" does too
+ assert(state == ThresholdState::FAILED);
+ assert(exp_state == ThresholdState::FAILED);
+ assert(since == 0);
} else {
- // except for always active, the initial state is always DEFINED
+ // for signalled deployments, 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);
- }
- }
}
} // namespace
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 45c9b90ee9..2a217f3455 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -18,7 +18,7 @@ namespace getarg_tests{
protected:
void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args);
void ResetArgs(const std::string& strArg);
- ArgsManager m_args;
+ ArgsManager m_local_args;
};
}
@@ -39,14 +39,14 @@ void LocalTestingSetup :: ResetArgs(const std::string& strArg)
vecChar.push_back(s.c_str());
std::string error;
- BOOST_CHECK(m_args.ParseParameters(vecChar.size(), vecChar.data(), error));
+ BOOST_CHECK(m_local_args.ParseParameters(vecChar.size(), vecChar.data(), error));
}
void LocalTestingSetup :: SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
- m_args.ClearArgs();
+ m_local_args.ClearArgs();
for (const auto& arg : args) {
- m_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
+ m_local_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
}
}
@@ -55,52 +55,52 @@ BOOST_AUTO_TEST_CASE(boolarg)
const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
SetupArgs({foo});
ResetArgs("-foo");
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!m_args.GetBoolArg("-fo", false));
- BOOST_CHECK(m_args.GetBoolArg("-fo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-fo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-fo", true));
- BOOST_CHECK(!m_args.GetBoolArg("-fooo", false));
- BOOST_CHECK(m_args.GetBoolArg("-fooo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-fooo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-fooo", true));
ResetArgs("-foo=0");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1");
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
// New 0.6 feature: auto-map -nosomething to !-something:
ResetArgs("-nofoo");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
ResetArgs("-foo -nofoo"); // -nofoo should win
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
// New 0.6 feature: treat -- same as -:
ResetArgs("--foo=1");
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
ResetArgs("--nofoo=1");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
}
@@ -110,24 +110,24 @@ BOOST_AUTO_TEST_CASE(stringarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven");
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "");
ResetArgs("-foo=11");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "11");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "11");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "11");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "11");
ResetArgs("-foo=eleven");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "eleven");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", "eleven"), "eleven");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "eleven");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven");
}
@@ -137,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 11);
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 0);
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 11), 0);
- BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
ResetArgs("-foo=11 -bar=12");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 0), 11);
- BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 12);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12);
ResetArgs("-foo=NaN -bar=NotANumber");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", 1), 0);
- BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 1), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
}
BOOST_AUTO_TEST_CASE(doubledash)
@@ -159,11 +159,11 @@ BOOST_AUTO_TEST_CASE(doubledash)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("--foo");
- BOOST_CHECK_EQUAL(m_args.GetBoolArg("-foo", false), true);
+ BOOST_CHECK_EQUAL(m_local_args.GetBoolArg("-foo", false), true);
ResetArgs("--foo=verbose --bar=1");
- BOOST_CHECK_EQUAL(m_args.GetArg("-foo", ""), "verbose");
- BOOST_CHECK_EQUAL(m_args.GetArg("-bar", 0), 1);
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "verbose");
+ BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), 1);
}
BOOST_AUTO_TEST_CASE(boolargno)
@@ -172,24 +172,24 @@ BOOST_AUTO_TEST_CASE(boolargno)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("-nofoo");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=1");
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo=0");
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
ResetArgs("-foo --nofoo"); // --nofoo should win
- BOOST_CHECK(!m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(!m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false));
ResetArgs("-nofoo -foo"); // foo always wins:
- BOOST_CHECK(m_args.GetBoolArg("-foo", true));
- BOOST_CHECK(m_args.GetBoolArg("-foo", false));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", true));
+ BOOST_CHECK(m_local_args.GetBoolArg("-foo", false));
}
BOOST_AUTO_TEST_CASE(logargs)
@@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(logargs)
});
// Log the arguments
- m_args.LogArgs();
+ m_local_args.LogArgs();
LogInstance().DeleteCallback(print_connection);
// Check that what should appear does, and what shouldn't doesn't.
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 41a626c0ea..677bf39fd9 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -10,7 +10,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(hash_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(hash_tests)
BOOST_AUTO_TEST_CASE(murmurhash3)
{
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index 334f71106c..bd9ba4b8f7 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -27,7 +27,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
};
CThreadInterrupt interrupt;
- i2p::sam::Session session(GetDataDir() / "test_i2p_private_key", CService{}, &interrupt);
+ i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", CService{}, &interrupt);
{
ASSERT_DEBUG_LOG("Creating SAM session");
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index 611e9f2623..8629d13840 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -15,7 +15,7 @@
#include <univalue.h>
-extern UniValue read_json(const std::string& jsondata);
+UniValue read_json(const std::string& jsondata);
BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup)
diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp
index 25655b8894..e99c6e0fc8 100644
--- a/src/test/logging_tests.cpp
+++ b/src/test/logging_tests.cpp
@@ -14,7 +14,6 @@ BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(logging_timer)
{
-
SetMockTime(1);
auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
SetMockTime(2);
@@ -29,8 +28,6 @@ BOOST_AUTO_TEST_CASE(logging_timer)
auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
SetMockTime(2);
BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000.00μs)");
-
- SetMockTime(0);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 38fed51af2..bf36f8a6c9 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -571,8 +571,6 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
// ... unless it has gone all the way to 0 (after getting past 1000/2)
-
- SetMockTime(0);
}
inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index 98b27994a6..e540b59b35 100644
--- a/src/test/merkleblock_tests.cpp
+++ b/src/test/merkleblock_tests.cpp
@@ -8,8 +8,10 @@
#include <boost/test/unit_test.hpp>
+#include <set>
+#include <vector>
-BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(merkleblock_tests)
/**
* Create a CMerkleBlock using a list of txids which will be found in the
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 9ba004cc38..c47d0eae1e 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -28,7 +28,8 @@ 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(::ChainstateActive(), *m_node.mempool, tx, flags);
+ CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
+ return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags);
}
BlockAssembler AssemblerForTest(const CChainParams& params);
};
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index e14d2dd72d..39f9b7ee28 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -77,20 +77,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys.assign(1,key[0]);
keys.push_back(key[1]);
s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
- BOOST_CHECK(VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err));
+ BOOST_CHECK(VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
for (int i = 0; i < 4; i++)
{
keys.assign(1,key[i]);
s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
- BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 1: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("a&b 1: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
keys.assign(1,key[1]);
keys.push_back(key[i]);
s = sign_multisig(a_and_b, keys, CTransaction(txTo[0]), 0);
- BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 2: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("a&b 2: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
@@ -101,18 +101,18 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
s = sign_multisig(a_or_b, keys, CTransaction(txTo[1]), 0);
if (i == 0 || i == 1)
{
- BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i));
+ BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
else
{
- BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
}
s.clear();
s << OP_0 << OP_1;
- BOOST_CHECK(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err));
+ BOOST_CHECK(!VerifyScript(s, a_or_b, nullptr, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err));
@@ -124,12 +124,12 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
s = sign_multisig(escrow, keys, CTransaction(txTo[2]), 0);
if (i < j && i < 3 && j < 3)
{
- BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 1: %d %d", i, j));
+ BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("escrow 1: %d %d", i, j));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
else
{
- BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 2: %d %d", i, j));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, nullptr, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount, MissingDataBehavior::ASSERT_FAIL), &err), strprintf("escrow 2: %d %d", i, j));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 8eab26f3d5..46f88c1282 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -300,16 +300,17 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
// IPv6, scoped/link-local. See https://tools.ietf.org/html/rfc4007
// We support non-negative decimal integers (uint32_t) as zone id indices.
- // Test with a fairly-high value, e.g. 32, to avoid locally reserved ids.
+ // Normal link-local scoped address functionality is to append "%" plus the
+ // zone id, for example, given a link-local address of "fe80::1" and a zone
+ // id of "32", return the address as "fe80::1%32".
const std::string link_local{"fe80::1"};
const std::string scoped_addr{link_local + "%32"};
BOOST_REQUIRE(LookupHost(scoped_addr, addr, false));
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsIPv6());
BOOST_CHECK(!addr.IsBindAny());
- const std::string addr_str{addr.ToString()};
- BOOST_CHECK(addr_str == scoped_addr || addr_str == "fe80:0:0:0:0:0:0:1");
- // The fallback case "fe80:0:0:0:0:0:0:1" is needed for macOS 10.14/10.15 and (probably) later.
+ BOOST_CHECK_EQUAL(addr.ToString(), scoped_addr);
+
// Test that the delimiter "%" and default zone id of 0 can be omitted for the default scope.
BOOST_REQUIRE(LookupHost(link_local + "%0", addr, false));
BOOST_REQUIRE(addr.IsValid());
@@ -317,15 +318,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK_EQUAL(addr.ToString(), link_local);
- // TORv2
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- 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");
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
// TORv3
const char* torv3_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";
@@ -469,10 +463,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v1)
BOOST_CHECK_EQUAL(HexStr(s), "1a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
s.clear();
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- s << addr;
- BOOST_CHECK_EQUAL(HexStr(s), "fd87d87eeb43f1f2f3f4f5f6f7f8f9fa");
- s.clear();
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
s << addr;
@@ -507,10 +499,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v2)
BOOST_CHECK_EQUAL(HexStr(s), "02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
s.clear();
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- s << addr;
- BOOST_CHECK_EQUAL(HexStr(s), "030af1f2f3f4f5f6f7f8f9fa");
- s.clear();
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.SetSpecial("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"));
s << addr;
@@ -616,26 +606,14 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(!addr.IsValid());
BOOST_REQUIRE(s.empty());
- // Valid TORv2.
+ // TORv2, no longer supported.
s << MakeSpan(ParseHex("03" // network type (TORv2)
"0a" // address length
"f1f2f3f4f5f6f7f8f9fa")); // address
s >> addr;
- BOOST_CHECK(addr.IsValid());
- BOOST_CHECK(addr.IsTor());
- BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+ BOOST_CHECK(!addr.IsValid());
BOOST_REQUIRE(s.empty());
- // Invalid TORv2, with bogus length.
- s << MakeSpan(ParseHex("03" // network type (TORv2)
- "07" // address length
- "00")); // address
- BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
- HasReason("BIP155 TORv2 address with length 7 (should be 10)"));
- BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
- s.clear();
-
// Valid TORv3.
s << MakeSpan(ParseHex("04" // network type (TORv3)
"20" // address length
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 33b56624a8..687d2f6747 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -44,13 +44,12 @@ static CNetAddr CreateInternal(const std::string& host)
BOOST_AUTO_TEST_CASE(netbase_networks)
{
- BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
- BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION);
- BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
-
+ BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
+ BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
+ BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").GetNetwork() == NET_ONION);
+ BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
}
BOOST_AUTO_TEST_CASE(netbase_properties)
@@ -73,7 +72,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343());
BOOST_CHECK(ResolveIP("FE80::").IsRFC4862());
BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052());
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
+ BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").IsTor());
BOOST_CHECK(ResolveIP("127.0.0.1").IsLocal());
BOOST_CHECK(ResolveIP("::1").IsLocal());
BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable());
@@ -133,18 +132,6 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse("[fd6c:88c0:8724:1:2:3:4:5]", "[fd6c:88c0:8724:1:2:3:4:5]:65535"));
}
-BOOST_AUTO_TEST_CASE(onioncat_test)
-{
- // values from https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat
- CNetAddr addr1(ResolveIP("5wyqrzbvrdsumnok.onion"));
- CNetAddr addr2(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca"));
- BOOST_CHECK(addr1 == addr2);
- BOOST_CHECK(addr1.IsTor());
- BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
- BOOST_CHECK(addr1.IsRoutable());
-
-}
-
BOOST_AUTO_TEST_CASE(embedded_test)
{
CNetAddr addr1(ResolveIP("1.2.3.4"));
@@ -338,7 +325,6 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup)
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
@@ -381,41 +367,63 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
// If no permission flags, assume backward compatibility
BOOST_CHECK(NetWhitebindPermissions::TryParse("1.2.3.4:32", whitebindPermissions, error));
BOOST_CHECK(error.empty());
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ISIMPLICIT);
- BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
- NetPermissions::ClearFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
- BOOST_CHECK(!NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
- NetPermissions::AddFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
- BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::Implicit);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, NetPermissionFlags::Implicit));
+ NetPermissions::ClearFlag(whitebindPermissions.m_flags, NetPermissionFlags::Implicit);
+ BOOST_CHECK(!NetPermissions::HasFlag(whitebindPermissions.m_flags, NetPermissionFlags::Implicit));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
+ NetPermissions::AddFlag(whitebindPermissions.m_flags, NetPermissionFlags::Implicit);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, NetPermissionFlags::Implicit));
// Can set one permission
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::BloomFilter);
BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
+
+ NetWhitebindPermissions noban, noban_download, download_noban, download;
+
+ // "noban" implies "download"
+ BOOST_REQUIRE(NetWhitebindPermissions::TryParse("noban@1.2.3.4:32", noban, error));
+ BOOST_CHECK_EQUAL(noban.m_flags, NetPermissionFlags::NoBan);
+ BOOST_CHECK(NetPermissions::HasFlag(noban.m_flags, NetPermissionFlags::Download));
+ BOOST_CHECK(NetPermissions::HasFlag(noban.m_flags, NetPermissionFlags::NoBan));
+
+ // "noban,download" is equivalent to "noban"
+ BOOST_REQUIRE(NetWhitebindPermissions::TryParse("noban,download@1.2.3.4:32", noban_download, error));
+ BOOST_CHECK_EQUAL(noban_download.m_flags, noban.m_flags);
+
+ // "download,noban" is equivalent to "noban"
+ BOOST_REQUIRE(NetWhitebindPermissions::TryParse("download,noban@1.2.3.4:32", download_noban, error));
+ BOOST_CHECK_EQUAL(download_noban.m_flags, noban.m_flags);
+
+ // "download" excludes (does not imply) "noban"
+ BOOST_REQUIRE(NetWhitebindPermissions::TryParse("download@1.2.3.4:32", download, error));
+ BOOST_CHECK_EQUAL(download.m_flags, NetPermissionFlags::Download);
+ BOOST_CHECK(NetPermissions::HasFlag(download.m_flags, NetPermissionFlags::Download));
+ BOOST_CHECK(!NetPermissions::HasFlag(download.m_flags, NetPermissionFlags::NoBan));
// Happy path, can parse flags
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error));
// forcerelay should also activate the relay permission
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::ForceRelay | NetPermissionFlags::Relay);
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::Relay | NetPermissionFlags::NoBan);
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitebindPermissions, error));
BOOST_CHECK(NetWhitebindPermissions::TryParse("all@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ALL);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::All);
// Allow dups
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::Relay | NetPermissionFlags::NoBan | NetPermissionFlags::Download); // "noban" implies "download"
// Allow empty
BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::Relay | NetPermissionFlags::NoBan);
BOOST_CHECK(NetWhitebindPermissions::TryParse(",@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
- BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
// Detect invalid flag
BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
@@ -427,14 +435,16 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
// Happy path for whitelist parsing
BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
- BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN);
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::NoBan);
+ BOOST_CHECK(NetPermissions::HasFlag(whitelistPermissions.m_flags, NetPermissionFlags::NoBan));
+
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error));
- BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY);
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::ForceRelay | NetPermissionFlags::NoBan | NetPermissionFlags::Relay);
BOOST_CHECK(error.empty());
BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
- const auto strings = NetPermissions::ToStrings(PF_ALL);
+ const auto strings = NetPermissions::ToStrings(NetPermissionFlags::All);
BOOST_CHECK_EQUAL(strings.size(), 7U);
BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end());
@@ -457,10 +467,10 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret));
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret));
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret));
- BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret));
+ BOOST_CHECK(LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com\0"s, ret));
}
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp
index 6d8872b11e..4a15be6ca6 100644
--- a/src/test/policy_fee_tests.cpp
+++ b/src/test/policy_fee_tests.cpp
@@ -5,11 +5,11 @@
#include <amount.h>
#include <policy/fees.h>
-#include <test/util/setup_common.h>
-
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(policy_fee_tests, BasicTestingSetup)
+#include <set>
+
+BOOST_AUTO_TEST_SUITE(policy_fee_tests)
BOOST_AUTO_TEST_CASE(FeeRounder)
{
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 7da364d316..21576bb868 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -7,7 +7,9 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(reverselock_tests, BasicTestingSetup)
+#include <stdexcept>
+
+BOOST_AUTO_TEST_SUITE(reverselock_tests)
BOOST_AUTO_TEST_CASE(reverselock_basics)
{
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 67e70b3bc3..67fbc9f8a2 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -33,8 +33,8 @@ UniValue RPCTestingSetup::CallRPC(std::string args)
boost::split(vArgs, args, boost::is_any_of(" \t"));
std::string strMethod = vArgs[0];
vArgs.erase(vArgs.begin());
- std::any context{&m_node};
- JSONRPCRequest request(context);
+ JSONRPCRequest request;
+ request.context = &m_node;
request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs);
if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();
@@ -269,22 +269,29 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- UniValue banned_until = find_value(o1, "banned_until");
+ int64_t banned_until{find_value(o1, "banned_until").get_int64()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
- BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check
+ BOOST_CHECK_EQUAL(banned_until, 9907731200); // absolute time check
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
+ auto now = 10'000s;
+ SetMockTime(now);
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 200")));
+ SetMockTime(now += 2s);
+ const int64_t time_remaining_expected{198};
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
ar = r.get_array();
o1 = ar[0].get_obj();
adr = find_value(o1, "address");
- banned_until = find_value(o1, "banned_until");
+ banned_until = find_value(o1, "banned_until").get_int64();
+ const int64_t ban_created{find_value(o1, "ban_created").get_int64()};
+ const int64_t ban_duration{find_value(o1, "ban_duration").get_int64()};
+ const int64_t time_remaining{find_value(o1, "time_remaining").get_int64()};
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
- int64_t now = GetTime();
- BOOST_CHECK(banned_until.get_int64() > now);
- BOOST_CHECK(banned_until.get_int64()-now <= 200);
+ BOOST_CHECK_EQUAL(banned_until, time_remaining_expected + now.count());
+ BOOST_CHECK_EQUAL(ban_duration, banned_until - ban_created);
+ BOOST_CHECK_EQUAL(time_remaining, time_remaining_expected);
// must throw an exception because 127.0.0.1 is in already banned subnet range
BOOST_CHECK_THROW(r = CallRPC(std::string("setban 127.0.0.1 add")), std::runtime_error);
@@ -431,4 +438,39 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight)
}
}
+BOOST_AUTO_TEST_CASE(help_example)
+{
+ // test different argument types
+ const RPCArgList& args = {{"foo", "bar"}, {"b", true}, {"n", 1}};
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", args), "> bitcoin-cli -named test foo=bar b=true n=1\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", args), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"foo\":\"bar\",\"b\":true,\"n\":1}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test shell escape
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b'ar"}}), "> bitcoin-cli -named test foo='b'''ar'\n");
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b\"ar"}}), "> bitcoin-cli -named test foo='b\"ar'\n");
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b ar"}}), "> bitcoin-cli -named test foo='b ar'\n");
+
+ // test object params
+ UniValue obj_value(UniValue::VOBJ);
+ obj_value.pushKV("foo", "bar");
+ obj_value.pushKV("b", false);
+ obj_value.pushKV("n", 1);
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", obj_value}}), "> bitcoin-cli -named test name='{\"foo\":\"bar\",\"b\":false,\"n\":1}'\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", obj_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":{\"foo\":\"bar\",\"b\":false,\"n\":1}}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test array params
+ UniValue arr_value(UniValue::VARR);
+ arr_value.push_back("bar");
+ arr_value.push_back(false);
+ arr_value.push_back(1);
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", arr_value}}), "> bitcoin-cli -named test name='[\"bar\",false,1]'\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", arr_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":[\"bar\",false,1]}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test types don't matter for shell
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("foo", {{"arg", true}}), HelpExampleCliNamed("foo", {{"arg", "true"}}));
+
+ // test types matter for Rpc
+ BOOST_CHECK_NE(HelpExampleRpcNamed("foo", {{"arg", true}}), HelpExampleRpcNamed("foo", {{"arg", "true"}}));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 856ec6346d..d8a44a65dd 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -41,7 +41,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri
txTo.vin[0].scriptSig = scriptSig;
txTo.vout[0].nValue = 1;
- return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), &err);
+ return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err);
}
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 4dc0dd5f51..a01d3fa03a 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -3,10 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
+#include <key_io.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <test/util/setup_common.h>
+#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
@@ -98,7 +100,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
// TxoutType::WITNESS_V0_SCRIPTHASH
uint256 scriptHash;
- CSHA256().Write(&redeemScript[0], redeemScript.size())
+ CSHA256().Write(redeemScript.data(), redeemScript.size())
.Finalize(scriptHash.begin());
s.clear();
@@ -111,9 +113,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
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));
+ BOOST_CHECK_EQUAL(solutions.size(), 1U);
+ BOOST_CHECK(solutions[0] == ToByteVector(uint256::ZERO));
// TxoutType::WITNESS_UNKNOWN
s.clear();
@@ -199,23 +200,20 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
s.clear();
s << ToByteVector(pubkey) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(std::get_if<PKHash>(&address) &&
- *std::get_if<PKHash>(&address) == PKHash(pubkey));
+ BOOST_CHECK(std::get<PKHash>(address) == PKHash(pubkey));
// TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(std::get_if<PKHash>(&address) &&
- *std::get_if<PKHash>(&address) == PKHash(pubkey));
+ BOOST_CHECK(std::get<PKHash>(address) == PKHash(pubkey));
// TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(std::get_if<ScriptHash>(&address) &&
- *std::get_if<ScriptHash>(&address) == ScriptHash(redeemScript));
+ BOOST_CHECK(std::get<ScriptHash>(address) == ScriptHash(redeemScript));
// TxoutType::MULTISIG
s.clear();
@@ -233,7 +231,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(ExtractDestination(s, address));
WitnessV0KeyHash keyhash;
CHash160().Write(pubkey).Finalize(keyhash);
- BOOST_CHECK(std::get_if<WitnessV0KeyHash>(&address) && *std::get_if<WitnessV0KeyHash>(&address) == keyhash);
+ BOOST_CHECK(std::get<WitnessV0KeyHash>(address) == keyhash);
// TxoutType::WITNESS_V0_SCRIPTHASH
s.clear();
@@ -241,7 +239,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
s << OP_0 << ToByteVector(scripthash);
BOOST_CHECK(ExtractDestination(s, address));
- BOOST_CHECK(std::get_if<WitnessV0ScriptHash>(&address) && *std::get_if<WitnessV0ScriptHash>(&address) == scripthash);
+ BOOST_CHECK(std::get<WitnessV0ScriptHash>(address) == scripthash);
// TxoutType::WITNESS_UNKNOWN with unknown version
s.clear();
@@ -251,7 +249,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
unk.length = 33;
unk.version = 1;
std::copy(pubkey.begin(), pubkey.end(), unk.program);
- BOOST_CHECK(std::get_if<WitnessUnknown>(&address) && *std::get_if<WitnessUnknown>(&address) == unk);
+ BOOST_CHECK(std::get<WitnessUnknown>(address) == unk);
}
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
@@ -275,8 +273,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
- *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
+ BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
// TxoutType::PUBKEYHASH
s.clear();
@@ -285,8 +282,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
- *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
+ BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
// TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
@@ -296,8 +292,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get_if<ScriptHash>(&addresses[0]) &&
- *std::get_if<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
+ BOOST_CHECK(std::get<ScriptHash>(addresses[0]) == ScriptHash(redeemScript));
// TxoutType::MULTISIG
s.clear();
@@ -309,10 +304,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
BOOST_CHECK_EQUAL(nRequired, 2);
- BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
- *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
- BOOST_CHECK(std::get_if<PKHash>(&addresses[1]) &&
- *std::get_if<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
+ BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
+ BOOST_CHECK(std::get<PKHash>(addresses[1]) == PKHash(pubkeys[1]));
// TxoutType::NULL_DATA
s.clear();
@@ -378,7 +371,7 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG;
uint256 scriptHash;
- CSHA256().Write(&witnessScript[0], witnessScript.size())
+ CSHA256().Write(witnessScript.data(), witnessScript.size())
.Finalize(scriptHash.begin());
expected.clear();
@@ -387,4 +380,70 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
BOOST_CHECK(result == expected);
}
+BOOST_AUTO_TEST_CASE(script_standard_taproot_builder)
+{
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,2}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2,3,4,5,6,7,8,9,10,11,12,14,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,31,31,31,31,31,31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({128,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({129,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), false);
+
+ XOnlyPubKey key_inner{ParseHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")};
+ XOnlyPubKey key_1{ParseHex("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5")};
+ XOnlyPubKey key_2{ParseHex("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")};
+ CScript script_1 = CScript() << ToByteVector(key_1) << OP_CHECKSIG;
+ CScript script_2 = CScript() << ToByteVector(key_2) << OP_CHECKSIG;
+ uint256 hash_3 = uint256S("31fe7061656bea2a36aa60a2f7ef940578049273746935d296426dc0afd86b68");
+
+ TaprootBuilder builder;
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ builder.Add(2, script_2, 0xc0);
+ BOOST_CHECK(builder.IsValid() && !builder.IsComplete());
+ builder.AddOmitted(2, hash_3);
+ BOOST_CHECK(builder.IsValid() && !builder.IsComplete());
+ builder.Add(1, script_1, 0xc0);
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ builder.Finalize(key_inner);
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 25ca171b33..62fd81673d 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -39,8 +39,7 @@ static const unsigned int gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
unsigned int ParseScriptFlags(std::string strFlags);
std::string FormatScriptFlags(unsigned int flags);
-UniValue
-read_json(const std::string& jsondata)
+UniValue read_json(const std::string& jsondata)
{
UniValue v;
@@ -135,7 +134,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
const CTransaction txCredit{BuildCreditingTransaction(scriptPubKey, nValue)};
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, scriptWitness, txCredit);
CMutableTransaction tx2 = tx;
- BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message);
+ BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message);
BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
@@ -145,7 +144,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
// Weed out some invalid flag combinations.
if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
- BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
+ BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
}
#if defined(HAVE_CONSENSUS_LIB)
@@ -155,10 +154,10 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
if (libconsensus_flags == flags) {
int expectedSuccessCode = expect ? 1 : 0;
if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
} else {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
}
}
#endif
@@ -224,7 +223,7 @@ struct KeyData
pubkey0 = key0.GetPubKey();
pubkey0H = key0.GetPubKey();
pubkey0C = key0C.GetPubKey();
- *const_cast<unsigned char*>(&pubkey0H[0]) = 0x06 | (pubkey0H[64] & 1);
+ *const_cast<unsigned char*>(pubkey0H.data()) = 0x06 | (pubkey0H[64] & 1);
key1.Set(vchKey1, vchKey1 + 32, false);
key1C.Set(vchKey1, vchKey1 + 32, true);
@@ -290,7 +289,7 @@ public:
} else if (wm == WitnessMode::SH) {
witscript = scriptPubKey;
uint256 hash;
- CSHA256().Write(&witscript[0], witscript.size()).Finalize(hash.begin());
+ CSHA256().Write(witscript.data(), witscript.size()).Finalize(hash.begin());
scriptPubKey = CScript() << witnessversion << ToByteVector(hash);
}
if (P2SH) {
@@ -774,7 +773,7 @@ BOOST_AUTO_TEST_CASE(script_build)
{
CScript witscript = CScript() << ToByteVector(keys.pubkey0);
uint256 hash;
- CSHA256().Write(&witscript[0], witscript.size()).Finalize(hash.begin());
+ CSHA256().Write(witscript.data(), witscript.size()).Finalize(hash.begin());
std::vector<unsigned char> hashBytes = ToByteVector(hash);
hashBytes.pop_back();
tests.push_back(TestBuilder(CScript() << OP_0 << hashBytes,
@@ -1071,18 +1070,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom12);
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, CTransaction(txTo12));
- BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
+ BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
txTo12.vout[0].nValue = 2;
- BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
CScript goodsig2 = sign_multisig(scriptPubKey12, key2, CTransaction(txTo12));
- BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
+ BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
CScript badsig1 = sign_multisig(scriptPubKey12, key3, CTransaction(txTo12));
- BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
}
@@ -1104,54 +1103,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2);
CScript goodsig1 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key1); keys.push_back(key3);
CScript goodsig2 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key3);
CScript goodsig3 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
CScript badsig1 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
CScript badsig2 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
CScript badsig3 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
CScript badsig4 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear();
keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
CScript badsig5 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err));
keys.clear(); // Must have signatures
CScript badsig6 = sign_multisig(scriptPubKey23, keys, CTransaction(txTo23));
- BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err));
+ BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, nullptr, gFlags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}
@@ -1520,7 +1519,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 1);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
}
@@ -1543,7 +1542,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
}
@@ -1566,7 +1565,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size() * 2, nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size() * 2, nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
}
@@ -1589,7 +1588,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
stream << 0xffffffff;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
}
@@ -1612,7 +1611,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
}
@@ -1635,7 +1634,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
@@ -1733,7 +1732,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
size_t length = file.tellg();
file.seekg(0, std::ios::beg);
std::string data(length, '\0');
- file.read(&data[0], data.size());
+ file.read(data.data(), data.size());
UniValue tests = read_json(data);
BOOST_CHECK(tests.isArray());
BOOST_CHECK(tests.size() > 0);
diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp
new file mode 100644
index 0000000000..54e07b0f61
--- /dev/null
+++ b/src/test/serfloat_tests.cpp
@@ -0,0 +1,129 @@
+// Copyright (c) 2014-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 <hash.h>
+#include <test/util/setup_common.h>
+#include <util/serfloat.h>
+#include <serialize.h>
+#include <streams.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <cmath>
+#include <limits>
+
+BOOST_FIXTURE_TEST_SUITE(serfloat_tests, BasicTestingSetup)
+
+namespace {
+
+uint64_t TestDouble(double f) {
+ uint64_t i = EncodeDouble(f);
+ double f2 = DecodeDouble(i);
+ if (std::isnan(f)) {
+ // NaN is not guaranteed to round-trip exactly.
+ BOOST_CHECK(std::isnan(f2));
+ } else {
+ // Everything else is.
+ BOOST_CHECK(!std::isnan(f2));
+ uint64_t i2 = EncodeDouble(f2);
+ BOOST_CHECK_EQUAL(f, f2);
+ BOOST_CHECK_EQUAL(i, i2);
+ }
+ return i;
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
+ BOOST_CHECK_EQUAL(TestDouble(0.0), 0);
+ BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::infinity()), 0xfff0000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(0.5), 0x3fe0000000000000ULL);
+ BOOST_CHECK_EQUAL(TestDouble(1.0), 0x3ff0000000000000ULL);
+ BOOST_CHECK_EQUAL(TestDouble(2.0), 0x4000000000000000ULL);
+ BOOST_CHECK_EQUAL(TestDouble(4.0), 0x4010000000000000ULL);
+ BOOST_CHECK_EQUAL(TestDouble(785.066650390625), 0x4088888880000000ULL);
+
+ // Roundtrip test on IEC559-compatible systems
+ if (std::numeric_limits<double>::is_iec559) {
+ BOOST_CHECK_EQUAL(sizeof(double), 8);
+ BOOST_CHECK_EQUAL(sizeof(uint64_t), 8);
+ // Test extreme values
+ TestDouble(std::numeric_limits<double>::min());
+ TestDouble(-std::numeric_limits<double>::min());
+ TestDouble(std::numeric_limits<double>::max());
+ TestDouble(-std::numeric_limits<double>::max());
+ TestDouble(std::numeric_limits<double>::lowest());
+ TestDouble(-std::numeric_limits<double>::lowest());
+ TestDouble(std::numeric_limits<double>::quiet_NaN());
+ TestDouble(-std::numeric_limits<double>::quiet_NaN());
+ TestDouble(std::numeric_limits<double>::signaling_NaN());
+ TestDouble(-std::numeric_limits<double>::signaling_NaN());
+ TestDouble(std::numeric_limits<double>::denorm_min());
+ TestDouble(-std::numeric_limits<double>::denorm_min());
+ // Test exact encoding: on currently supported platforms, EncodeDouble
+ // should produce exactly the same as the in-memory representation for non-NaN.
+ for (int j = 0; j < 1000; ++j) {
+ // Iterate over 9 specific bits exhaustively; the others are chosen randomly.
+ // These specific bits are the sign bit, and the 2 top and bottom bits of
+ // exponent and mantissa in the IEEE754 binary64 format.
+ for (int x = 0; x < 512; ++x) {
+ uint64_t v = InsecureRandBits(64);
+ v &= ~(uint64_t{1} << 0);
+ if (x & 1) v |= (uint64_t{1} << 0);
+ v &= ~(uint64_t{1} << 1);
+ if (x & 2) v |= (uint64_t{1} << 1);
+ v &= ~(uint64_t{1} << 50);
+ if (x & 4) v |= (uint64_t{1} << 50);
+ v &= ~(uint64_t{1} << 51);
+ if (x & 8) v |= (uint64_t{1} << 51);
+ v &= ~(uint64_t{1} << 52);
+ if (x & 16) v |= (uint64_t{1} << 52);
+ v &= ~(uint64_t{1} << 53);
+ if (x & 32) v |= (uint64_t{1} << 53);
+ v &= ~(uint64_t{1} << 61);
+ if (x & 64) v |= (uint64_t{1} << 61);
+ v &= ~(uint64_t{1} << 62);
+ if (x & 128) v |= (uint64_t{1} << 62);
+ v &= ~(uint64_t{1} << 63);
+ if (x & 256) v |= (uint64_t{1} << 63);
+ double f;
+ memcpy(&f, &v, 8);
+ uint64_t v2 = TestDouble(f);
+ if (!std::isnan(f)) BOOST_CHECK_EQUAL(v, v2);
+ }
+ }
+ }
+}
+
+/*
+Python code to generate the below hashes:
+
+ def reversed_hex(x):
+ return binascii.hexlify(''.join(reversed(x)))
+ def dsha256(x):
+ return hashlib.sha256(hashlib.sha256(x).digest()).digest()
+
+ reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96'
+*/
+BOOST_AUTO_TEST_CASE(doubles)
+{
+ CDataStream ss(SER_DISK, 0);
+ // encode
+ for (int i = 0; i < 1000; i++) {
+ ss << EncodeDouble(i);
+ }
+ BOOST_CHECK(Hash(ss) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96"));
+
+ // decode
+ for (int i = 0; i < 1000; i++) {
+ uint64_t val;
+ ss >> val;
+ double j = DecodeDouble(val);
+ BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index f77cda7ba2..4b55e3bc26 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -24,7 +24,7 @@ protected:
CTransactionRef txval;
public:
CSerializeMethodsTestSingle() = default;
- CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin)
+ CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const uint8_t* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin)
{
memcpy(charstrval, charstrvalin, sizeof(charstrval));
}
@@ -70,10 +70,8 @@ BOOST_AUTO_TEST_CASE(sizes)
BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0), 0));
- BOOST_CHECK_EQUAL(sizeof(float), GetSerializeSize(float(0), 0));
- BOOST_CHECK_EQUAL(sizeof(double), GetSerializeSize(double(0), 0));
- // Bool is serialized as char
- BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(bool(0), 0));
+ // Bool is serialized as uint8_t
+ BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0), 0));
// Sanity-check GetSerializeSize and c++ type matching
BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1U);
@@ -85,93 +83,9 @@ BOOST_AUTO_TEST_CASE(sizes)
BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4U);
BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8U);
BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8U);
- BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0), 4U);
- BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0), 8U);
BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1U);
}
-BOOST_AUTO_TEST_CASE(floats_conversion)
-{
- // Choose values that map unambiguously to binary floating point to avoid
- // rounding issues at the compiler side.
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x00000000), 0.0F);
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f000000), 0.5F);
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f800000), 1.0F);
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40000000), 2.0F);
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40800000), 4.0F);
- BOOST_CHECK_EQUAL(ser_uint32_to_float(0x44444444), 785.066650390625F);
-
- BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000U);
- BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000U);
- BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000U);
- BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000U);
- BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000U);
- BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444U);
-}
-
-BOOST_AUTO_TEST_CASE(doubles_conversion)
-{
- // Choose values that map unambiguously to binary floating point to avoid
- // rounding issues at the compiler side.
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x0000000000000000ULL), 0.0);
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3fe0000000000000ULL), 0.5);
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3ff0000000000000ULL), 1.0);
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4000000000000000ULL), 2.0);
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4010000000000000ULL), 4.0);
- BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4088888880000000ULL), 785.066650390625);
-
- BOOST_CHECK_EQUAL(ser_double_to_uint64(0.0), 0x0000000000000000ULL);
- BOOST_CHECK_EQUAL(ser_double_to_uint64(0.5), 0x3fe0000000000000ULL);
- BOOST_CHECK_EQUAL(ser_double_to_uint64(1.0), 0x3ff0000000000000ULL);
- BOOST_CHECK_EQUAL(ser_double_to_uint64(2.0), 0x4000000000000000ULL);
- BOOST_CHECK_EQUAL(ser_double_to_uint64(4.0), 0x4010000000000000ULL);
- BOOST_CHECK_EQUAL(ser_double_to_uint64(785.066650390625), 0x4088888880000000ULL);
-}
-/*
-Python code to generate the below hashes:
-
- def reversed_hex(x):
- return binascii.hexlify(''.join(reversed(x)))
- def dsha256(x):
- return hashlib.sha256(hashlib.sha256(x).digest()).digest()
-
- reversed_hex(dsha256(''.join(struct.pack('<f', x) for x in range(0,1000)))) == '8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c'
- reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96'
-*/
-BOOST_AUTO_TEST_CASE(floats)
-{
- CDataStream ss(SER_DISK, 0);
- // encode
- for (int i = 0; i < 1000; i++) {
- ss << float(i);
- }
- BOOST_CHECK(Hash(ss) == uint256S("8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c"));
-
- // decode
- for (int i = 0; i < 1000; i++) {
- float j;
- ss >> j;
- BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
- }
-}
-
-BOOST_AUTO_TEST_CASE(doubles)
-{
- CDataStream ss(SER_DISK, 0);
- // encode
- for (int i = 0; i < 1000; i++) {
- ss << double(i);
- }
- BOOST_CHECK(Hash(ss) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96"));
-
- // decode
- for (int i = 0; i < 1000; i++) {
- double j;
- ss >> j;
- BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
- }
-}
-
BOOST_AUTO_TEST_CASE(varints)
{
// encode
@@ -349,7 +263,7 @@ BOOST_AUTO_TEST_CASE(class_methods)
int intval(100);
bool boolval(true);
std::string stringval("testing");
- const char charstrval[16] = "testing charstr";
+ const uint8_t charstrval[16]{"testing charstr"};
CMutableTransaction txval;
CTransactionRef tx_ref{MakeTransactionRef(txval)};
CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref);
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
index 548fd020a6..340ce33d91 100644
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(ReadWrite)
{
- fs::path path = GetDataDir() / "settings.json";
+ fs::path path = m_args.GetDataDirBase() / "settings.json";
WriteText(path, R"({
"string": "string",
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 2eb980e8cd..195565c1f8 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -21,7 +21,7 @@
#include <univalue.h>
-extern UniValue read_json(const std::string& jsondata);
+UniValue read_json(const std::string& jsondata);
// Old script.cpp SignatureHash function
uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 7e5274450d..12fc575c1e 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -71,7 +71,7 @@ static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTran
{
ScriptError error;
CTransaction inputi(input);
- bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error);
+ bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error);
BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK));
return error;
diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp
index 400de875b7..9e98f4f0b1 100644
--- a/src/test/sock_tests.cpp
+++ b/src/test/sock_tests.cpp
@@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(wait)
Sock sock0(s[0]);
Sock sock1(s[1]);
- std::thread waiter([&sock0]() { sock0.Wait(24h, Sock::RECV); });
+ std::thread waiter([&sock0]() { (void)sock0.Wait(24h, Sock::RECV); });
BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
// 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);
+ (void)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);
}
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 3079c9ff29..7af2b79f37 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -112,6 +112,17 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader)
BOOST_CHECK_THROW(new_reader >> d, std::ios_base::failure);
}
+BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue)
+{
+ std::vector<uint8_t> data{0x82, 0xa7, 0x31};
+ VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, data, /* pos= */ 0);
+ uint32_t varint = 0;
+ // Deserialize into r-value
+ reader >> VARINT(varint);
+ BOOST_CHECK_EQUAL(varint, 54321);
+ BOOST_CHECK(reader.empty());
+}
+
BOOST_AUTO_TEST_CASE(bitstream_reader_writer)
{
CDataStream data(SER_NETWORK, INIT_PROTO_VERSION);
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index 3e4d1dac9e..f5a8fc3aa6 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -8,6 +8,7 @@
#include <boost/test/unit_test.hpp>
#include <mutex>
+#include <stdexcept>
namespace {
template <typename MutexType>
@@ -76,7 +77,7 @@ void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_
}
} // namespace
-BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(sync_tests)
BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
{
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index 41aa17988c..659caaef61 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_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 <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -15,7 +14,7 @@ std::pair<std::string, std::string> SplitTorReplyLine(const std::string& s);
std::map<std::string, std::string> ParseTorReplyMapping(const std::string& s);
-BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(torcontrol_tests)
static void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
{
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 8f1d99b199..40c53cb2ec 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -20,6 +20,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
+#include <test/util/script.h>
#include <test/util/transaction_utils.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -38,7 +39,7 @@
typedef std::vector<unsigned char> valtype;
// In script_tests.cpp
-extern UniValue read_json(const std::string& jsondata);
+UniValue read_json(const std::string& jsondata);
static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
@@ -59,6 +60,9 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
+ {std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
+ {std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
+ {std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
};
unsigned int ParseScriptFlags(std::string strFlags)
@@ -78,6 +82,16 @@ unsigned int ParseScriptFlags(std::string strFlags)
return flags;
}
+// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
+bool CheckMapFlagNames()
+{
+ unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
+ for (const auto& pair : mapFlagNames) {
+ standard_flags_missing &= ~(pair.second);
+ }
+ return standard_flags_missing == 0;
+}
+
std::string FormatScriptFlags(unsigned int flags)
{
if (flags == 0) {
@@ -108,7 +122,7 @@ bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>&
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);
+ &input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata, MissingDataBehavior::ASSERT_FAIL), &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.
@@ -139,6 +153,7 @@ unsigned int TrimFlags(unsigned int flags)
// CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK;
+ Assert(IsValidFlagCombination(flags));
return flags;
}
@@ -149,17 +164,21 @@ unsigned int FillFlags(unsigned int flags)
// WITNESS implies P2SH (and transitively CLEANSTACK implies P2SH)
if (flags & SCRIPT_VERIFY_WITNESS) flags |= SCRIPT_VERIFY_P2SH;
+ Assert(IsValidFlagCombination(flags));
return flags;
}
-// Return valid flags that are all except one flag for each flag
-std::vector<unsigned int> ExcludeIndividualFlags(unsigned int flags)
+// Exclude each possible script verify flag from flags. Returns a set of these flag combinations
+// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
+// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
+// Assumes that mapFlagNames contains all script verify flags.
+std::set<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);
+ std::set<unsigned int> flags_combos;
+ for (const auto& pair : mapFlagNames) {
+ const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second));
+ if (flags != flags_excluding_one) {
+ flags_combos.insert(flags_excluding_one);
}
}
return flags_combos;
@@ -169,6 +188,7 @@ BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(tx_valid)
{
+ BOOST_CHECK_MESSAGE(CheckMapFlagNames(), "mapFlagNames is missing a script verification flag");
// Read tests from test/data/tx_valid.json
UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
@@ -228,16 +248,15 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_ERROR("Bad test flags: " << strTest);
}
- if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true)) {
- BOOST_ERROR("Tx unexpectedly failed: " << strTest);
- }
+ BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true),
+ "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) {
+ for (const auto& [name, flag] : mapFlagNames) {
// Removing individual flags
- unsigned int flags = TrimFlags(~(verify_flags | (1U << i)));
+ unsigned int flags = TrimFlags(~(verify_flags | flag));
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) {
- BOOST_ERROR("Tx unexpectedly failed with flag " << ToString(i) << " unset: " << strTest);
+ BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest);
}
// Removing random combinations of flags
flags = TrimFlags(~(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size())));
@@ -247,7 +266,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
}
// Check that flags are maximal: transaction should fail if any unset flags are set.
- for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) {
+ 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);
}
@@ -314,27 +333,31 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
PrecomputedTransactionData txdata(tx);
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);
+ // 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);
}
+ // Not using FillFlags() in the main test, in order to detect invalid verifyFlags combination
+ BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false),
+ "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));
+ for (const auto& [name, flag] : mapFlagNames) {
+ unsigned int flags = FillFlags(verify_flags | flag);
// 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);
+ BOOST_ERROR("Tx unexpectedly passed with flag " << name << " 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);
+ BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest);
}
}
// Check that flags are minimal: transaction should succeed if any set flags are unset.
- for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) {
+ 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);
}
@@ -427,7 +450,7 @@ static void CheckWithFlag(const CTransactionRef& output, const CMutableTransacti
{
ScriptError error;
CTransaction inputi(input);
- bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error);
+ bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error);
assert(ret == success);
}
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 5fc172ee86..d47c54fd6e 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -7,6 +7,7 @@
#include <script/standard.h>
#include <test/util/setup_common.h>
#include <util/time.h>
+#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -27,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
- txindex.Start();
+ BOOST_REQUIRE(txindex.Start(::ChainstateActive()));
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 8d14071297..95ad85d0f8 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -3,8 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
+#include <key_io.h>
+#include <policy/packages.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
+#include <script/standard.h>
#include <test/util/setup_common.h>
#include <validation.h>
@@ -47,4 +51,98 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
+// Create placeholder transactions that have no meaning.
+inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
+{
+ CMutableTransaction mtx = CMutableTransaction();
+ mtx.vin.resize(num_inputs);
+ mtx.vout.resize(num_outputs);
+ auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256());
+ for (size_t i{0}; i < num_inputs; ++i) {
+ mtx.vin[i].prevout.hash = InsecureRand256();
+ mtx.vin[i].prevout.n = 0;
+ mtx.vin[i].scriptSig = random_script;
+ }
+ for (size_t o{0}; o < num_outputs; ++o) {
+ mtx.vout[o].nValue = 1 * CENT;
+ mtx.vout[o].scriptPubKey = random_script;
+ }
+ return MakeTransactionRef(mtx);
+}
+
+BOOST_FIXTURE_TEST_CASE(package_tests, TestChain100Setup)
+{
+ LOCK(cs_main);
+ unsigned int initialPoolSize = m_node.mempool->size();
+
+ // Parent and Child Package
+ CKey parent_key;
+ parent_key.MakeNewKey(true);
+ CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
+ auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0,
+ /* input_height */ 0, /* input_signing_key */ coinbaseKey,
+ /* output_destination */ parent_locking_script,
+ /* output_amount */ CAmount(49 * COIN), /* submit */ false);
+ CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
+
+ CKey child_key;
+ child_key.MakeNewKey(true);
+ CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
+ auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0,
+ /* input_height */ 101, /* input_signing_key */ parent_key,
+ /* output_destination */ child_locking_script,
+ /* output_amount */ CAmount(48 * COIN), /* submit */ false);
+ CTransactionRef tx_child = MakeTransactionRef(mtx_child);
+ const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /* test_accept */ true);
+ BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason());
+ auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
+ auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
+ BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason());
+ BOOST_CHECK(it_child != result_parent_child.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason());
+
+ // Packages can't have more than 25 transactions.
+ Package package_too_many;
+ package_too_many.reserve(MAX_PACKAGE_COUNT + 1);
+ for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {
+ package_too_many.emplace_back(create_placeholder_tx(1, 1));
+ }
+ auto result_too_many = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_many, /* test_accept */ true);
+ BOOST_CHECK(result_too_many.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result_too_many.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
+ BOOST_CHECK_EQUAL(result_too_many.m_state.GetRejectReason(), "package-too-many-transactions");
+
+ // Packages can't have a total size of more than 101KvB.
+ CTransactionRef large_ptx = create_placeholder_tx(150, 150);
+ Package package_too_large;
+ auto size_large = GetVirtualTransactionSize(*large_ptx);
+ size_t total_size{0};
+ while (total_size <= MAX_PACKAGE_SIZE * 1000) {
+ package_too_large.push_back(large_ptx);
+ total_size += size_large;
+ }
+ BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
+ auto result_too_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_large, /* test_accept */ true);
+ BOOST_CHECK(result_too_large.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result_too_large.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
+ BOOST_CHECK_EQUAL(result_too_large.m_state.GetRejectReason(), "package-too-large");
+
+ // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
+ CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
+ BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000);
+ auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /* test_accept */ true);
+ BOOST_CHECK(result_single_large.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
+ BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
+ auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash());
+ BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end());
+ BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size");
+
+ // Check that mempool size hasn't changed.
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index ae626d4613..b4744cabc7 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -13,8 +13,9 @@
#include <iomanip>
#include <sstream>
#include <string>
+#include <vector>
-BOOST_FIXTURE_TEST_SUITE(uint256_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(uint256_tests)
const unsigned char R1Array[] =
"\x9c\x52\x4a\xdb\xcf\x56\x11\x12\x2b\x29\x12\x5e\x5d\x35\xd2\xd2"
diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp
index bccff5e5a6..b8ab9d2344 100644
--- a/src/test/util/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -5,6 +5,7 @@
#include <test/util/blockfilter.h>
#include <chainparams.h>
+#include <node/blockstorage.h>
#include <validation.h>
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 3fc3329da2..1204873828 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -11,8 +11,10 @@
#include <node/context.h>
#include <pow.h>
#include <script/standard.h>
+#include <test/util/script.h>
#include <util/check.h>
#include <validation.h>
+#include <versionbits.h>
CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
{
@@ -23,6 +25,37 @@ CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
return MineBlock(node, coinbase_script);
}
+std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params)
+{
+ std::vector<std::shared_ptr<CBlock>> ret{total_height};
+ auto time{params.GenesisBlock().nTime};
+ for (size_t height{0}; height < total_height; ++height) {
+ CBlock& block{*(ret.at(height) = std::make_shared<CBlock>())};
+
+ CMutableTransaction coinbase_tx;
+ coinbase_tx.vin.resize(1);
+ coinbase_tx.vin[0].prevout.SetNull();
+ coinbase_tx.vout.resize(1);
+ coinbase_tx.vout[0].scriptPubKey = P2WSH_OP_TRUE;
+ coinbase_tx.vout[0].nValue = GetBlockSubsidy(height + 1, params.GetConsensus());
+ coinbase_tx.vin[0].scriptSig = CScript() << (height + 1) << OP_0;
+ block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
+
+ block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
+ block.hashPrevBlock = (height >= 1 ? *ret.at(height - 1) : params.GenesisBlock()).GetHash();
+ block.hashMerkleRoot = BlockMerkleRoot(block);
+ block.nTime = ++time;
+ block.nBits = params.GenesisBlock().nBits;
+ block.nNonce = 0;
+
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, params.GetConsensus())) {
+ ++block.nNonce;
+ assert(block.nNonce);
+ }
+ }
+ return ret;
+}
+
CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = PrepareBlock(node, coinbase_scriptPubKey);
diff --git a/src/test/util/mining.h b/src/test/util/mining.h
index 5f250fffe8..1fc1864b91 100644
--- a/src/test/util/mining.h
+++ b/src/test/util/mining.h
@@ -7,12 +7,17 @@
#include <memory>
#include <string>
+#include <vector>
class CBlock;
+class CChainParams;
class CScript;
class CTxIn;
struct NodeContext;
+/** Create a blockchain, starting from genesis */
+std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params);
+
/** Returns the generated coin */
CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey);
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 2b7988413f..71685d437a 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -46,16 +46,16 @@ constexpr ServiceFlags ALL_SERVICE_FLAGS[]{
};
constexpr NetPermissionFlags ALL_NET_PERMISSION_FLAGS[]{
- NetPermissionFlags::PF_NONE,
- NetPermissionFlags::PF_BLOOMFILTER,
- NetPermissionFlags::PF_RELAY,
- NetPermissionFlags::PF_FORCERELAY,
- NetPermissionFlags::PF_NOBAN,
- NetPermissionFlags::PF_MEMPOOL,
- NetPermissionFlags::PF_ADDR,
- NetPermissionFlags::PF_DOWNLOAD,
- NetPermissionFlags::PF_ISIMPLICIT,
- NetPermissionFlags::PF_ALL,
+ NetPermissionFlags::None,
+ NetPermissionFlags::BloomFilter,
+ NetPermissionFlags::Relay,
+ NetPermissionFlags::ForceRelay,
+ NetPermissionFlags::NoBan,
+ NetPermissionFlags::Mempool,
+ NetPermissionFlags::Addr,
+ NetPermissionFlags::Download,
+ NetPermissionFlags::Implicit,
+ NetPermissionFlags::All,
};
constexpr ConnectionType ALL_CONNECTION_TYPES[]{
@@ -78,8 +78,7 @@ public:
explicit StaticContentsSock(const std::string& contents) : m_contents{contents}, m_consumed{0}
{
// Just a dummy number that is not INVALID_SOCKET.
- static_assert(INVALID_SOCKET != 1000);
- m_socket = 1000;
+ m_socket = INVALID_SOCKET - 1;
}
~StaticContentsSock() override { Reset(); }
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index f7800aefca..863c3ab565 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -28,6 +28,8 @@
#include <txdb.h>
#include <util/strencodings.h>
#include <util/string.h>
+#include <util/thread.h>
+#include <util/threadnames.h>
#include <util/time.h>
#include <util/translation.h>
#include <util/url.h>
@@ -71,7 +73,8 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args)
- : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()}
+ : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
+ m_args{}
{
const std::vector<const char*> arguments = Cat(
{
@@ -87,8 +90,9 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
extra_args);
util::ThreadRename("test");
fs::create_directories(m_path_root);
+ m_args.ForceSetArg("-datadir", m_path_root.string());
gArgs.ForceSetArg("-datadir", m_path_root.string());
- ClearDatadirCache();
+ gArgs.ClearPathCache();
{
SetupServerArgs(m_node);
std::string error;
@@ -120,6 +124,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
BasicTestingSetup::~BasicTestingSetup()
{
+ SetMockTime(0s); // Reset mocktime for following tests
LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
gArgs.ClearArgs();
@@ -132,7 +137,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 = std::make_unique<CScheduler>();
- m_node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); });
+ m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
@@ -190,7 +195,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
m_node.addrman = std::make_unique<CAddrMan>();
- m_node.banman = std::make_unique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
m_node.banman.get(), *m_node.scheduler, *m_node.chainman,
@@ -202,43 +207,31 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup(bool deterministic)
+TestChain100Setup::TestChain100Setup()
{
- 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);
- }
+ 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(), true);
// Generate a 100-block chain:
this->mineBlocks(COINBASE_MATURITY);
- if (m_deterministic) {
+ {
LOCK(::cs_main);
assert(
m_node.chainman->ActiveChain().Tip()->GetBlockHash().ToString() ==
- "49c95db1e470fed04496d801c9d8fbb78155d2c7f855232c918823d2c17d0cf6");
+ "571d80a9967ae599cec0448b0b0ba1cfb606f584d8069bd7166b86854ba7a191");
}
}
void TestChain100Setup::mineBlocks(int num_blocks)
{
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- for (int i = 0; i < num_blocks; i++)
- {
+ for (int i = 0; i < num_blocks; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
- if (m_deterministic) {
- SetMockTime(GetTime() + 1);
- }
+ SetMockTime(GetTime() + 1);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
@@ -253,7 +246,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
- RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
+ RegenerateCommitments(block, *Assert(m_node.chainman));
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
@@ -269,7 +262,8 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
int input_height,
CKey input_signing_key,
CScript output_destination,
- CAmount output_amount)
+ CAmount output_amount,
+ bool submit)
{
// Transaction we will submit to the mempool
CMutableTransaction mempool_txn;
@@ -302,8 +296,8 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
std::map<int, std::string> input_errors;
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
- // Add transaction to the mempool
- {
+ // If submit=true, add transaction to the mempool.
+ if (submit) {
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);
@@ -315,9 +309,6 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
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 7323f1f0b6..5d12dc2323 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -8,6 +8,7 @@
#include <chainparamsbase.h>
#include <fs.h>
#include <key.h>
+#include <util/system.h>
#include <node/context.h>
#include <pubkey.h>
#include <random.h>
@@ -80,6 +81,7 @@ struct BasicTestingSetup {
~BasicTestingSetup();
const fs::path m_path_root;
+ ArgsManager m_args;
};
/** Testing setup that performs all steps up until right before
@@ -112,7 +114,7 @@ class CScript;
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public RegTestingSetup {
- TestChain100Setup(bool deterministic = false);
+ TestChain100Setup();
/**
* Create a new block with just given transactions, coinbase paying to
@@ -133,26 +135,22 @@ struct TestChain100Setup : public RegTestingSetup {
* @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
+ * @param submit Whether or not to submit to mempool
*/
CMutableTransaction CreateValidMempoolTransaction(CTransactionRef input_transaction,
int input_vout,
int input_height,
CKey input_signing_key,
CScript output_destination,
- CAmount output_amount = CAmount(1 * COIN));
+ CAmount output_amount = CAmount(1 * COIN),
+ bool submit = true);
~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.
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 5ac09b05db..7ce38519cf 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -49,24 +49,27 @@ BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(util_datadir)
{
- ClearDatadirCache();
- const fs::path dd_norm = GetDataDir();
+ // Use local args variable instead of m_args to avoid making assumptions about test setup
+ ArgsManager args;
+ args.ForceSetArg("-datadir", m_path_root.string());
- gArgs.ForceSetArg("-datadir", dd_norm.string() + "/");
- ClearDatadirCache();
- BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+ const fs::path dd_norm = args.GetDataDirBase();
- gArgs.ForceSetArg("-datadir", dd_norm.string() + "/.");
- ClearDatadirCache();
- BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+ args.ForceSetArg("-datadir", dd_norm.string() + "/");
+ args.ClearPathCache();
+ BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
- gArgs.ForceSetArg("-datadir", dd_norm.string() + "/./");
- ClearDatadirCache();
- BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+ args.ForceSetArg("-datadir", dd_norm.string() + "/.");
+ args.ClearPathCache();
+ BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
- gArgs.ForceSetArg("-datadir", dd_norm.string() + "/.//");
- ClearDatadirCache();
- BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+ args.ForceSetArg("-datadir", dd_norm.string() + "/./");
+ args.ClearPathCache();
+ BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
+
+ args.ForceSetArg("-datadir", dd_norm.string() + "/.//");
+ args.ClearPathCache();
+ BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase());
}
BOOST_AUTO_TEST_CASE(util_check)
@@ -179,7 +182,7 @@ BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime)
BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0);
BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777);
- auto time = GetSystemTimeInSeconds();
+ auto time = GetTimeSeconds();
BOOST_CHECK_EQUAL(ParseISO8601DateTime(FormatISO8601DateTime(time)), time);
}
@@ -326,6 +329,25 @@ BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest)
CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
}
+struct NoIncludeConfTest {
+ std::string Parse(const char* arg)
+ {
+ TestArgsManager test;
+ test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}});
+ std::array argv{"ignored", arg};
+ std::string error;
+ (void)test.ParseParameters(argv.size(), argv.data(), error);
+ return error;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest)
+{
+ BOOST_CHECK_EQUAL(Parse("-noincludeconf"), "");
+ BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\"");
+ BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\"");
+}
+
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
TestArgsManager testArgs;
@@ -1143,21 +1165,23 @@ BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)
{
// Test writing setting.
TestArgsManager args1;
+ args1.ForceSetArg("-datadir", m_path_root.string());
args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; });
args1.WriteSettingsFile();
// Test reading setting.
TestArgsManager args2;
+ args2.ForceSetArg("-datadir", m_path_root.string());
args2.ReadSettingsFile();
args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); });
// Test error logging, and remove previously written setting.
{
ASSERT_DEBUG_LOG("Failed renaming settings file");
- fs::remove(GetDataDir() / "settings.json");
- fs::create_directory(GetDataDir() / "settings.json");
+ fs::remove(args1.GetDataDirBase() / "settings.json");
+ fs::create_directory(args1.GetDataDirBase() / "settings.json");
args2.WriteSettingsFile();
- fs::remove(GetDataDir() / "settings.json");
+ fs::remove(args1.GetDataDirBase() / "settings.json");
}
}
@@ -1754,6 +1778,15 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
BOOST_CHECK(!ParseFixedPoint("1.1e", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("1.1e-", 8, &amount));
BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount));
+
+ // Test with 3 decimal places for fee rates in sat/vB.
+ BOOST_CHECK(ParseFixedPoint("0.001", 3, &amount));
+ BOOST_CHECK_EQUAL(amount, CAmount{1});
+ BOOST_CHECK(!ParseFixedPoint("0.0009", 3, &amount));
+ BOOST_CHECK(!ParseFixedPoint("31.00100001", 3, &amount));
+ BOOST_CHECK(!ParseFixedPoint("31.0011", 3, &amount));
+ BOOST_CHECK(!ParseFixedPoint("31.99999999", 3, &amount));
+ BOOST_CHECK(!ParseFixedPoint("31.999999999999999999999", 3, &amount));
}
static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
@@ -1766,7 +1799,7 @@ static constexpr char LockCommand = 'L';
static constexpr char UnlockCommand = 'U';
static constexpr char ExitCommand = 'X';
-static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
+[[noreturn]] static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
{
char ch;
while (true) {
@@ -1796,7 +1829,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
BOOST_AUTO_TEST_CASE(test_LockDirectory)
{
- fs::path dirname = GetDataDir() / "lock_dir";
+ fs::path dirname = m_args.GetDataDirBase() / "lock_dir";
const std::string lockname = ".lock";
#ifndef WIN32
// Revert SIGCHLD to default, otherwise boost.test will catch and fail on
@@ -1885,7 +1918,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory)
BOOST_AUTO_TEST_CASE(test_DirIsWritable)
{
// Should be able to write to the data dir.
- fs::path tmpdirname = GetDataDir();
+ fs::path tmpdirname = m_args.GetDataDirBase();
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp
index f3f9fb2bff..a5b456dd7a 100644
--- a/src/test/util_threadnames_tests.cpp
+++ b/src/test/util_threadnames_tests.cpp
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/util/setup_common.h>
#include <util/string.h>
#include <util/threadnames.h>
#include <mutex>
#include <set>
+#include <string>
#include <thread>
#include <vector>
@@ -17,7 +17,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(util_threadnames_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_SUITE(util_threadnames_tests)
const std::string TEST_THREAD_NAME_BASE = "test_thread.";
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 94d4277019..0b912acb08 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -200,7 +200,7 @@ CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleati
}
//! Test basic snapshot activation.
-BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100DeterministicSetup)
+BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
{
ChainstateManager& chainman = *Assert(m_node.chainman);
@@ -226,13 +226,12 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Determi
// 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()));
+ BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
+ BOOST_CHECK(!chainman.SnapshotBlockhash());
// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
// be found.
+ constexpr int snapshot_height = 110;
mineBlocks(10);
initial_size += 10;
initial_total_coins += 10;
@@ -259,15 +258,30 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Determi
// Coins count is smaller than coins in file
metadata.m_coins_count -= 1;
}));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Wrong hash
+ metadata.m_base_blockhash = uint256::ZERO;
+ }));
+ BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
+ m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
+ // Wrong hash
+ metadata.m_base_blockhash = uint256::ONE;
+ }));
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(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
BOOST_CHECK_EQUAL(
- chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ *chainman.ActiveChainstate().m_from_snapshot_blockhash,
*chainman.SnapshotBlockhash());
+ const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
+ const CBlockIndex* tip = chainman.ActiveTip();
+
+ BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx);
+
// To be checked against later when we try loading a subsequent snapshot.
uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
@@ -336,7 +350,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Determi
// Snapshot blockhash should be unchanged.
BOOST_CHECK_EQUAL(
- chainman.ActiveChainstate().m_from_snapshot_blockhash,
+ *chainman.ActiveChainstate().m_from_snapshot_blockhash,
loaded_snapshot_blockhash);
}
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index ecf9453094..a0c2e76f00 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -135,12 +135,12 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
}
const auto out110 = *ExpectedAssumeutxo(110, *params);
- BOOST_CHECK_EQUAL(out110.hash_serialized, uint256S("76fd7334ac7c1baf57ddc0c626f073a655a35d98a4258cd1382c8cc2b8392e10"));
- BOOST_CHECK_EQUAL(out110.nChainTx, (unsigned int)110);
+ BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618");
+ BOOST_CHECK_EQUAL(out110.nChainTx, 110U);
- const auto out210 = *ExpectedAssumeutxo(210, *params);
- BOOST_CHECK_EQUAL(out210.hash_serialized, uint256S("9c5ed99ef98544b34f8920b6d1802f72ac28ae6e2bd2bd4c316ff10c230df3f2"));
- BOOST_CHECK_EQUAL(out210.nChainTx, (unsigned int)210);
+ const auto out210 = *ExpectedAssumeutxo(200, *params);
+ BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62");
+ BOOST_CHECK_EQUAL(out210.nChainTx, 200U);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 8841a540f2..304cd8feb0 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -44,6 +44,12 @@ public:
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
+class TestDelayedActivationConditionChecker : public TestConditionChecker
+{
+public:
+ int MinActivationHeight(const Consensus::Params& params) const override { return 15000; }
+};
+
class TestAlwaysActiveConditionChecker : public TestConditionChecker
{
public:
@@ -53,8 +59,7 @@ public:
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; }
+ int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::NEVER_ACTIVE; }
};
#define CHECKERS 6
@@ -68,23 +73,27 @@ class VersionBitsTester
// The first one performs all checks, the second only 50%, the third only 25%, etc...
// This is to test whether lack of cached information leads to the same results.
TestConditionChecker checker[CHECKERS];
+ // Another 6 that assume delayed activation
+ TestDelayedActivationConditionChecker checker_delayed[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;
+ int num{1000};
public:
- VersionBitsTester() : num(0) {}
-
VersionBitsTester& Reset() {
+ // Have each group of tests be counted by the 1000s part, starting at 1000
+ num = num - (num % 1000) + 1000;
+
for (unsigned int i = 0; i < vpblock.size(); i++) {
delete vpblock[i];
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
+ checker_delayed[i] = TestDelayedActivationConditionChecker();
checker_always[i] = TestAlwaysActiveConditionChecker();
checker_never[i] = TestNeverActiveConditionChecker();
}
@@ -100,7 +109,7 @@ public:
while (vpblock.size() < height) {
CBlockIndex* pindex = new CBlockIndex();
pindex->nHeight = vpblock.size();
- pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr;
+ pindex->pprev = Tip();
pindex->nTime = nTime;
pindex->nVersion = nVersion;
pindex->BuildSkip();
@@ -109,34 +118,53 @@ public:
return *this;
}
- VersionBitsTester& TestStateSinceHeight(int height) {
+ VersionBitsTester& TestStateSinceHeight(int height)
+ {
+ return TestStateSinceHeight(height, height);
+ }
+
+ VersionBitsTester& TestStateSinceHeight(int height, int height_delayed)
+ {
+ const CBlockIndex* tip = Tip();
for (int i = 0; i < CHECKERS; i++) {
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));
-
- // 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));
+ BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(tip) == height, strprintf("Test %i for StateSinceHeight", num));
+ BOOST_CHECK_MESSAGE(checker_delayed[i].GetStateSinceHeightFor(tip) == height_delayed, strprintf("Test %i for StateSinceHeight (delayed)", num));
+ BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(tip) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
+ BOOST_CHECK_MESSAGE(checker_never[i].GetStateSinceHeightFor(tip) == 0, strprintf("Test %i for StateSinceHeight (never active)", num));
}
}
num++;
return *this;
}
- VersionBitsTester& TestState(ThresholdState exp) {
+ VersionBitsTester& TestState(ThresholdState exp)
+ {
+ return TestState(exp, exp);
+ }
+
+ VersionBitsTester& TestState(ThresholdState exp, ThresholdState exp_delayed)
+ {
+ if (exp != exp_delayed) {
+ // only expected differences are that delayed stays in locked_in longer
+ BOOST_CHECK_EQUAL(exp, ThresholdState::ACTIVE);
+ BOOST_CHECK_EQUAL(exp_delayed, ThresholdState::LOCKED_IN);
+ }
+
+ const CBlockIndex* pindex = Tip();
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
- const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
ThresholdState got = checker[i].GetStateFor(pindex);
+ ThresholdState got_delayed = checker_delayed[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_delayed == exp_delayed, strprintf("Test %i for %s height %d (got %s; delayed case)", num, StateName(exp_delayed), height, StateName(got_delayed)));
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)));
+ BOOST_CHECK_MESSAGE(got_never == ThresholdState::FAILED, strprintf("Test %i for FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
}
}
num++;
@@ -149,7 +177,10 @@ public:
VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
- CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; }
+ // non-delayed should be active; delayed should still be locked in
+ VersionBitsTester& TestActiveDelayed() { return TestState(ThresholdState::ACTIVE, ThresholdState::LOCKED_IN); }
+
+ CBlockIndex* Tip() { return vpblock.empty() ? nullptr : vpblock.back(); }
};
BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
@@ -157,18 +188,19 @@ BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
- // DEFINED -> FAILED
+ // DEFINED -> STARTED after timeout reached -> FAILED
VersionBitsTester().TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
.Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
.Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
- .Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0)
- .Mine(1000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(1000)
- .Mine(1999, TestTime(30001), 0x100).TestFailed().TestStateSinceHeight(1000)
- .Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(1000)
- .Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(1000)
- .Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(1000)
- .Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0) // Timeout and start time reached simultaneously
+ .Mine(1000, TestTime(20000), 0).TestStarted().TestStateSinceHeight(1000) // Hit started, stop signalling
+ .Mine(1999, TestTime(30001), 0).TestStarted().TestStateSinceHeight(1000)
+ .Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(2000) // Hit failed, start signalling again
+ .Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(2000)
+ .Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(2000)
+ .Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(2000)
+ .Mine(4000, TestTime(30006), 0x100).TestFailed().TestStateSinceHeight(2000)
// DEFINED -> STARTED -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
@@ -180,19 +212,19 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
.Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)
- // DEFINED -> STARTED -> FAILED while threshold reached
+ // DEFINED -> STARTED -> LOCKEDIN after timeout reached -> ACTIVE
.Reset().TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
.Mine(2999, TestTime(30000), 0x100).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
- .Mine(3000, TestTime(30000), 0x100).TestFailed().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
- .Mine(3999, TestTime(30001), 0).TestFailed().TestStateSinceHeight(3000)
- .Mine(4000, TestTime(30002), 0).TestFailed().TestStateSinceHeight(3000)
- .Mine(14333, TestTime(30003), 0).TestFailed().TestStateSinceHeight(3000)
- .Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(3000, TestTime(30000), 0x100).TestLockedIn().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
+ .Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
+ .Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
+ .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
- // DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
+ // DEFINED -> STARTED -> LOCKEDIN before timeout -> ACTIVE
.Reset().TestDefined()
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
@@ -202,9 +234,10 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
.Mine(3000, TestTime(29999), 0x200).TestLockedIn().TestStateSinceHeight(3000) // 1 old block (so 900 out of the past 1000)
.Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
- .Mine(4000, TestTime(30002), 0).TestActive().TestStateSinceHeight(4000)
- .Mine(14333, TestTime(30003), 0).TestActive().TestStateSinceHeight(4000)
- .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000) // delayed will not become active until height=15000
+ .Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
+ .Mine(15000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
+ .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
// DEFINED multiple periods -> STARTED multiple periods -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
@@ -214,109 +247,135 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
.Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
.Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(5999, TestTime(20000), 0).TestStarted().TestStateSinceHeight(3000)
.Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
- .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000);
- }
-
- // Sanity checks of version bit deployments
- const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
- const Consensus::Params &mainnetParams = chainParams->GetConsensus();
- for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
- uint32_t bitmask = VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(i));
- // Make sure that no deployment tries to set an invalid bit.
- BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
-
- // Verify that the deployment windows of different deployment using the
- // same bit are disjoint.
- // This test may need modification at such time as a new deployment
- // is proposed that reuses the bit of an activated soft fork, before the
- // end time of that soft fork. (Alternatively, the end time of that
- // activated soft fork could be later changed to be earlier to avoid
- // overlap.)
- for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) {
- if (VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(j)) == bitmask) {
- BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout ||
- mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout);
- }
- }
+ .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000)
+ .Mine(24000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000) // stay in FAILED no matter how much we signal
+ ;
}
}
-BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
+/** Check that ComputeBlockVersion will set the appropriate bit correctly */
+static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
{
- // Check that ComputeBlockVersion will set the appropriate bit correctly
- // on mainnet.
- const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
- const Consensus::Params &mainnetParams = chainParams->GetConsensus();
+ // This implicitly uses versionbitscache, so clear it every time
+ versionbitscache.Clear();
+
+ int64_t bit = params.vDeployments[dep].bit;
+ int64_t nStartTime = params.vDeployments[dep].nStartTime;
+ int64_t nTimeout = params.vDeployments[dep].nTimeout;
+ int min_activation_height = params.vDeployments[dep].min_activation_height;
+
+ // should not be any signalling for first block
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
+
+ // always/never active deployments shouldn't need to be tested further
+ if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE ||
+ nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE)
+ {
+ BOOST_CHECK_EQUAL(min_activation_height, 0);
+ return;
+ }
- // Use the TESTDUMMY deployment for testing purposes.
- int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
- int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime;
- int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout;
+ BOOST_REQUIRE(nStartTime < nTimeout);
+ BOOST_REQUIRE(nStartTime >= 0);
+ BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT);
+ BOOST_REQUIRE(0 <= bit && bit < 32);
+ // Make sure that no deployment tries to set an invalid bit.
+ BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0);
+ BOOST_REQUIRE(min_activation_height >= 0);
+ // Check min_activation_height is on a retarget boundary
+ BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0U);
- assert(nStartTime < nTimeout);
+ const uint32_t bitmask{VersionBitsMask(params, dep)};
+ BOOST_CHECK_EQUAL(bitmask, uint32_t{1} << bit);
// In the first chain, test that the bit is set by CBV until it has failed.
// In the second chain, test the bit is set by CBV while STARTED and
// LOCKED-IN, and then no longer set while ACTIVE.
VersionBitsTester firstChain, secondChain;
- // Start generating blocks before nStartTime
- int64_t nTime = nStartTime - 1;
+ int64_t nTime = nStartTime;
+
+ const CBlockIndex *lastBlock = nullptr;
// Before MedianTimePast of the chain has crossed nStartTime, the bit
// should not be set.
- CBlockIndex *lastBlock = nullptr;
- lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
-
- // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
- for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) {
- lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
- // to be 4, and the bit we're testing happens to be bit 28.
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
- }
- // Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
- // CBV should still not yet set the bit.
- nTime = nStartTime;
- for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; i <= mainnetParams.nMinerConfirmationWindow; i++) {
- lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
+ if (nTime == 0) {
+ // since CBlockIndex::nTime is uint32_t we can't represent any
+ // earlier time, so will transition from DEFINED to STARTED at the
+ // end of the first period by mining blocks at nTime == 0
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ // then we'll keep mining at nStartTime...
+ } else {
+ // use a time 1s earlier than start time to check we stay DEFINED
+ --nTime;
+
+ // Start generating blocks before nStartTime
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+
+ // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
+ for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ }
+ // Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
+ // CBV should still not yet set the bit.
+ nTime = nStartTime;
+ for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
+ }
+ // Next we will advance to the next period and transition to STARTED,
}
- // Advance to the next period and transition to STARTED,
- lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
- uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
- uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3;
+ uint32_t blocksToMine = params.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
+ uint32_t nHeight = params.nMinerConfirmationWindow * 3;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
blocksToMine--;
nTime += 600;
nHeight += 1;
}
- nTime = nTimeout;
- // FAILED is only triggered at the end of a period, so CBV should be setting
- // the bit until the period transition.
- for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) {
+ if (nTimeout != Consensus::BIP9Deployment::NO_TIMEOUT) {
+ // can reach any nTimeout other than NO_TIMEOUT due to earlier BOOST_REQUIRE
+
+ nTime = nTimeout;
+
+ // finish the last period before we start timing out
+ while (nHeight % params.nMinerConfirmationWindow != 0) {
+ lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ nHeight += 1;
+ }
+
+ // FAILED is only triggered at the end of a period, so CBV should be setting
+ // the bit until the period transition.
+ for (uint32_t i = 0; i < params.nMinerConfirmationWindow - 1; i++) {
+ lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
+ nHeight += 1;
+ }
+ // The next block should trigger no longer setting the bit.
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
- nHeight += 1;
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
}
- // The next block should trigger no longer setting the bit.
- lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// On a new chain:
// verify that the bit will be set after lock-in, and then stop being set
@@ -325,26 +384,72 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
- lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
+ lastBlock = secondChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
- lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
+ lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
- lastBlock = secondChain.Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
- lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
-
- // Finally, verify that after a soft fork has activated, CBV no longer uses
- // VERSIONBITS_LAST_OLD_BLOCK_VERSION.
- //BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
+ lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+
+ if (lastBlock->nHeight + 1 < min_activation_height) {
+ // check signalling continues while min_activation_height is not reached
+ lastBlock = secondChain.Mine(min_activation_height - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
+ // then reach min_activation_height, which was already REQUIRE'd to start a new period
+ lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ }
+
+ // Check that we don't signal after activation
+ BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
}
+BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
+{
+ // check that any deployment on any chain can conceivably reach both
+ // ACTIVE and FAILED states in roughly the way we expect
+ for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST}) {
+ const auto chainParams = CreateChainParams(*m_node.args, chain_name);
+ uint32_t chain_all_vbits{0};
+ for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
+ const auto dep = static_cast<Consensus::DeploymentPos>(i);
+ // Check that no bits are re-used (within the same chain). This is
+ // disallowed because the transition to FAILED (on timeout) does
+ // not take precedence over STARTED/LOCKED_IN. So all softforks on
+ // the same bit might overlap, even when non-overlapping start-end
+ // times are picked.
+ const uint32_t dep_mask{VersionBitsMask(chainParams->GetConsensus(), dep)};
+ BOOST_CHECK(!(chain_all_vbits & dep_mask));
+ chain_all_vbits |= dep_mask;
+ check_computeblockversion(chainParams->GetConsensus(), dep);
+ }
+ }
+
+ {
+ // Use regtest/testdummy to ensure we always exercise some
+ // deployment that's not always/never active
+ ArgsManager args;
+ args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999"); // January 1, 2008 - December 31, 2008
+ const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
+ check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
+ }
+
+ {
+ // Use regtest/testdummy to ensure we always exercise the
+ // min_activation_height test, even if we're not using that in a
+ // live deployment
+ ArgsManager args;
+ args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200
+ const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
+ check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
+ }
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 6666e49a2b..a0499fa51f 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -15,6 +15,7 @@
#include <util/readwritefile.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/thread.h>
#include <util/time.h>
#include <deque>
@@ -562,7 +563,7 @@ void TorController::Reconnect()
fs::path TorController::GetPrivateKeyFile()
{
- return GetDataDir() / "onion_v3_private_key";
+ return gArgs.GetDataDirNet() / "onion_v3_private_key";
}
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
@@ -596,7 +597,7 @@ void StartTorControl(CService onion_service_target)
return;
}
- torControlThread = std::thread(&TraceThread<std::function<void()>>, "torcontrol", [onion_service_target] {
+ torControlThread = std::thread(&util::TraceThread, "torcontrol", [onion_service_target] {
TorControlThread(onion_service_target);
});
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 3a08e28c01..762f71feb1 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -16,22 +16,22 @@
#include <stdint.h>
-static const char DB_COIN = 'C';
-static const char DB_COINS = 'c';
-static const char DB_BLOCK_FILES = 'f';
-static const char DB_BLOCK_INDEX = 'b';
+static constexpr uint8_t DB_COIN{'C'};
+static constexpr uint8_t DB_COINS{'c'};
+static constexpr uint8_t DB_BLOCK_FILES{'f'};
+static constexpr uint8_t DB_BLOCK_INDEX{'b'};
-static const char DB_BEST_BLOCK = 'B';
-static const char DB_HEAD_BLOCKS = 'H';
-static const char DB_FLAG = 'F';
-static const char DB_REINDEX_FLAG = 'R';
-static const char DB_LAST_BLOCK = 'l';
+static constexpr uint8_t DB_BEST_BLOCK{'B'};
+static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
+static constexpr uint8_t DB_FLAG{'F'};
+static constexpr uint8_t DB_REINDEX_FLAG{'R'};
+static constexpr uint8_t DB_LAST_BLOCK{'l'};
namespace {
struct CoinEntry {
COutPoint* outpoint;
- char key;
+ uint8_t key;
explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
@@ -143,10 +143,10 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
size_t CCoinsViewDB::EstimateSize() const
{
- return m_db->EstimateSize(DB_COIN, (char)(DB_COIN+1));
+ return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
}
-CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
+CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(gArgs.GetDataDirNet() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
}
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
@@ -155,7 +155,7 @@ bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
if (fReindexing)
- return Write(DB_REINDEX_FLAG, '1');
+ return Write(DB_REINDEX_FLAG, uint8_t{'1'});
else
return Erase(DB_REINDEX_FLAG);
}
@@ -235,14 +235,14 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
}
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
- return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
+ return Write(std::make_pair(DB_FLAG, name), fValue ? uint8_t{'1'} : uint8_t{'0'});
}
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
- char ch;
+ uint8_t ch;
if (!Read(std::make_pair(DB_FLAG, name), ch))
return false;
- fValue = ch == '1';
+ fValue = ch == uint8_t{'1'};
return true;
}
@@ -255,7 +255,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load m_block_index
while (pcursor->Valid()) {
if (ShutdownRequested()) return false;
- std::pair<char, uint256> key;
+ std::pair<uint8_t, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
CDiskBlockIndex diskindex;
if (pcursor->GetValue(diskindex)) {
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 67549fc13d..4413da7ea7 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -18,6 +18,7 @@
#include <validation.h>
#include <validationinterface.h>
+#include <cmath>
#include <optional>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
@@ -514,7 +515,9 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
LockPoints lp = it->GetLockPoints();
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)) {
+ CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this);
+ if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
+ || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, 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);
@@ -919,6 +922,13 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
+ // Check to see if the inputs are made available by another tx in the package.
+ // These Coins would not be available in the underlying CoinsView.
+ if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) {
+ coin = it->second;
+ return true;
+ }
+
// If an entry in the mempool exists, always return that one, as it's guaranteed to never
// conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
// transactions. First checking the underlying cache risks returning a pruned entry instead.
@@ -934,6 +944,13 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
return base->GetCoin(outpoint, coin);
}
+void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx)
+{
+ for (unsigned int n = 0; n < tx->vout.size(); ++n) {
+ m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false));
+ }
+}
+
size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
// Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
diff --git a/src/txmempool.h b/src/txmempool.h
index c3a9bd851d..46b89049bb 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -479,21 +479,21 @@ class CTxMemPool
protected:
const int m_check_ratio; //!< Value n means that 1 times in n we check.
std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
- CBlockPolicyEstimator* minerPolicyEstimator;
+ CBlockPolicyEstimator* const minerPolicyEstimator;
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 int64_t lastRollingFeeUpdate GUARDED_BY(cs);
+ mutable bool blockSinceLastRollingFeeBump GUARDED_BY(cs);
+ mutable double rollingMinimumFeeRate GUARDED_BY(cs); //!< minimum fee to get into the pool, decreases exponentially
mutable Epoch m_epoch GUARDED_BY(cs);
// In-memory counter for external mempool tracking purposes.
// This number is incremented once every time a transaction
// is added or removed from the mempool for any reason.
- mutable uint64_t m_sequence_number{1};
+ mutable uint64_t m_sequence_number GUARDED_BY(cs){1};
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -587,7 +587,7 @@ private:
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs);
- std::map<uint256, CAmount> mapDeltas;
+ std::map<uint256, CAmount> mapDeltas GUARDED_BY(cs);
/** Create a new CTxMemPool.
* Sanity checks will be off by default for performance, because otherwise
@@ -852,7 +852,8 @@ public:
* CCoinsView that brings transactions from a mempool into view.
* It does not check for spendings by memory pool transactions.
* Instead, it provides access to all Coins which are either unspent in the
- * base CCoinsView, or are outputs from any mempool transaction!
+ * base CCoinsView, are outputs from any mempool transaction, or are
+ * tracked temporarily to allow transaction dependencies in package validation.
* This allows transaction replacement to work as expected, as you want to
* have all inputs "available" to check signatures, and any cycles in the
* dependency graph are checked directly in AcceptToMemoryPool.
@@ -862,12 +863,19 @@ public:
*/
class CCoinsViewMemPool : public CCoinsViewBacked
{
+ /**
+ * Coins made available by transactions being validated. Tracking these allows for package
+ * validation, since we can access transaction outputs without submitting them to mempool.
+ */
+ std::unordered_map<COutPoint, Coin, SaltedOutpointHasher> m_temp_added;
protected:
const CTxMemPool& mempool;
public:
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
+ /** Add the coins created by this transaction. */
+ void PackageAddTransaction(const CTransactionRef& tx);
};
/**
diff --git a/src/txorphanage.h b/src/txorphanage.h
index df55cdb3be..e4266e470a 100644
--- a/src/txorphanage.h
+++ b/src/txorphanage.h
@@ -24,9 +24,9 @@ public:
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);
+ bool HaveTx(const GenTxid& gtxid) const LOCKS_EXCLUDED(::g_cs_orphans);
- /** Get an orphan transaction and its orginating peer
+ /** Get an orphan transaction and its originating peer
* (Transaction ref will be nullptr if not found)
*/
std::pair<CTransactionRef, NodeId> GetTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
@@ -38,7 +38,7 @@ public:
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);
+ void EraseForBlock(const CBlock& block) LOCKS_EXCLUDED(::g_cs_orphans);
/** Limit the orphanage to the given maximum */
unsigned int LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index 58424134b0..f8d7a1ece8 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -486,7 +486,7 @@ private:
}
//! Make the data structure consistent with a given point in time:
- //! - REQUESTED annoucements with expiry <= now are turned into COMPLETED.
+ //! - REQUESTED announcements with expiry <= now are turned into COMPLETED.
//! - CANDIDATE_DELAYED announcements with reqtime <= now are turned into CANDIDATE_{READY,BEST}.
//! - CANDIDATE_{READY,BEST} announcements with reqtime > now are turned into CANDIDATE_DELAYED.
void SetTimePoint(std::chrono::microseconds now, std::vector<std::pair<NodeId, GenTxid>>* expired)
diff --git a/src/uint256.h b/src/uint256.h
index fadf2320af..d4917d0eac 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -75,7 +75,7 @@ public:
return &m_data[WIDTH];
}
- unsigned int size() const
+ static constexpr unsigned int size()
{
return sizeof(m_data);
}
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index bd77d74218..bacc3690a2 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -93,8 +93,7 @@ uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip)
jump = DecodeJump(pos, endpos);
if (jump == INVALID) break; // Jump offset straddles EOF
if (bits == 0) break; // No input bits left
- if (pos + jump < pos) break; // overflow
- if (pos + jump >= endpos) break; // Jumping past EOF
+ if (int64_t{jump} >= int64_t{endpos - pos}) break; // Jumping past EOF
if (ip[ip.size() - bits]) {
pos += jump;
}
@@ -156,8 +155,7 @@ bool SanityCheckASMap(const std::vector<bool>& asmap, int bits)
} else if (opcode == Instruction::JUMP) {
uint32_t jump = DecodeJump(pos, endpos);
if (jump == INVALID) return false; // Jump offset straddles EOF
- if (pos + jump < pos) return false; // overflow
- if (pos + jump > endpos) return false; // Jump out of range
+ if (int64_t{jump} > int64_t{endpos - pos}) return false; // Jump out of range
if (bits == 0) return false; // Consuming bits past the end of the input
--bits;
uint32_t jump_offset = pos - begin + jump;
diff --git a/src/util/hash_type.h b/src/util/hash_type.h
new file mode 100644
index 0000000000..13b831cf19
--- /dev/null
+++ b/src/util/hash_type.h
@@ -0,0 +1,72 @@
+// 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_HASH_TYPE_H
+#define BITCOIN_UTIL_HASH_TYPE_H
+
+template <typename HashType>
+class BaseHash
+{
+protected:
+ HashType m_hash;
+
+public:
+ BaseHash() : m_hash() {}
+ explicit BaseHash(const HashType& in) : m_hash(in) {}
+
+ unsigned char* begin()
+ {
+ return m_hash.begin();
+ }
+
+ const unsigned char* begin() const
+ {
+ return m_hash.begin();
+ }
+
+ unsigned char* end()
+ {
+ return m_hash.end();
+ }
+
+ const unsigned char* end() const
+ {
+ return m_hash.end();
+ }
+
+ operator std::vector<unsigned char>() const
+ {
+ return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
+ }
+
+ std::string ToString() const
+ {
+ return m_hash.ToString();
+ }
+
+ bool operator==(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash == other.m_hash;
+ }
+
+ bool operator!=(const BaseHash<HashType>& other) const noexcept
+ {
+ return !(m_hash == other.m_hash);
+ }
+
+ bool operator<(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash < other.m_hash;
+ }
+
+ size_t size() const
+ {
+ return m_hash.size();
+ }
+
+ unsigned char* data() { return m_hash.data(); }
+ const unsigned char* data() const { return m_hash.data(); }
+};
+
+#endif // BITCOIN_UTIL_HASH_TYPE_H
diff --git a/src/util/serfloat.cpp b/src/util/serfloat.cpp
new file mode 100644
index 0000000000..8edca924cd
--- /dev/null
+++ b/src/util/serfloat.cpp
@@ -0,0 +1,64 @@
+// 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/serfloat.h>
+
+#include <cmath>
+#include <limits>
+
+double DecodeDouble(uint64_t v) noexcept {
+ static constexpr double NANVAL = std::numeric_limits<double>::quiet_NaN();
+ static constexpr double INFVAL = std::numeric_limits<double>::infinity();
+ double sign = 1.0;
+ if (v & 0x8000000000000000) {
+ sign = -1.0;
+ v ^= 0x8000000000000000;
+ }
+ // Zero
+ if (v == 0) return copysign(0.0, sign);
+ // Infinity
+ if (v == 0x7ff0000000000000) return copysign(INFVAL, sign);
+ // Other numbers
+ int exp = (v & 0x7FF0000000000000) >> 52;
+ uint64_t man = v & 0xFFFFFFFFFFFFF;
+ if (exp == 2047) {
+ // NaN
+ return NANVAL;
+ } else if (exp == 0) {
+ // Subnormal
+ return copysign(ldexp((double)man, -1074), sign);
+ } else {
+ // Normal
+ return copysign(ldexp((double)(man + 0x10000000000000), -1075 + exp), sign);
+ }
+}
+
+uint64_t EncodeDouble(double f) noexcept {
+ int cls = std::fpclassify(f);
+ uint64_t sign = 0;
+ if (copysign(1.0, f) == -1.0) {
+ f = -f;
+ sign = 0x8000000000000000;
+ }
+ // Zero
+ if (cls == FP_ZERO) return sign;
+ // Infinity
+ if (cls == FP_INFINITE) return sign | 0x7ff0000000000000;
+ // NaN
+ if (cls == FP_NAN) return 0x7ff8000000000000;
+ // Other numbers
+ int exp;
+ uint64_t man = std::round(std::frexp(f, &exp) * 9007199254740992.0);
+ if (exp < -1021) {
+ // Too small to represent, encode 0
+ if (exp < -1084) return sign;
+ // Subnormal numbers
+ return sign | (man >> (-1021 - exp));
+ } else {
+ // Too big to represent, encode infinity
+ if (exp > 1024) return sign | 0x7ff0000000000000;
+ // Normal numbers
+ return sign | (((uint64_t)(1022 + exp)) << 52) | (man & 0xFFFFFFFFFFFFF);
+ }
+}
diff --git a/src/util/serfloat.h b/src/util/serfloat.h
new file mode 100644
index 0000000000..4d912b0176
--- /dev/null
+++ b/src/util/serfloat.h
@@ -0,0 +1,16 @@
+// 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_SERFLOAT_H
+#define BITCOIN_UTIL_SERFLOAT_H
+
+#include <stdint.h>
+
+/* Encode a double using the IEEE 754 binary64 format. All NaNs are encoded as x86/ARM's
+ * positive quiet NaN with payload 0. */
+uint64_t EncodeDouble(double f) noexcept;
+/* Reverse operation of DecodeDouble. DecodeDouble(EncodeDouble(f))==f unless isnan(f). */
+double DecodeDouble(uint64_t v) noexcept;
+
+#endif // BITCOIN_UTIL_SERFLOAT_H
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 0bc9795db3..b6c2a47434 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -179,7 +179,7 @@ void Sock::SendComplete(const std::string& data,
// 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);
+ (void)Wait(wait_time, SEND);
}
}
@@ -262,7 +262,7 @@ std::string Sock::RecvUntilTerminator(uint8_t terminator,
// 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);
+ (void)Wait(wait_time, RECV);
}
}
diff --git a/src/util/sock.h b/src/util/sock.h
index c4ad0cbc43..59cc8c0b1d 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -64,7 +64,7 @@ public:
* Get the value of the contained socket.
* @return socket or INVALID_SOCKET if empty
*/
- virtual SOCKET Get() const;
+ [[nodiscard]] virtual SOCKET Get() const;
/**
* Get the value of the contained socket and drop ownership. It will not be closed by the
@@ -82,26 +82,29 @@ public:
* 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;
+ [[nodiscard]] 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;
+ [[nodiscard]] virtual ssize_t Recv(void* buf, size_t len, int flags) const;
/**
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
- virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
+ [[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
/**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
*/
- virtual int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const;
+ [[nodiscard]] virtual int GetSockOpt(int level,
+ int opt_name,
+ void* opt_val,
+ socklen_t* opt_len) const;
using Event = uint8_t;
@@ -124,9 +127,9 @@ public:
* 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;
+ [[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout,
+ Event requested,
+ Event* occurred = nullptr) const;
/* Higher level, convenience, methods. These may throw. */
@@ -154,17 +157,17 @@ public:
* @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;
+ [[nodiscard]] 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.
+ * @param[out] errmsg The error string, if the socket has been disconnected.
* @return true if connected
*/
- virtual bool IsConnected(std::string& errmsg) const;
+ [[nodiscard]] virtual bool IsConnected(std::string& errmsg) const;
protected:
/**
diff --git a/src/util/spanparsing.cpp b/src/util/spanparsing.cpp
index 0f68254f2c..e2e2782bec 100644
--- a/src/util/spanparsing.cpp
+++ b/src/util/spanparsing.cpp
@@ -34,11 +34,11 @@ Span<const char> Expr(Span<const char>& sp)
int level = 0;
auto it = sp.begin();
while (it != sp.end()) {
- if (*it == '(') {
+ if (*it == '(' || *it == '{') {
++level;
- } else if (level && *it == ')') {
+ } else if (level && (*it == ')' || *it == '}')) {
--level;
- } else if (level == 0 && (*it == ')' || *it == ',')) {
+ } else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) {
break;
}
++it;
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 4734de3e0b..f514613f0d 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -593,13 +593,14 @@ std::string Capitalize(std::string str)
std::string HexStr(const Span<const uint8_t> s)
{
- std::string rv;
+ std::string rv(s.size() * 2, '\0');
static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- rv.reserve(s.size() * 2);
- for (uint8_t v: s) {
- rv.push_back(hexmap[v >> 4]);
- rv.push_back(hexmap[v & 15]);
+ auto it = rv.begin();
+ for (uint8_t v : s) {
+ *it++ = hexmap[v >> 4];
+ *it++ = hexmap[v & 15];
}
+ assert(it == rv.end());
return rv;
}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 0b83a76504..13ccf7463e 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -235,6 +235,19 @@ static bool CheckValid(const std::string& key, const util::SettingsValue& val, u
return true;
}
+namespace {
+fs::path StripRedundantLastElementsOfPath(const fs::path& path)
+{
+ auto result = path;
+ while (result.filename().string() == ".") {
+ result = result.parent_path();
+ }
+
+ assert(fs::equivalent(result, path));
+ return result;
+}
+} // namespace
+
// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
// #include class definitions for all members.
// For example, m_settings has an internal dependency on univalue.
@@ -352,15 +365,16 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
m_settings.command_line_options[key].push_back(value);
}
- // we do not allow -includeconf from command line
- bool success = true;
+ // we do not allow -includeconf from command line, only -noincludeconf
if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
- for (const auto& include : util::SettingsSpan(*includes)) {
- error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n";
- success = false;
+ const util::SettingsSpan values{*includes};
+ // Range may be empty if -noincludeconf was passed
+ if (!values.empty()) {
+ error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
+ return false; // pick first value as example
}
}
- return success;
+ return true;
}
std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
@@ -375,6 +389,72 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
return std::nullopt;
}
+const fs::path& ArgsManager::GetBlocksDirPath() const
+{
+ LOCK(cs_args);
+ fs::path& path = m_cached_blocks_path;
+
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
+
+ if (IsArgSet("-blocksdir")) {
+ path = fs::system_complete(GetArg("-blocksdir", ""));
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDataDirBase();
+ }
+
+ path /= BaseParams().DataDir();
+ path /= "blocks";
+ fs::create_directories(path);
+ path = StripRedundantLastElementsOfPath(path);
+ return path;
+}
+
+const fs::path& ArgsManager::GetDataDir(bool net_specific) const
+{
+ LOCK(cs_args);
+ fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
+
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) return path;
+
+ std::string datadir = GetArg("-datadir", "");
+ if (!datadir.empty()) {
+ path = fs::system_complete(datadir);
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDefaultDataDir();
+ }
+ if (net_specific)
+ path /= BaseParams().DataDir();
+
+ if (fs::create_directories(path)) {
+ // This is the first run, create wallets subdirectory too
+ fs::create_directories(path / "wallets");
+ }
+
+ path = StripRedundantLastElementsOfPath(path);
+ return path;
+}
+
+void ArgsManager::ClearPathCache()
+{
+ LOCK(cs_args);
+
+ m_cached_datadir_path = fs::path();
+ m_cached_network_datadir_path = fs::path();
+ m_cached_blocks_path = fs::path();
+}
+
std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
{
Command ret;
@@ -434,7 +514,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
}
if (filepath) {
std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME);
- *filepath = fsbridge::AbsPathJoin(GetDataDir(/* net_specific= */ true), temp ? settings + ".tmp" : settings);
+ *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
}
return true;
}
@@ -723,96 +803,12 @@ fs::path GetDefaultDataDir()
#endif
}
-namespace {
-fs::path StripRedundantLastElementsOfPath(const fs::path& path)
-{
- auto result = path;
- while (result.filename().string() == ".") {
- result = result.parent_path();
- }
-
- assert(fs::equivalent(result, path));
- return result;
-}
-} // namespace
-
-static fs::path g_blocks_path_cache_net_specific;
-static fs::path pathCached;
-static fs::path pathCachedNetSpecific;
-static RecursiveMutex csPathCached;
-
-const fs::path &GetBlocksDir()
-{
- LOCK(csPathCached);
- fs::path &path = g_blocks_path_cache_net_specific;
-
- // Cache the path to avoid calling fs::create_directories on every call of
- // this function
- if (!path.empty()) return path;
-
- if (gArgs.IsArgSet("-blocksdir")) {
- path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
- if (!fs::is_directory(path)) {
- path = "";
- return path;
- }
- } else {
- path = GetDataDir(false);
- }
-
- path /= BaseParams().DataDir();
- path /= "blocks";
- fs::create_directories(path);
- path = StripRedundantLastElementsOfPath(path);
- return path;
-}
-
-const fs::path &GetDataDir(bool fNetSpecific)
-{
- LOCK(csPathCached);
- fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
-
- // Cache the path to avoid calling fs::create_directories on every call of
- // this function
- if (!path.empty()) return path;
-
- std::string datadir = gArgs.GetArg("-datadir", "");
- if (!datadir.empty()) {
- path = fs::system_complete(datadir);
- if (!fs::is_directory(path)) {
- path = "";
- return path;
- }
- } else {
- path = GetDefaultDataDir();
- }
- if (fNetSpecific)
- path /= BaseParams().DataDir();
-
- if (fs::create_directories(path)) {
- // This is the first run, create wallets subdirectory too
- fs::create_directories(path / "wallets");
- }
-
- path = StripRedundantLastElementsOfPath(path);
- return path;
-}
-
bool CheckDataDirOption()
{
std::string datadir = gArgs.GetArg("-datadir", "");
return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
}
-void ClearDatadirCache()
-{
- LOCK(csPathCached);
-
- pathCached = fs::path();
- pathCachedNetSpecific = fs::path();
- g_blocks_path_cache_net_specific = fs::path();
-}
-
fs::path GetConfigFile(const std::string& confPath)
{
return AbsPathForConfigVal(fs::path(confPath), false);
@@ -971,7 +967,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
// If datadir is changed in .conf file:
- ClearDatadirCache();
+ gArgs.ClearPathCache();
if (!CheckDataDirOption()) {
error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
return false;
@@ -1361,7 +1357,7 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
if (path.is_absolute()) {
return path;
}
- return fsbridge::AbsPathJoin(GetDataDir(net_specific), path);
+ return fsbridge::AbsPathJoin(net_specific ? gArgs.GetDataDirNet() : gArgs.GetDataDirBase(), path);
}
void ScheduleBatchPriority()
diff --git a/src/util/system.h b/src/util/system.h
index 29657e56e2..c4317c62d0 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -22,7 +22,6 @@
#include <sync.h>
#include <tinyformat.h>
#include <util/settings.h>
-#include <util/threadnames.h>
#include <util/time.h>
#include <any>
@@ -91,13 +90,8 @@ void ReleaseDirectoryLocks();
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
-// The blocks directory is always net specific.
-const fs::path &GetBlocksDir();
-const fs::path &GetDataDir(bool fNetSpecific = true);
// Return true if -datadir option points to a valid directory or is not specified.
bool CheckDataDirOption();
-/** Tests only */
-void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
@@ -124,7 +118,7 @@ UniValue RunCommandParseJSON(const std::string& str_command, const std::string&
* the datadir if they are not absolute.
*
* @param path The path to be conditionally prefixed with datadir.
- * @param net_specific Forwarded to GetDataDir().
+ * @param net_specific Use network specific datadir variant
* @return The normalized path.
*/
fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true);
@@ -200,6 +194,9 @@ protected:
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);
+ mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
+ mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
+ mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
@@ -264,6 +261,34 @@ public:
std::optional<const Command> GetCommand() const;
/**
+ * Get blocks directory path
+ *
+ * @return Blocks path which is network specific
+ */
+ const fs::path& GetBlocksDirPath() const;
+
+ /**
+ * Get data directory path
+ *
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ * @post Returned directory path is created unless it is empty
+ */
+ const fs::path& GetDataDirBase() const { return GetDataDir(false); }
+
+ /**
+ * Get data directory path with appended network identifier
+ *
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ * @post Returned directory path is created unless it is empty
+ */
+ const fs::path& GetDataDirNet() const { return GetDataDir(true); }
+
+ /**
+ * Clear cached directory paths
+ */
+ void ClearPathCache();
+
+ /**
* Return a vector of strings of the given argument
*
* @param strArg Argument to get (e.g. "-foo")
@@ -418,6 +443,15 @@ public:
void LogArgs() const;
private:
+ /**
+ * Get data directory path
+ *
+ * @param net_specific Append network identifier to the returned path
+ * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
+ * @post Returned directory path is created unless it is empty
+ */
+ const fs::path& GetDataDir(bool net_specific) const;
+
// Helper function for LogArgs().
void logArgsPrefix(
const std::string& prefix,
@@ -458,28 +492,6 @@ std::string HelpMessageOpt(const std::string& option, const std::string& message
*/
int GetNumCores();
-/**
- * .. and a wrapper that just calls func once
- */
-template <typename Callable> void TraceThread(const char* name, Callable func)
-{
- util::ThreadRename(name);
- try
- {
- LogPrintf("%s thread start\n", name);
- func();
- LogPrintf("%s thread exit\n", name);
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, name);
- throw;
- }
- catch (...) {
- PrintExceptionContinue(nullptr, name);
- throw;
- }
-}
-
std::string CopyrightHolders(const std::string& strPrefix);
/**
diff --git a/src/util/thread.cpp b/src/util/thread.cpp
new file mode 100644
index 0000000000..14be668685
--- /dev/null
+++ b/src/util/thread.cpp
@@ -0,0 +1,27 @@
+// 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/thread.h>
+
+#include <logging.h>
+#include <util/system.h>
+#include <util/threadnames.h>
+
+#include <exception>
+
+void util::TraceThread(const char* thread_name, std::function<void()> thread_func)
+{
+ util::ThreadRename(thread_name);
+ try {
+ LogPrintf("%s thread start\n", thread_name);
+ thread_func();
+ LogPrintf("%s thread exit\n", thread_name);
+ } catch (const std::exception& e) {
+ PrintExceptionContinue(&e, thread_name);
+ throw;
+ } catch (...) {
+ PrintExceptionContinue(nullptr, thread_name);
+ throw;
+ }
+}
diff --git a/src/util/thread.h b/src/util/thread.h
new file mode 100644
index 0000000000..ca2eccc0c3
--- /dev/null
+++ b/src/util/thread.h
@@ -0,0 +1,18 @@
+// 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_THREAD_H
+#define BITCOIN_UTIL_THREAD_H
+
+#include <functional>
+
+namespace util {
+/**
+ * A wrapper for do-something-once thread functions.
+ */
+void TraceThread(const char* thread_name, std::function<void()> thread_func);
+
+} // namespace util
+
+#endif // BITCOIN_UTIL_THREAD_H
diff --git a/src/util/time.cpp b/src/util/time.cpp
index e6f0986a39..eda710b12c 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -124,7 +124,7 @@ int64_t GetTimeMicros()
return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
}
-int64_t GetSystemTimeInSeconds()
+int64_t GetTimeSeconds()
{
return int64_t{GetSystemTime<std::chrono::seconds>().count()};
}
diff --git a/src/util/time.h b/src/util/time.h
index 7ebcaaa339..4aee01967d 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -39,7 +39,7 @@ inline double CountSecondsDouble(SecondsDouble t) { return t.count(); }
/**
* DEPRECATED
- * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
+ * Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
*/
int64_t GetTime();
@@ -48,7 +48,7 @@ int64_t GetTimeMillis();
/** Returns the system time (not mockable) */
int64_t GetTimeMicros();
/** Returns the system time (not mockable) */
-int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable
+int64_t GetTimeSeconds(); // Like GetTime(), but not mockable
/**
* DEPRECATED
diff --git a/src/validation.cpp b/src/validation.cpp
index 19363c0efb..e6e6aadb17 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -21,6 +21,7 @@
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
+#include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/ui_interface.h>
#include <policy/policy.h>
@@ -41,6 +42,7 @@
#include <uint256.h>
#include <undo.h>
#include <util/check.h> // For NDEBUG compile time check
+#include <util/hasher.h>
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/strencodings.h>
@@ -49,6 +51,7 @@
#include <validationinterface.h>
#include <warnings.h>
+#include <numeric>
#include <optional>
#include <string>
@@ -65,10 +68,6 @@
static const unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT = 10000;
/** Maximum kilobytes for transactions to store for processing during reorg */
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
-/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
-static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
-/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
-static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
/** Time to wait between writing blocks/block index to disk. */
static constexpr std::chrono::hours DATABASE_WRITE_INTERVAL{1};
/** Time to wait between flushing chainstate to disk. */
@@ -134,14 +133,9 @@ Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
bool g_parallel_script_checks{false};
-std::atomic_bool fImporting(false);
-std::atomic_bool fReindex(false);
-bool fHavePruned = false;
-bool fPruneMode = false;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
-uint64_t nPruneTarget = 0;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
uint256 hashAssumeValid;
@@ -152,22 +146,17 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
// Internal stuff
namespace {
CBlockIndex* pindexBestInvalid = nullptr;
-
- RecursiveMutex cs_LastBlockFile;
- std::vector<CBlockFileInfo> vinfoBlockFile;
- int nLastBlockFile = 0;
- /** Global flag to indicate we should check to see if there are
- * block/undo files that should be deleted. Set on startup
- * or if we allocate more file space when we're in prune mode
- */
- bool fCheckForPruning = false;
-
- /** Dirty block index entries. */
- std::set<CBlockIndex*> setDirtyBlockIndex;
-
- /** Dirty block file entries. */
- std::set<int> setDirtyFileInfo;
-} // anon namespace
+} // namespace
+
+// Internal stuff from blockstorage ...
+extern RecursiveMutex cs_LastBlockFile;
+extern std::vector<CBlockFileInfo> vinfoBlockFile;
+extern int nLastBlockFile;
+extern bool fCheckForPruning;
+extern std::set<CBlockIndex*> setDirtyBlockIndex;
+extern std::set<int> setDirtyFileInfo;
+void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
+// ... TODO move fully to blockstorage
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
@@ -204,9 +193,6 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
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 CBlockIndex* active_chain_tip, const CTransaction &tx, int flags)
{
@@ -261,18 +247,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
return true;
}
-bool CheckSequenceLocks(CChainState& active_chainstate,
- const CTxMemPool& pool,
+bool CheckSequenceLocks(CBlockIndex* tip,
+ const CCoinsView& coins_view,
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 = active_chainstate.m_chain.Tip();
assert(tip != nullptr);
CBlockIndex index;
@@ -292,14 +273,12 @@ bool CheckSequenceLocks(CChainState& active_chainstate,
lockPair.second = lp->time;
}
else {
- // 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++) {
const CTxIn& txin = tx.vin[txinIndex];
Coin coin;
- if (!viewMemPool.GetCoin(txin.prevout, coin)) {
+ if (!coins_view.GetCoin(txin.prevout, coin)) {
return error("%s: Missing input", __func__);
}
if (coin.nHeight == MEMPOOL_HEIGHT) {
@@ -493,11 +472,20 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
+ /** Disable BIP125 RBFing; disallow all conflicts with mempool transactions. */
+ const bool disallow_mempool_conflicts;
};
// Single transaction acceptance
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * Multiple transaction acceptance. Transactions may or may not be interdependent,
+ * but must not conflict with each other. Parents must come before children if any
+ * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
+ */
+ PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
private:
// All the intermediate state that gets passed between the various levels
// of checking a given transaction.
@@ -654,7 +642,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
break;
}
}
- if (fReplacementOptOut) {
+ if (fReplacementOptOut || args.disallow_mempool_conflicts) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
@@ -702,10 +690,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept BIP68 sequence locked transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
- // CoinsViewCache instead of create its own
+ // Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
+ // backend was removed, it no longer pulls coins from the mempool.
assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
- if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+ if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
@@ -1061,7 +1049,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
Workspace ws(ptx);
- if (!PreChecks(args, ws)) return MempoolAcceptResult(ws.m_state);
+ if (!PreChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
// Only compute the precomputed transaction data if we need to verify
// scripts (ie, other policy checks pass). We perform the inexpensive
@@ -1069,20 +1057,121 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata;
- if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
+ if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state);
- if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
+ if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state);
// Tx was accepted, but not added
if (args.m_test_accept) {
- return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
}
- if (!Finalize(args, ws)) return MempoolAcceptResult(ws.m_state);
+ if (!Finalize(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
- return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+}
+
+PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args)
+{
+ AssertLockHeld(cs_main);
+
+ PackageValidationState package_state;
+ const unsigned int package_count = txns.size();
+
+ // These context-free package limits can be checked before taking the mempool lock.
+ if (package_count > MAX_PACKAGE_COUNT) {
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
+ return PackageMempoolAcceptResult(package_state, {});
+ }
+
+ const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
+ [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
+ // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
+ if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
+ return PackageMempoolAcceptResult(package_state, {});
+ }
+
+ // Construct workspaces and check package policies.
+ std::vector<Workspace> workspaces{};
+ workspaces.reserve(package_count);
+ {
+ std::unordered_set<uint256, SaltedTxidHasher> later_txids;
+ std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
+ [](const auto& tx) { return tx->GetHash(); });
+ // Require the package to be sorted in order of dependency, i.e. parents appear before children.
+ // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
+ // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
+ // spend nonexistent coins).
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (later_txids.find(input.prevout.hash) != later_txids.end()) {
+ // The parent is a subsequent transaction in the package.
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
+ return PackageMempoolAcceptResult(package_state, {});
+ }
+ }
+ later_txids.erase(tx->GetHash());
+ workspaces.emplace_back(Workspace(tx));
+ }
+ }
+ std::map<const uint256, const MempoolAcceptResult> results;
+ {
+ // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
+ std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
+ // This input is also present in another tx in the package.
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
+ return PackageMempoolAcceptResult(package_state, {});
+ }
+ }
+ // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
+ // catch duplicate inputs within a single tx. This is a more severe, consensus error,
+ // and we want to report that from CheckTransaction instead.
+ std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
+ [](const auto& input) { return input.prevout; });
+ }
+ }
+
+ LOCK(m_pool.cs);
+
+ // Do all PreChecks first and fail fast to avoid running expensive script checks when unnecessary.
+ for (Workspace& ws : workspaces) {
+ if (!PreChecks(args, ws)) {
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
+ // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
+ results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+ }
+ // Make the coins created by this transaction available for subsequent transactions in the
+ // package to spend. Since we already checked conflicts in the package and RBFs are
+ // impossible, we don't need to track the coins spent. Note that this logic will need to be
+ // updated if RBFs in packages are allowed in the future.
+ assert(args.disallow_mempool_conflicts);
+ m_viewmempool.PackageAddTransaction(ws.m_ptx);
+ }
+
+ for (Workspace& ws : workspaces) {
+ PrecomputedTransactionData txdata;
+ if (!PolicyScriptChecks(args, ws, txdata)) {
+ // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
+ package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
+ results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+ }
+ if (args.m_test_accept) {
+ // When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are
+ // no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks).
+ results.emplace(ws.m_ptx->GetWitnessHash(),
+ MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees));
+ }
+ }
+
+ return PackageMempoolAcceptResult(package_state, std::move(results));
}
} // anon namespace
@@ -1095,7 +1184,8 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
+ MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
+ test_accept, /* disallow_mempool_conflicts */ false };
assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
@@ -1121,6 +1211,29 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
+PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
+ const Package& package, bool test_accept)
+{
+ AssertLockHeld(cs_main);
+ assert(test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC).
+ assert(!package.empty());
+ assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
+
+ std::vector<COutPoint> coins_to_uncache;
+ const CChainParams& chainparams = Params();
+ MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
+ test_accept, /* disallow_mempool_conflicts */ true };
+ assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
+
+ // Uncache coins pertaining to transactions that were not submitted to the mempool.
+ // Ensure the cache is still within its size limits.
+ for (const COutPoint& hashTx : coins_to_uncache) {
+ active_chainstate.CoinsTip().Uncache(hashTx);
+ }
+ return result;
+}
+
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
{
LOCK(cs_main);
@@ -1148,123 +1261,6 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
return nullptr;
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// CBlock and CBlockIndex
-//
-
-static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
-{
- // Open history file to append
- CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
- if (fileout.IsNull())
- return error("WriteBlockToDisk: OpenBlockFile failed");
-
- // Write index header
- unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
- fileout << messageStart << nSize;
-
- // Write block
- long fileOutPos = ftell(fileout.Get());
- if (fileOutPos < 0)
- return error("WriteBlockToDisk: ftell failed");
- pos.nPos = (unsigned int)fileOutPos;
- fileout << block;
-
- return true;
-}
-
-bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
-{
- block.SetNull();
-
- // Open history file to read
- CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
- if (filein.IsNull())
- return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
-
- // Read block
- try {
- filein >> block;
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
- }
-
- // Check the header
- if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
- return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
-
- // Signet only: check block solution
- if (consensusParams.signet_blocks && !CheckSignetBlockSolution(block, consensusParams)) {
- return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
- }
-
- return true;
-}
-
-bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
-{
- FlatFilePos blockPos;
- {
- LOCK(cs_main);
- blockPos = pindex->GetBlockPos();
- }
-
- if (!ReadBlockFromDisk(block, blockPos, consensusParams))
- return false;
- if (block.GetHash() != pindex->GetBlockHash())
- return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
- pindex->ToString(), pindex->GetBlockPos().ToString());
- return true;
-}
-
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start)
-{
- FlatFilePos hpos = pos;
- hpos.nPos -= 8; // Seek back 8 bytes for meta header
- CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
- if (filein.IsNull()) {
- return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
- }
-
- try {
- CMessageHeader::MessageStartChars blk_start;
- unsigned int blk_size;
-
- filein >> blk_start >> blk_size;
-
- if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
- return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
- HexStr(blk_start),
- HexStr(message_start));
- }
-
- if (blk_size > MAX_SIZE) {
- return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
- blk_size, MAX_SIZE);
- }
-
- block.resize(blk_size); // Zeroing of memory is intentional here
- filein.read((char*)block.data(), blk_size);
- } catch(const std::exception& e) {
- return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
- }
-
- return true;
-}
-
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
-{
- FlatFilePos block_pos;
- {
- LOCK(cs_main);
- block_pos = pindex->GetBlockPos();
- }
-
- return ReadRawBlockFromDisk(block, block_pos, message_start);
-}
-
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
@@ -1283,7 +1279,7 @@ CoinsViews::CoinsViews(
size_t cache_size_bytes,
bool in_memory,
bool should_wipe) : m_dbview(
- GetDataDir() / ldb_name, cache_size_bytes, in_memory, should_wipe),
+ gArgs.GetDataDirNet() / ldb_name, cache_size_bytes, in_memory, should_wipe),
m_catcherview(&m_dbview) {}
void CoinsViews::InitCache()
@@ -1291,7 +1287,7 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash)
+CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_blockman(blockman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
@@ -1302,8 +1298,8 @@ void CChainState::InitCoinsDB(
bool should_wipe,
std::string leveldb_name)
{
- if (!m_from_snapshot_blockhash.IsNull()) {
- leveldb_name += "_" + m_from_snapshot_blockhash.ToString();
+ if (m_from_snapshot_blockhash) {
+ leveldb_name += "_" + m_from_snapshot_blockhash->ToString();
}
m_coins_views = std::make_unique<CoinsViews>(
@@ -1405,8 +1401,9 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
}
// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
-// which does its own setBlockIndexCandidates manageent.
-void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) {
+// which does its own setBlockIndexCandidates management.
+void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
+{
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_failed_blocks.insert(pindex);
@@ -1578,78 +1575,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
return true;
}
-static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
-{
- // Open history file to append
- CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
- if (fileout.IsNull())
- return error("%s: OpenUndoFile failed", __func__);
-
- // Write index header
- unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion());
- fileout << messageStart << nSize;
-
- // Write undo data
- long fileOutPos = ftell(fileout.Get());
- if (fileOutPos < 0)
- return error("%s: ftell failed", __func__);
- pos.nPos = (unsigned int)fileOutPos;
- fileout << blockundo;
-
- // calculate & write checksum
- CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
- hasher << hashBlock;
- hasher << blockundo;
- fileout << hasher.GetHash();
-
- return true;
-}
-
-bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
-{
- FlatFilePos pos = pindex->GetUndoPos();
- if (pos.IsNull()) {
- return error("%s: no undo data available", __func__);
- }
-
- // Open history file to read
- CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
- if (filein.IsNull())
- return error("%s: OpenUndoFile failed", __func__);
-
- // Read block
- uint256 hashChecksum;
- CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
- try {
- verifier << pindex->pprev->GetBlockHash();
- verifier >> blockundo;
- filein >> hashChecksum;
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
- }
-
- // Verify checksum
- if (hashChecksum != verifier.GetHash())
- return error("%s: Checksum mismatch", __func__);
-
- return true;
-}
-
-/** Abort with a message */
-static bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str())
-{
- SetMiscWarning(Untranslated(strMessage));
- LogPrintf("*** %s\n", strMessage);
- if (user_message.empty()) {
- user_message = _("A fatal internal error occurred, see debug.log for details");
- }
- AbortError(user_message);
- StartShutdown();
- return false;
-}
-
-static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str())
+bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage)
{
AbortNode(strMessage, userMessage);
return state.Error(strMessage);
@@ -1749,55 +1675,6 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
-static void FlushUndoFile(int block_file, bool finalize = false)
-{
- FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
- if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
- AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
- }
-}
-
-static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
-{
- LOCK(cs_LastBlockFile);
- FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
- if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
- AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
- }
- // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
- // e.g. during IBD or a sync after a node going offline
- if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
-}
-
-static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
-
-static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
-{
- // Write undo information to disk
- if (pindex->GetUndoPos().IsNull()) {
- FlatFilePos _pos;
- if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40))
- return error("ConnectBlock(): FindUndoPos failed");
- if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
- return AbortNode(state, "Failed to write undo data");
- // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
- // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
- // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
- // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
- // the FindBlockPos function
- if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
- FlushUndoFile(_pos.nFile, true);
- }
-
- // update nUndoPos in block index
- pindex->nUndoPos = _pos.nPos;
- pindex->nStatus |= BLOCK_HAVE_UNDO;
- setDirtyBlockIndex.insert(pindex);
- }
-
- return true;
-}
-
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void StartScriptCheckWorkerThreads(int threads_num)
@@ -1943,8 +1820,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// may have let in a block that violates the rule prior to updating the
// software, and we would NOT be enforcing the rule here. Fully solving
// upgrade from one software version to the next after a consensus rule
- // change is potentially tricky and issue-specific (see RewindBlockIndex()
- // for one general approach that was used for BIP 141 deployment).
+ // change is potentially tricky and issue-specific (see NeedsRedownload()
+ // for one approach that was used for BIP 141 deployment).
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
@@ -2333,7 +2210,7 @@ bool CChainState::FlushStateToDisk(
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
- if (!CheckDiskSpace(GetBlocksDir())) {
+ if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) {
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
{
@@ -2381,7 +2258,7 @@ bool CChainState::FlushStateToDisk(
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
+ if (!CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
@@ -3231,83 +3108,6 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
}
}
-static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
-{
- LOCK(cs_LastBlockFile);
-
- unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
- if (vinfoBlockFile.size() <= nFile) {
- vinfoBlockFile.resize(nFile + 1);
- }
-
- bool finalize_undo = false;
- if (!fKnown) {
- 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
- 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);
- }
- }
- pos.nFile = nFile;
- pos.nPos = vinfoBlockFile[nFile].nSize;
- }
-
- if ((int)nFile != nLastBlockFile) {
- if (!fKnown) {
- LogPrint(BCLog::VALIDATION, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
- }
- FlushBlockFile(!fKnown, finalize_undo);
- nLastBlockFile = nFile;
- }
-
- vinfoBlockFile[nFile].AddBlock(nHeight, nTime);
- if (fKnown)
- vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize);
- else
- vinfoBlockFile[nFile].nSize += nAddSize;
-
- if (!fKnown) {
- bool out_of_space;
- size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
- if (out_of_space) {
- return AbortNode("Disk space is too low!", _("Disk space is too low!"));
- }
- if (bytes_allocated != 0 && fPruneMode) {
- fCheckForPruning = true;
- }
- }
-
- setDirtyFileInfo.insert(nFile);
- return true;
-}
-
-static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize)
-{
- pos.nFile = nFile;
-
- LOCK(cs_LastBlockFile);
-
- pos.nPos = vinfoBlockFile[nFile].nUndoSize;
- vinfoBlockFile[nFile].nUndoSize += nAddSize;
- setDirtyFileInfo.insert(nFile);
-
- bool out_of_space;
- size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
- if (out_of_space) {
- return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
- }
- if (bytes_allocated != 0 && fPruneMode) {
- fCheckForPruning = true;
- }
-
- return true;
-}
-
static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
@@ -3709,25 +3509,6 @@ 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, 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, active_chain, block.GetBlockTime(), dbp != nullptr)) {
- error("%s: FindBlockPos failed", __func__);
- return FlatFilePos();
- }
- if (dbp == nullptr) {
- if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
- AbortNode("Failed to write block");
- return FlatFilePos();
- }
- }
- return blockPos;
-}
-
-/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
@@ -3811,29 +3592,32 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
return true;
}
-bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock)
+bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
{
CBlockIndex *pindex = nullptr;
- if (fNewBlock) *fNewBlock = false;
+ if (new_block) *new_block = false;
BlockValidationState state;
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
// Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
- bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ // Skipping AcceptBlock() for CheckBlock() failures means that we will never mark a block as invalid if
+ // CheckBlock() fails. This is protective against consensus failure if there are any unknown forms of block
+ // malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
+ // not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
+ bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
- ret = ActiveChainstate().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
+ ret = ActiveChainstate().AcceptBlock(block, state, chainparams, &pindex, force_processing, nullptr, new_block);
}
if (!ret) {
- GetMainSignals().BlockChecked(*pblock, state);
+ GetMainSignals().BlockChecked(*block, state);
return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString());
}
}
@@ -3841,7 +3625,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
NotifyHeaderTip(ActiveChainstate());
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
- if (!ActiveChainstate().ActivateBestChain(state, chainparams, pblock))
+ if (!ActiveChainstate().ActivateBestChain(state, chainparams, block))
return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString());
return true;
@@ -3884,18 +3668,6 @@ bool TestBlockValidity(BlockValidationState& state,
* BLOCK PRUNING CODE
*/
-/* Calculate the amount of disk space the block & undo files currently use */
-uint64_t CalculateCurrentUsage()
-{
- LOCK(cs_LastBlockFile);
-
- uint64_t retval = 0;
- for (const CBlockFileInfo &file : vinfoBlockFile) {
- retval += file.nSize + file.nUndoSize;
- }
- return retval;
-}
-
void BlockManager::PruneOneBlockFile(const int fileNumber)
{
AssertLockHeld(cs_main);
@@ -3930,17 +3702,6 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
setDirtyFileInfo.insert(fileNumber);
}
-
-void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
-{
- for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
- FlatFilePos pos(*it, 0);
- fs::remove(BlockFileSeq().FileName(pos));
- fs::remove(UndoFileSeq().FileName(pos));
- LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
- }
-}
-
void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
{
assert(fPruneMode && nManualPruneHeight > 0);
@@ -4035,30 +3796,6 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
nLastBlockWeCanPrune, count);
}
-static FlatFileSeq BlockFileSeq()
-{
- return FlatFileSeq(GetBlocksDir(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
-}
-
-static FlatFileSeq UndoFileSeq()
-{
- return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE);
-}
-
-FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) {
- return BlockFileSeq().Open(pos, fReadOnly);
-}
-
-/** Open an undo file (rev?????.dat) */
-static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) {
- return UndoFileSeq().Open(pos, fReadOnly);
-}
-
-fs::path GetBlockPosFilename(const FlatFilePos &pos)
-{
- return BlockFileSeq().FileName(pos);
-}
-
CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
{
AssertLockHeld(cs_main);
@@ -4240,7 +3977,7 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams)
CVerifyDB::CVerifyDB()
{
- uiInterface.ShowProgress(_("Verifying blocks...").translated, 0, false);
+ uiInterface.ShowProgress(_("Verifying blocks…").translated, 0, false);
}
CVerifyDB::~CVerifyDB()
@@ -4248,38 +3985,46 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100, false);
}
-bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(
+ CChainState& chainstate,
+ const CChainParams& chainparams,
+ CCoinsView& coinsview,
+ int nCheckLevel, int nCheckDepth)
{
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)
+ assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+ if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
// Verify blocks in the best chain
- if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height())
- nCheckDepth = active_chainstate.m_chain.Height();
+ if (nCheckDepth <= 0 || nCheckDepth > chainstate.m_chain.Height())
+ nCheckDepth = 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);
+ CCoinsViewCache coins(&coinsview);
CBlockIndex* pindex;
CBlockIndex* pindexFailure = nullptr;
int nGoodTransactions = 0;
BlockValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- 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))));
+
+ const bool is_snapshot_cs{!chainstate.m_from_snapshot_blockhash};
+
+ for (pindex = chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+ const int percentageDone = std::max(1, std::min(99, (int)(((double)(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 <= active_chainstate.m_chain.Height()-nCheckDepth)
+ uiInterface.ShowProgress(_("Verifying blocks…").translated, percentageDone, false);
+ if (pindex->nHeight <= chainstate.m_chain.Height()-nCheckDepth)
break;
- if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, only go back as far as we have data.
+ if ((fPruneMode || is_snapshot_cs) && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
+ // If pruning or running under an assumeutxo snapshot, only go
+ // back as far as we have data.
LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight);
break;
}
@@ -4301,9 +4046,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_ch
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) {
+ size_t curr_coins_usage = coins.DynamicMemoryUsage() + chainstate.CoinsTip().DynamicMemoryUsage();
+
+ if (nCheckLevel >= 3 && curr_coins_usage <= chainstate.m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
- DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins);
+ DisconnectResult res = 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());
}
@@ -4317,26 +4064,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_ch
if (ShutdownRequested()) return true;
}
if (pindexFailure)
- 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);
+ return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// store block count as we move pindex at check level >= 4
- int block_count = active_chainstate.m_chain.Height() - pindex->nHeight;
+ int block_count = chainstate.m_chain.Height() - pindex->nHeight;
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- 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)));
+ while (pindex != chainstate.m_chain.Tip()) {
+ const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(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 = active_chainstate.m_chain.Next(pindex);
+ uiInterface.ShowProgress(_("Verifying blocks…").translated, percentageDone, false);
+ pindex = 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 (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
+ if (!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;
}
@@ -4380,7 +4127,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
if (hashHeads.empty()) return true; // We're already in a consistent state.
if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state");
- uiInterface.ShowProgress(_("Replaying blocks...").translated, 0, false);
+ uiInterface.ShowProgress(_("Replaying blocks…").translated, 0, false);
LogPrintf("Replaying blocks\n");
const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush.
@@ -4426,7 +4173,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
- uiInterface.ShowProgress(_("Replaying blocks...").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
+ uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
if (!RollforwardBlock(pindex, cache, params)) return false;
}
@@ -4436,143 +4183,23 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
return true;
}
-//! Helper for CChainState::RewindBlockIndex
-void CChainState::EraseBlockData(CBlockIndex* index)
+bool CChainState::NeedsRedownload(const CChainParams& params) const
{
AssertLockHeld(cs_main);
- assert(!m_chain.Contains(index)); // Make sure this block isn't active
-
- // Reduce validity
- index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
- // Remove have-data flags.
- index->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
- // Remove storage location.
- index->nFile = 0;
- index->nDataPos = 0;
- index->nUndoPos = 0;
- // Remove various other things
- index->nTx = 0;
- index->nChainTx = 0;
- index->nSequenceId = 0;
- // Make sure it gets written.
- setDirtyBlockIndex.insert(index);
- // Update indexes
- setBlockIndexCandidates.erase(index);
- auto ret = m_blockman.m_blocks_unlinked.equal_range(index->pprev);
- while (ret.first != ret.second) {
- if (ret.first->second == index) {
- m_blockman.m_blocks_unlinked.erase(ret.first++);
- } else {
- ++ret.first;
- }
- }
- // Mark parent as eligible for main chain again
- if (index->pprev && index->pprev->IsValid(BLOCK_VALID_TRANSACTIONS) && index->pprev->HaveTxsDownloaded()) {
- setBlockIndexCandidates.insert(index->pprev);
- }
-}
-
-bool CChainState::RewindBlockIndex(const CChainParams& params)
-{
- // Note that during -reindex-chainstate we are called with an empty m_chain!
-
- // First erase all post-segwit blocks without witness not in the main chain,
- // as this can we done without costly DisconnectTip calls. Active
- // blocks will be dealt with below (releasing cs_main in between).
- {
- LOCK(cs_main);
- for (const auto& entry : m_blockman.m_block_index) {
- if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
- EraseBlockData(entry.second);
- }
- }
- }
-
- // Find what height we need to reorganize to.
- CBlockIndex *tip;
- int nHeight = 1;
- {
- LOCK(cs_main);
- while (nHeight <= m_chain.Height()) {
- // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
- // blocks in ConnectBlock, we don't need to go back and
- // re-download/re-verify blocks from before segwit actually activated.
- if (IsWitnessEnabled(m_chain[nHeight - 1], params.GetConsensus()) && !(m_chain[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
- break;
- }
- nHeight++;
- }
- tip = m_chain.Tip();
- }
- // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
-
- BlockValidationState state;
- // Loop until the tip is below nHeight, or we reach a pruned block.
- while (!ShutdownRequested()) {
- {
- LOCK(cs_main);
- LOCK(m_mempool.cs);
- // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
- assert(tip == m_chain.Tip());
- if (tip == nullptr || tip->nHeight < nHeight) break;
- if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, don't try rewinding past the HAVE_DATA point;
- // since older blocks can't be served anyway, there's
- // no need to walk further, and trying to DisconnectTip()
- // will fail (and require a needless reindex/redownload
- // of the blockchain).
- break;
- }
-
- // Disconnect block
- if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, state.ToString());
- }
-
- // Reduce validity flag and have-data flags.
- // We do this after actual disconnecting, otherwise we'll end up writing the lack of data
- // to disk before writing the chainstate, resulting in a failure to continue if interrupted.
- // Note: If we encounter an insufficiently validated block that
- // is on m_chain, it must be because we are a pruning node, and
- // this block or some successor doesn't HAVE_DATA, so we were unable to
- // rewind all the way. Blocks remaining on m_chain at this point
- // must not have their validity reduced.
- EraseBlockData(tip);
-
- tip = tip->pprev;
- }
- // Make sure the queue of validation callbacks doesn't grow unboundedly.
- LimitValidationInterfaceQueue();
-
- // Occasionally flush state to disk.
- if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
- return false;
- }
- }
-
- {
- LOCK(cs_main);
- if (m_chain.Tip() != nullptr) {
- // We can't prune block index candidates based on our tip if we have
- // no tip due to m_chain being empty!
- PruneBlockIndexCandidates();
+ // At and above params.SegwitHeight, segwit consensus rules must be validated
+ CBlockIndex* block{m_chain.Tip()};
+ const int segwit_height{params.GetConsensus().SegwitHeight};
- CheckBlockIndex(params.GetConsensus());
-
- // FlushStateToDisk can possibly read ::ChainActive(). Be conservative
- // and skip it here, we're about to -reindex-chainstate anyway, so
- // it'll get called a bunch real soon.
- BlockValidationState state;
- if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
- return false;
- }
+ while (block != nullptr && block->nHeight >= segwit_height) {
+ if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
+ // block is insufficiently validated for a segwit client
+ return true;
}
+ block = block->pprev;
}
- return true;
+ return false;
}
void CChainState::UnloadBlockIndex() {
@@ -4964,8 +4591,8 @@ std::string CChainState::ToString()
{
CBlockIndex* tip = m_chain.Tip();
return strprintf("Chainstate [%s] @ height %d (%s)",
- m_from_snapshot_blockhash.IsNull() ? "ibd" : "snapshot",
- tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
+ m_from_snapshot_blockhash ? "snapshot" : "ibd",
+ tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
}
bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
@@ -5001,25 +4628,13 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
return ret;
}
-std::string CBlockFileInfo::ToString() const
-{
- return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
-}
-
-CBlockFileInfo* GetBlockFileInfo(size_t n)
-{
- LOCK(cs_LastBlockFile);
-
- return &vinfoBlockFile.at(n);
-}
-
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
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{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")};
+ FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat", "rb")};
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -5123,7 +4738,7 @@ bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool s
int64_t mid = GetTimeMicros();
try {
- FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")};
+ FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat.new", "wb")};
if (!filestr) {
return false;
}
@@ -5149,7 +4764,7 @@ bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool s
if (!skip_file_commit && !FileCommit(file.Get()))
throw std::runtime_error("FileCommit failed");
file.fclose();
- if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) {
+ if (!RenameOver(gArgs.GetDataDirNet() / "mempool.dat.new", gArgs.GetDataDirNet() / "mempool.dat")) {
throw std::runtime_error("Rename failed");
}
int64_t last = GetTimeMicros();
@@ -5180,10 +4795,10 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
}
-std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
+std::optional<uint256> ChainstateManager::SnapshotBlockhash() const
+{
LOCK(::cs_main);
- if (m_active_chainstate != nullptr &&
- !m_active_chainstate->m_from_snapshot_blockhash.IsNull()) {
+ if (m_active_chainstate && m_active_chainstate->m_from_snapshot_blockhash) {
// If a snapshot chainstate exists, it will always be our active.
return m_active_chainstate->m_from_snapshot_blockhash;
}
@@ -5206,9 +4821,9 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash)
+CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash)
{
- bool is_snapshot = !snapshot_blockhash.IsNull();
+ bool is_snapshot = snapshot_blockhash.has_value();
std::unique_ptr<CChainState>& to_modify =
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
@@ -5333,6 +4948,26 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
uint256 base_blockhash = metadata.m_base_blockhash;
+ CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));
+
+ if (!snapshot_start_block) {
+ // Needed for GetUTXOStats and ExpectedAssumeutxo to determine the height and to avoid a crash when base_blockhash.IsNull()
+ LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
+ base_blockhash.ToString());
+ return false;
+ }
+
+ 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;
+
COutPoint outpoint;
Coin coin;
const uint64_t coins_count = metadata.m_coins_count;
@@ -5345,12 +4980,12 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
while (coins_left > 0) {
try {
coins_file >> outpoint;
+ coins_file >> coin;
} catch (const std::ios_base::failure&) {
- LogPrintf("[snapshot] bad snapshot - no coins left after deserializing %d coins\n",
- coins_count - coins_left);
+ LogPrintf("[snapshot] bad snapshot format or truncated snapshot 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;
@@ -5423,57 +5058,20 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(coins_cache.GetBestBlock() == base_blockhash);
- CCoinsStats stats;
+ CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
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)) {
+ if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, 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) {
+ if (AssumeutxoHash{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;
@@ -5485,31 +5083,27 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
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];
+ // Fake nTx so that LoadBlockIndex() loads assumed-valid CBlockIndex
+ // entries (among other things)
if (!index->nTx) {
index->nTx = 1;
}
+ // Fake nChainTx so that GuessVerificationProgress reports accurately
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.
+ // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
+ // won't ask 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;
+ index->nChainTx = au_data.nChainTx;
snapshot_chainstate.setBlockIndexCandidates.insert(snapshot_start_block);
LogPrintf("[snapshot] validated snapshot (%.2f MB)\n",
diff --git a/src/validation.h b/src/validation.h
index 21e63947fa..359a6c779f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -18,6 +18,7 @@
#include <fs.h>
#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
+#include <policy/packages.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
#include <sync.h>
@@ -27,6 +28,7 @@
#include <serialize.h>
#include <util/check.h>
#include <util/hasher.h>
+#include <util/translation.h>
#include <atomic>
#include <map>
@@ -35,6 +37,7 @@
#include <set>
#include <stdint.h>
#include <string>
+#include <thread>
#include <utility>
#include <vector>
@@ -69,8 +72,6 @@ static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25;
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336;
-/** The maximum size of a blk?????.dat file (since 0.8) */
-static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
/** Maximum number of dedicated script-checking threads allowed */
static const int MAX_SCRIPTCHECK_THREADS = 15;
/** -par default (number of script-checking threads, 0 = auto) */
@@ -78,11 +79,10 @@ static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
+static constexpr bool DEFAULT_COINSTATSINDEX{false};
static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
-/** Default for using fee filter */
-static const bool DEFAULT_FEEFILTER = true;
/** Default for -stopatheight */
static const int DEFAULT_STOPATHEIGHT = 0;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ::ChainActive().Tip() will not be pruned. */
@@ -111,8 +111,6 @@ typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
extern uint256 g_best_block;
-extern std::atomic_bool fImporting;
-extern std::atomic_bool fReindex;
/** Whether there are dedicated script-checking threads running.
* False indicates all script checking is done on the main threadMessageHandler thread.
*/
@@ -134,20 +132,9 @@ extern arith_uint256 nMinimumChainWork;
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
-/** Pruning-related variables and constants */
-/** True if any block files have ever been pruned. */
-extern bool fHavePruned;
-/** True if we're running in -prune mode. */
-extern bool fPruneMode;
-/** Number of MiB of block files that we're trying to stay below. */
-extern uint64_t nPruneTarget;
/** Documentation for argument 'checklevel'. */
extern const std::vector<std::string> CHECKLEVEL_DOC;
-/** Open a block file (blk?????.dat) */
-FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
-/** Translation to a filesystem path */
-fs::path GetBlockPosFilename(const FlatFilePos &pos);
/** Unload database information */
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
/** Run instances of script checking worker threads */
@@ -169,17 +156,11 @@ void StopScriptCheckWorkerThreads();
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock);
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
+bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{});
+
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
-/** Calculate the amount of disk space the block & undo files currently use */
-uint64_t CalculateCurrentUsage();
-
-/**
- * Actually unlink the specified files
- */
-void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
-
/** Prune block files up to a given height */
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
@@ -187,22 +168,29 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
* Validation result for a single transaction mempool acceptance.
*/
struct MempoolAcceptResult {
- /** Used to indicate the results of mempool validation,
- * including the possibility of unfinished validation.
- */
+ /** Used to indicate the results of mempool validation. */
enum class ResultType {
VALID, //!> Fully validated, valid.
INVALID, //!> Invalid.
};
- ResultType m_result_type;
- TxValidationState m_state;
+ const ResultType m_result_type;
+ const 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;
+ const std::optional<std::list<CTransactionRef>> m_replaced_transactions;
+ /** Raw base fees in satoshis. */
+ const std::optional<CAmount> m_base_fees;
+ static MempoolAcceptResult Failure(TxValidationState state) {
+ return MempoolAcceptResult(state);
+ }
+
+ static MempoolAcceptResult Success(std::list<CTransactionRef>&& replaced_txns, CAmount fees) {
+ return MempoolAcceptResult(std::move(replaced_txns), fees);
+ }
+// Private constructors. Use static methods MempoolAcceptResult::Success, etc. to construct.
+private:
/** Constructor for failure case */
explicit MempoolAcceptResult(TxValidationState state)
: m_result_type(ResultType::INVALID), m_state(state) {
@@ -211,11 +199,33 @@ struct MempoolAcceptResult {
/** Constructor for success case */
explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, CAmount fees)
- : m_result_type(ResultType::VALID), m_state(TxValidationState{}),
+ : m_result_type(ResultType::VALID),
m_replaced_transactions(std::move(replaced_txns)), m_base_fees(fees) {}
};
/**
+* Validation result for package mempool acceptance.
+*/
+struct PackageMempoolAcceptResult
+{
+ const PackageValidationState m_state;
+ /**
+ * Map from wtxid to finished MempoolAcceptResults. The client is responsible
+ * for keeping track of the transaction objects themselves. If a result is not
+ * present, it means validation was unfinished for that transaction.
+ */
+ std::map<const uint256, const MempoolAcceptResult> m_tx_results;
+
+ explicit PackageMempoolAcceptResult(PackageValidationState state,
+ std::map<const uint256, const MempoolAcceptResult>&& results)
+ : m_state{state}, m_tx_results(std::move(results)) {}
+
+ /** Constructor to create a PackageMempoolAcceptResult from a single MempoolAcceptResult */
+ explicit PackageMempoolAcceptResult(const uint256& wtxid, const MempoolAcceptResult& result)
+ : m_tx_results{ {wtxid, result} } {}
+};
+
+/**
* (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.
@@ -223,6 +233,18 @@ struct MempoolAcceptResult {
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+/**
+* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply.
+* @param[in] txns Group of transactions which may be independent or contain
+* parent-child dependencies. The transactions must not conflict, i.e.
+* must not spend the same inputs, even if it would be a valid BIP125
+* replace-by-fee. Parents must appear before children.
+* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
+* If a transaction fails, validation will exit early and some results may be missing.
+*/
+PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
+ const Package& txns, bool test_accept)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
@@ -244,9 +266,13 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
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.
- *
- * Simulates calling SequenceLocks() with data from the tip of the current active chain.
+ * Check if transaction will be BIP68 final in the next block to be created on top of tip.
+ * @param[in] tip Chain tip to check tx sequence locks against. For example,
+ * the tip of the current active chain.
+ * @param[in] coins_view Any CCoinsView that provides access to the relevant coins
+ * for checking sequence locks. Any CCoinsView can be passed in;
+ * it is assumed to be consistent with the tip.
+ * Simulates calling SequenceLocks() with data from the tip passed in.
* Optionally stores in LockPoints the resulting height and time calculated and the hash
* of the block needed for calculation or skips the calculation and uses the LockPoints
* passed in for evaluation.
@@ -254,12 +280,12 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(CChainState& active_chainstate,
- const CTxMemPool& pool,
+bool CheckSequenceLocks(CBlockIndex* tip,
+ const CCoinsView& coins_view,
const CTransaction& tx,
int flags,
LockPoints* lp = nullptr,
- bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
+ bool useExistingLockPoints = false);
/**
* Closure representing one script verification
@@ -299,15 +325,6 @@ public:
/** Initializes the script-execution cache */
void InitScriptExecutionCache();
-
-/** Functions for disk access for blocks */
-bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
-bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
-
-bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
-
/** Functions for validating blocks and updating the block tree */
/** Context-independent validity checks */
@@ -337,7 +354,12 @@ class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
- bool VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool VerifyDB(
+ CChainState& chainstate,
+ const CChainParams& chainparams,
+ CCoinsView& coinsview,
+ int nCheckLevel,
+ int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
enum DisconnectResult
@@ -533,7 +555,7 @@ enum class CoinsCacheSizeState
*
* Anything that is contingent on the current tip of the chain is stored here,
* whereas block information and metadata independent of the current tip is
- * kept in `BlockMetadataManager`.
+ * kept in `BlockManager`.
*/
class CChainState
{
@@ -575,7 +597,7 @@ public:
//! CChainState instances.
BlockManager& m_blockman;
- explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
+ explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash = std::nullopt);
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -606,9 +628,9 @@ public:
/**
* The blockhash which is the base of the snapshot this chainstate was created from.
*
- * IsNull() if this chainstate was not created from a snapshot.
+ * std::nullopt if this chainstate was not created from a snapshot.
*/
- const uint256 m_from_snapshot_blockhash{};
+ const std::optional<uint256> m_from_snapshot_blockhash;
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
@@ -721,7 +743,9 @@ public:
/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params);
- bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
+
+ /** Whether the chain state needs to be redownloaded due to lack of witness data */
+ [[nodiscard]] bool NeedsRedownload(const CChainParams& params) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
@@ -768,9 +792,6 @@ private:
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- //! Mark a block as not having block data
- void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -869,6 +890,7 @@ private:
friend CChain& ChainActive();
public:
+ std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
BlockManager m_blockman GUARDED_BY(::cs_main);
@@ -888,7 +910,7 @@ public:
// constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
- CChainState& InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash = uint256())
+ CChainState& InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash = std::nullopt)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
@@ -921,6 +943,8 @@ public:
return m_blockman.m_block_index;
}
+ //! @returns true if a snapshot-based chainstate is in use. Also implies
+ //! that a background validation chainstate is also in use.
bool IsSnapshotActive() const;
std::optional<uint256> SnapshotBlockhash() const;
@@ -946,22 +970,21 @@ public:
* block is made active. Note that it does not, however, guarantee that the
* specific block passed to it has been checked for validity!
*
- * If you want to *possibly* get feedback on whether pblock is valid, you must
+ * If you want to *possibly* get feedback on whether block is valid, you must
* install a CValidationInterface (see validationinterface.h) - this will have
* its BlockChecked method called whenever *any* block completes validation.
*
- * Note that we guarantee that either the proof-of-work is valid on pblock, or
+ * Note that we guarantee that either the proof-of-work is valid on block, or
* (and possibly also) BlockChecked will have been called.
*
- * May not be called in a
- * validationinterface callback.
+ * May not be called in a validationinterface callback.
*
- * @param[in] pblock The block we want to process.
- * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources.
- * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
+ * @param[in] block The block we want to process.
+ * @param[in] force_processing Process this block even if unrequested; used for non-network block sources.
+ * @param[out] new_block A boolean which is set to indicate if the block was first received via this call
* @returns If the block was processed, independently of block validity
*/
- bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
+ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
/**
* Process incoming block headers.
@@ -1009,9 +1032,6 @@ extern VersionBitsCache versionbitscache;
*/
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
-/** 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. */
@@ -1020,16 +1040,10 @@ bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbri
/** Load the mempool from disk. */
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)
-{
- 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.
+ * @param[in] height Get the assumeutxo value for this height.
*
* @returns empty if no assumeutxo configuration exists for the given height.
*/
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index af07c67ccf..df2ec4e056 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -9,6 +9,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
{
int nPeriod = Period(params);
int nThreshold = Threshold(params);
+ int min_activation_height = MinActivationHeight(params);
int64_t nTimeStart = BeginTime(params);
int64_t nTimeTimeout = EndTime(params);
@@ -17,6 +18,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return ThresholdState::ACTIVE;
}
+ // Check if this deployment is never active.
+ if (nTimeStart == Consensus::BIP9Deployment::NEVER_ACTIVE) {
+ return ThresholdState::FAILED;
+ }
+
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
if (pindexPrev != nullptr) {
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
@@ -51,18 +57,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
switch (state) {
case ThresholdState::DEFINED: {
- if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
- stateNext = ThresholdState::FAILED;
- } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
+ if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
stateNext = ThresholdState::STARTED;
}
break;
}
case ThresholdState::STARTED: {
- if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
- stateNext = ThresholdState::FAILED;
- break;
- }
// We need to count
const CBlockIndex* pindexCount = pindexPrev;
int count = 0;
@@ -74,12 +74,16 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
}
if (count >= nThreshold) {
stateNext = ThresholdState::LOCKED_IN;
+ } else if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
+ stateNext = ThresholdState::FAILED;
}
break;
}
case ThresholdState::LOCKED_IN: {
- // Always progresses into ACTIVE.
- stateNext = ThresholdState::ACTIVE;
+ // Progresses into ACTIVE provided activation height will have been reached.
+ if (pindexPrev->nHeight + 1 >= min_activation_height) {
+ stateNext = ThresholdState::ACTIVE;
+ }
break;
}
case ThresholdState::FAILED:
@@ -126,7 +130,7 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int64_t start_time = BeginTime(params);
- if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
+ if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE || start_time == Consensus::BIP9Deployment::NEVER_ACTIVE) {
return 0;
}
@@ -170,6 +174,7 @@ private:
protected:
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
+ int MinActivationHeight(const Consensus::Params& params) const override { return params.vDeployments[id].min_activation_height; }
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
diff --git a/src/versionbits.h b/src/versionbits.h
index 6df1db8814..634a848ef5 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -25,7 +25,7 @@ static const int32_t VERSIONBITS_NUM_BITS = 29;
enum class ThresholdState {
DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
STARTED, // For blocks past the starttime.
- LOCKED_IN, // For one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion.
+ LOCKED_IN, // For at least one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion, until min_activation_height is reached.
ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state)
FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
};
@@ -57,6 +57,7 @@ protected:
virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0;
virtual int64_t BeginTime(const Consensus::Params& params) const =0;
virtual int64_t EndTime(const Consensus::Params& params) const =0;
+ virtual int MinActivationHeight(const Consensus::Params& params) const { return 0; }
virtual int Period(const Consensus::Params& params) const =0;
virtual int Threshold(const Consensus::Params& params) const =0;
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index 720877ead0..598c5f082c 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -6,21 +6,7 @@
#include <util/system.h>
-void CCoinControl::SetNull()
+CCoinControl::CCoinControl()
{
- destChange = CNoDestination();
- m_change_type.reset();
- m_add_inputs = true;
- fAllowOtherInputs = false;
- fAllowWatchOnly = false;
m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
- m_avoid_address_reuse = false;
- setSelected.clear();
- m_feerate.reset();
- fOverrideFeeRate = false;
- m_confirm_target.reset();
- m_signal_bip125_rbf.reset();
- m_fee_mode = FeeEstimateMode::UNSET;
- m_min_depth = DEFAULT_MIN_DEPTH;
- m_max_depth = DEFAULT_MAX_DEPTH;
}
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index d25a3fb3fa..85cbec76b7 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -24,17 +24,19 @@ class CCoinControl
{
public:
//! Custom change destination, if not set an address is generated
- CTxDestination destChange;
+ CTxDestination destChange = CNoDestination();
//! Override the default change type if set, ignored if destChange is set
std::optional<OutputType> m_change_type;
//! If false, only selected inputs are used
- bool m_add_inputs;
+ bool m_add_inputs = true;
+ //! If false, only safe inputs will be used
+ bool m_include_unsafe_inputs = false;
//! If false, allows unselected inputs, but requires all selected inputs be used
- bool fAllowOtherInputs;
+ bool fAllowOtherInputs = false;
//! Includes watch only addresses which are solvable
- bool fAllowWatchOnly;
+ bool fAllowWatchOnly = false;
//! Override automatic min/max checks on fee, m_feerate must be set if true
- bool fOverrideFeeRate;
+ bool fOverrideFeeRate = false;
//! Override the wallet's m_pay_tx_fee if set
std::optional<CFeeRate> m_feerate;
//! Override the default confirmation target if set
@@ -42,22 +44,17 @@ public:
//! Override the wallet's m_signal_rbf if set
std::optional<bool> m_signal_bip125_rbf;
//! Avoid partial use of funds sent to a given address
- bool m_avoid_partial_spends;
+ bool m_avoid_partial_spends = DEFAULT_AVOIDPARTIALSPENDS;
//! Forbids inclusion of dirty (previously used) addresses
- bool m_avoid_address_reuse;
+ bool m_avoid_address_reuse = false;
//! Fee estimation mode to control arguments to estimateSmartFee
- FeeEstimateMode m_fee_mode;
+ FeeEstimateMode m_fee_mode = FeeEstimateMode::UNSET;
//! Minimum chain depth value for coin availability
int m_min_depth = DEFAULT_MIN_DEPTH;
//! Maximum chain depth value for coin availability
int m_max_depth = DEFAULT_MAX_DEPTH;
- CCoinControl()
- {
- SetNull();
- }
-
- void SetNull();
+ CCoinControl();
bool HasSelected() const
{
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 5a18308a73..6d502e1df1 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -14,7 +14,7 @@
struct {
bool operator()(const OutputGroup& a, const OutputGroup& b) const
{
- return a.effective_value > b.effective_value;
+ return a.GetSelectionAmount() > b.GetSelectionAmount();
}
} descending;
@@ -49,37 +49,34 @@ struct {
* @param const std::vector<CInputCoin>& utxo_pool The set of UTXOs that we are choosing from.
* These UTXOs will be sorted in descending order by effective value and the CInputCoins'
* values are their effective values.
- * @param const CAmount& target_value This is the value that we want to select. It is the lower
+ * @param const CAmount& selection_target This is the value that we want to select. It is the lower
* bound of the range.
* @param const CAmount& cost_of_change This is the cost of creating and spending a change output.
- * This plus target_value is the upper bound of the range.
+ * This plus selection_target is the upper bound of the range.
* @param std::set<CInputCoin>& out_set -> This is an output parameter for the set of CInputCoins
* that have been selected.
* @param CAmount& value_ret -> This is an output parameter for the total value of the CInputCoins
* that were selected.
- * @param CAmount not_input_fees -> The fees that need to be paid for the outputs and fixed size
- * overhead (version, locktime, marker and flag)
*/
static const size_t TOTAL_TRIES = 100000;
-bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees)
+bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret)
{
out_set.clear();
CAmount curr_value = 0;
std::vector<bool> curr_selection; // select the utxo at this index
curr_selection.reserve(utxo_pool.size());
- CAmount actual_target = not_input_fees + target_value;
// Calculate curr_available_value
CAmount curr_available_value = 0;
for (const OutputGroup& utxo : utxo_pool) {
// Assert that this utxo is not negative. It should never be negative, effective value calculation should have removed it
- assert(utxo.effective_value > 0);
- curr_available_value += utxo.effective_value;
+ assert(utxo.GetSelectionAmount() > 0);
+ curr_available_value += utxo.GetSelectionAmount();
}
- if (curr_available_value < actual_target) {
+ if (curr_available_value < selection_target) {
return false;
}
@@ -94,12 +91,12 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v
for (size_t i = 0; i < TOTAL_TRIES; ++i) {
// Conditions for starting a backtrack
bool backtrack = false;
- if (curr_value + curr_available_value < actual_target || // Cannot possibly reach target with the amount remaining in the curr_available_value.
- curr_value > actual_target + cost_of_change || // Selected value is out of range, go back and try other branch
+ if (curr_value + curr_available_value < selection_target || // Cannot possibly reach target with the amount remaining in the curr_available_value.
+ curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch
(curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) { // Don't select things which we know will be more wasteful if the waste is increasing
backtrack = true;
- } else if (curr_value >= actual_target) { // Selected value is within range
- curr_waste += (curr_value - actual_target); // This is the excess value which is added to the waste for the below comparison
+ } else if (curr_value >= selection_target) { // Selected value is within range
+ curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison
// Adding another UTXO after this check could bring the waste down if the long term fee is higher than the current fee.
// However we are not going to explore that because this optimization for the waste is only done when we have hit our target
// value. Adding any more UTXOs will be just burning the UTXO; it will go entirely to fees. Thus we aren't going to
@@ -112,7 +109,7 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v
break;
}
}
- curr_waste -= (curr_value - actual_target); // Remove the excess value as we will be selecting different coins now
+ curr_waste -= (curr_value - selection_target); // Remove the excess value as we will be selecting different coins now
backtrack = true;
}
@@ -121,7 +118,7 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v
// Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
while (!curr_selection.empty() && !curr_selection.back()) {
curr_selection.pop_back();
- curr_available_value += utxo_pool.at(curr_selection.size()).effective_value;
+ curr_available_value += utxo_pool.at(curr_selection.size()).GetSelectionAmount();
}
if (curr_selection.empty()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched
@@ -131,24 +128,24 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v
// Output was included on previous iterations, try excluding now.
curr_selection.back() = false;
OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
- curr_value -= utxo.effective_value;
+ curr_value -= utxo.GetSelectionAmount();
curr_waste -= utxo.fee - utxo.long_term_fee;
} else { // Moving forwards, continuing down this branch
OutputGroup& utxo = utxo_pool.at(curr_selection.size());
// Remove this utxo from the curr_available_value utxo amount
- curr_available_value -= utxo.effective_value;
+ curr_available_value -= utxo.GetSelectionAmount();
// Avoid searching a branch if the previous UTXO has the same value and same waste and was excluded. Since the ratio of fee to
// long term fee is the same, we only need to check if one of those values match in order to know that the waste is the same.
if (!curr_selection.empty() && !curr_selection.back() &&
- utxo.effective_value == utxo_pool.at(curr_selection.size() - 1).effective_value &&
+ utxo.GetSelectionAmount() == utxo_pool.at(curr_selection.size() - 1).GetSelectionAmount() &&
utxo.fee == utxo_pool.at(curr_selection.size() - 1).fee) {
curr_selection.push_back(false);
} else {
// Inclusion branch first (Largest First Exploration)
curr_selection.push_back(true);
- curr_value += utxo.effective_value;
+ curr_value += utxo.GetSelectionAmount();
curr_waste += utxo.fee - utxo.long_term_fee;
}
}
@@ -230,14 +227,14 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
Shuffle(groups.begin(), groups.end(), FastRandomContext());
for (const OutputGroup& group : groups) {
- if (group.m_value == nTargetValue) {
+ if (group.GetSelectionAmount() == nTargetValue) {
util::insert(setCoinsRet, group.m_outputs);
nValueRet += group.m_value;
return true;
- } else if (group.m_value < nTargetValue + MIN_CHANGE) {
+ } else if (group.GetSelectionAmount() < nTargetValue + MIN_CHANGE) {
applicable_groups.push_back(group);
- nTotalLower += group.m_value;
- } else if (!lowest_larger || group.m_value < lowest_larger->m_value) {
+ nTotalLower += group.GetSelectionAmount();
+ } else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
lowest_larger = group;
}
}
@@ -270,7 +267,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
if (lowest_larger &&
- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->m_value <= nBest)) {
+ ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->GetSelectionAmount() <= nBest)) {
util::insert(setCoinsRet, lowest_larger->m_outputs);
nValueRet += lowest_larger->m_value;
} else {
@@ -339,3 +336,8 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f
&& m_ancestors <= eligibility_filter.max_ancestors
&& m_descendants <= eligibility_filter.max_descendants;
}
+
+CAmount OutputGroup::GetSelectionAmount() const
+{
+ return m_subtract_fee_outputs ? m_value : effective_value;
+}
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index f0e1addaf1..7a3fb82139 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -15,6 +15,7 @@ static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
+/** A UTXO under consideration for use in funding a new transaction. */
class CInputCoin {
public:
CInputCoin(const CTransactionRef& tx, unsigned int i)
@@ -56,44 +57,116 @@ public:
}
};
+/** Parameters for one iteration of Coin Selection. */
+struct CoinSelectionParams
+{
+ /** Size of a change output in bytes, determined by the output type. */
+ size_t change_output_size = 0;
+ /** Size of the input to spend a change output in virtual bytes. */
+ size_t change_spend_size = 0;
+ /** Cost of creating the change output. */
+ CAmount m_change_fee{0};
+ /** Cost of creating the change output + cost of spending the change output in the future. */
+ CAmount m_cost_of_change{0};
+ /** The targeted feerate of the transaction being built. */
+ CFeeRate m_effective_feerate;
+ /** The feerate estimate used to estimate an upper bound on what should be sufficient to spend
+ * the change output sometime in the future. */
+ CFeeRate m_long_term_feerate;
+ /** If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees. */
+ CFeeRate m_discard_feerate;
+ /** Size of the transaction before coin selection, consisting of the header and recipient
+ * output(s), excluding the inputs and change output(s). */
+ size_t tx_noinputs_size = 0;
+ /** Indicate that we are subtracting the fee from outputs */
+ bool m_subtract_fee_outputs = false;
+ /** When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs
+ * associated with the same address. This helps reduce privacy leaks resulting from address
+ * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */
+ bool m_avoid_partial_spends = false;
+
+ CoinSelectionParams(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) :
+ change_output_size(change_output_size),
+ change_spend_size(change_spend_size),
+ 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)
+ {}
+ CoinSelectionParams() {}
+};
+
+/** Parameters for filtering which OutputGroups we may use in coin selection.
+ * We start by being very selective and requiring multiple confirmations and
+ * then get more permissive if we cannot fund the transaction. */
struct CoinEligibilityFilter
{
+ /** Minimum number of confirmations for outputs that we sent to ourselves.
+ * We may use unconfirmed UTXOs sent from ourselves, e.g. change outputs. */
const int conf_mine;
+ /** Minimum number of confirmations for outputs received from a different wallet. */
const int conf_theirs;
+ /** Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup. */
const uint64_t max_ancestors;
+ /** Maximum number of descendants that a single UTXO in the OutputGroup may have. */
const uint64_t max_descendants;
- const bool m_include_partial_groups{false}; //! Include partial destination groups when avoid_reuse and there are full groups
+ /** When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any partial groups.*/
+ const bool m_include_partial_groups{false};
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {}
};
+/** A group of UTXOs paid to the same output script. */
struct OutputGroup
{
+ /** The list of UTXOs contained in this output group. */
std::vector<CInputCoin> m_outputs;
+ /** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at
+ * least a certain number of confirmations on UTXOs received from outside wallets while trusting
+ * our own UTXOs more. */
bool m_from_me{true};
+ /** The total value of the UTXOs in sum. */
CAmount m_value{0};
+ /** The minimum number of confirmations the UTXOs in the group have. Unconfirmed is 0. */
int m_depth{999};
+ /** The aggregated count of unconfirmed ancestors of all UTXOs in this
+ * group. Not deduplicated and may overestimate when ancestors are shared. */
size_t m_ancestors{0};
+ /** The maximum count of descendants of a single UTXO in this output group. */
size_t m_descendants{0};
+ /** The value of the UTXOs after deducting the cost of spending them at the effective feerate. */
CAmount effective_value{0};
+ /** The fee to spend these UTXOs at the effective feerate. */
CAmount fee{0};
+ /** The target feerate of the transaction we're trying to build. */
CFeeRate m_effective_feerate{0};
+ /** The fee to spend these UTXOs at the long term feerate. */
CAmount long_term_fee{0};
+ /** The feerate for spending a created change output eventually (i.e. not urgently, and thus at
+ * a lower feerate). Calculated using long term fee estimate. This is used to decide whether
+ * it could be economical to create a change output. */
CFeeRate m_long_term_feerate{0};
+ /** Indicate that we are subtracting the fee from outputs.
+ * When true, the value that is used for coin selection is the UTXO's real value rather than effective value */
+ bool m_subtract_fee_outputs{false};
OutputGroup() {}
- OutputGroup(const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate) :
- m_effective_feerate(effective_feerate),
- m_long_term_feerate(long_term_feerate)
+ OutputGroup(const CoinSelectionParams& params) :
+ m_effective_feerate(params.m_effective_feerate),
+ m_long_term_feerate(params.m_long_term_feerate),
+ m_subtract_fee_outputs(params.m_subtract_fee_outputs)
{}
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only);
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
+ CAmount GetSelectionAmount() const;
};
-bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees);
+bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret);
// Original coin selection algorithm as a fallback
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index b50f00e7d1..251778e401 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -78,7 +78,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true);
- size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), vchCiphertext.data());
+ size_t nLen = enc.Encrypt(vchPlaintext.data(), vchPlaintext.size(), vchCiphertext.data());
if(nLen < vchPlaintext.size())
return false;
vchCiphertext.resize(nLen);
@@ -97,7 +97,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
vchPlaintext.resize(nLen);
AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true);
- nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), &vchPlaintext[0]);
+ nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), vchPlaintext.data());
if(nLen == 0)
return false;
vchPlaintext.resize(nLen);
@@ -121,7 +121,7 @@ bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned
memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE);
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
- return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
+ return cKeyCrypter.Decrypt(vchCiphertext, vchPlaintext);
}
bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index cd49baeb78..5bf037b222 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -18,7 +18,12 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
if (ec) {
- LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
+ if (fs::is_directory(*it)) {
+ it.no_push();
+ LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), it->path().string());
+ } else {
+ LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
+ }
continue;
}
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index e314107988..c39c0c7e73 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -194,8 +194,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
std::shared_ptr<CWallet> wallet(new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet);
{
LOCK(wallet->cs_wallet);
- bool first_run = true;
- DBErrors load_wallet_ret = wallet->LoadWallet(first_run);
+ DBErrors load_wallet_ret = wallet->LoadWallet();
if (load_wallet_ret != DBErrors::LOAD_OK) {
error = strprintf(_("Error creating %s"), name);
return false;
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index a2071e521a..fe2c810afa 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -3,9 +3,16 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
-#include <wallet/external_signer.h>
+#include <external_signer.h>
#include <wallet/external_signer_scriptpubkeyman.h>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
#ifdef ENABLE_EXTERNAL_SIGNER
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index e60d7b8004..1786958912 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -8,6 +8,8 @@
#ifdef ENABLE_EXTERNAL_SIGNER
#include <wallet/scriptpubkeyman.h>
+#include <memory>
+
class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
{
public:
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 08adf09df4..30fef50c3b 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).first;
+ const int64_t maxTxSize{CalculateMaximumSignedTxSize(*wtx.tx, &wallet).vsize};
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 fdeead1fa5..eb0d6316c0 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -43,22 +43,22 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions(ArgsManager& argsman) const
{
argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
+ argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kvB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
"Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target",
CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data. 0 to entirely disable the fallbackfee feature. (default: %s)",
+ argsman.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kvB) that will be used when fee estimation has insufficient data. 0 to entirely disable the fallbackfee feature. (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u). Warning: Smaller sizes may increase the risk of losing funds when restoring from an old backup, if none of the addresses in the original keypool have been used.", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-maxapsfee=<n>", strprintf("Spend up to this amount in additional (absolute) fees (in %s) if it allows the use of partial spend avoidance (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_MAX_AVOIDPARTIALSPEND_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
+ argsman.AddArg("-mintxfee=<amt>", strprintf("Fee rates (in %s/kvB) smaller than this are considered zero fee for transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
+ argsman.AddArg("-paytxfee=<amt>", strprintf("Fee rate (in %s/kvB) 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
@@ -82,6 +82,12 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddHiddenArgs({"-dblogsize", "-flushwallet", "-privdb"});
#endif
+#ifdef USE_SQLITE
+ argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+#else
+ argsman.AddHiddenArgs({"-unsafesqlitesync"});
+#endif
+
argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddHiddenArgs({"-zapwallettxes"});
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index da5b84ce83..aca52964ee 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -22,7 +22,6 @@
#include <wallet/fees.h>
#include <wallet/ismine.h>
#include <wallet/load.h>
-#include <wallet/rpcsigner.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
@@ -198,22 +197,14 @@ public:
}
return result;
}
- bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
- {
+ std::vector<std::string> getAddressReceiveRequests() override {
LOCK(m_wallet->cs_wallet);
- WalletBatch batch{m_wallet->GetDatabase()};
- return m_wallet->AddDestData(batch, dest, key, value);
+ return m_wallet->GetAddressReceiveRequests();
}
- bool eraseDestData(const CTxDestination& dest, const std::string& key) override
- {
+ bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) override {
LOCK(m_wallet->cs_wallet);
WalletBatch batch{m_wallet->GetDatabase()};
- return m_wallet->EraseDestData(batch, dest, key);
- }
- std::vector<std::string> getDestValues(const std::string& prefix) override
- {
- LOCK(m_wallet->cs_wallet);
- return m_wallet->GetDestValues(prefix);
+ return m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
}
void lockCoin(const COutPoint& output) override
{
@@ -514,19 +505,12 @@ public:
{
for (const CRPCCommand& command : GetWalletRPCCommands()) {
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()));
- }
-
-#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);
+ JSONRPCRequest wallet_request = request;
+ wallet_request.context = &m_context;
+ return command.actor(wallet_request, 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); }
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 6a59bc2b38..dbf9fd46b6 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -39,7 +39,7 @@ bool VerifyWallets(interfaces::Chain& chain)
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
- chain.initMessage(_("Verifying wallet(s)...").translated);
+ chain.initMessage(_("Verifying wallet(s)…").translated);
// For backwards compatibility if an unnamed top level wallet exists in the
// wallets directory, include it in the default list of wallets to load.
@@ -105,7 +105,8 @@ bool LoadWallets(interfaces::Chain& chain)
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
continue;
}
- std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
+ chain.initMessage(_("Loading wallet…").translated);
+ std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(&chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) {
chain.initError(error);
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
new file mode 100644
index 0000000000..de81dbf324
--- /dev/null
+++ b/src/wallet/receive.cpp
@@ -0,0 +1,471 @@
+// 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 <consensus/consensus.h>
+#include <wallet/receive.h>
+#include <wallet/transaction.h>
+#include <wallet/wallet.h>
+
+isminetype CWallet::IsMine(const CTxIn &txin) const
+{
+ AssertLockHeld(cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
+ {
+ const CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
+ }
+ return ISMINE_NO;
+}
+
+bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
+{
+ LOCK(cs_wallet);
+
+ for (const CTxIn& txin : tx.vin)
+ {
+ auto mi = mapWallet.find(txin.prevout.hash);
+ if (mi == mapWallet.end())
+ return false; // any unknown inputs can't be from us
+
+ const CWalletTx& prev = (*mi).second;
+
+ if (txin.prevout.n >= prev.tx->vout.size())
+ return false; // invalid input!
+
+ if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
+ return false;
+ }
+ return true;
+}
+
+CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
+{
+ if (!MoneyRange(txout.nValue))
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
+ LOCK(cs_wallet);
+ return ((IsMine(txout) & filter) ? txout.nValue : 0);
+}
+
+CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
+{
+ CAmount nCredit = 0;
+ for (const CTxOut& txout : tx.vout)
+ {
+ nCredit += GetCredit(txout, filter);
+ if (!MoneyRange(nCredit))
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
+ }
+ return nCredit;
+}
+
+bool CWallet::IsChange(const CScript& script) const
+{
+ // TODO: fix handling of 'change' outputs. The assumption is that any
+ // payment to a script that is ours, but is not in the address book
+ // is change. That assumption is likely to break when we implement multisignature
+ // wallets that return change back into a multi-signature-protected address;
+ // a better way of identifying which outputs are 'the send' and which are
+ // 'the change' will need to be implemented (maybe extend CWalletTx to remember
+ // which output, if any, was change).
+ AssertLockHeld(cs_wallet);
+ if (IsMine(script))
+ {
+ CTxDestination address;
+ if (!ExtractDestination(script, address))
+ return true;
+ if (!FindAddressBookEntry(address)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CWallet::IsChange(const CTxOut& txout) const
+{
+ return IsChange(txout.scriptPubKey);
+}
+
+CAmount CWallet::GetChange(const CTxOut& txout) const
+{
+ AssertLockHeld(cs_wallet);
+ if (!MoneyRange(txout.nValue))
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
+ return (IsChange(txout) ? txout.nValue : 0);
+}
+
+CAmount CWallet::GetChange(const CTransaction& tx) const
+{
+ LOCK(cs_wallet);
+ CAmount nChange = 0;
+ for (const CTxOut& txout : tx.vout)
+ {
+ nChange += GetChange(txout);
+ if (!MoneyRange(nChange))
+ throw std::runtime_error(std::string(__func__) + ": value out of range");
+ }
+ return nChange;
+}
+
+CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
+{
+ auto& amount = m_amounts[type];
+ if (recalculate || !amount.m_cached[filter]) {
+ amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
+ m_is_cache_empty = false;
+ }
+ return amount.m_value[filter];
+}
+
+CAmount CWalletTx::GetCredit(const isminefilter& filter) const
+{
+ // Must wait until coinbase is safely deep enough in the chain before valuing it
+ if (IsImmatureCoinBase())
+ return 0;
+
+ CAmount credit = 0;
+ if (filter & ISMINE_SPENDABLE) {
+ // GetBalance can assume transactions in mapWallet won't change
+ credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
+ }
+ if (filter & ISMINE_WATCH_ONLY) {
+ credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
+ }
+ return credit;
+}
+
+CAmount CWalletTx::GetDebit(const isminefilter& filter) const
+{
+ if (tx->vin.empty())
+ return 0;
+
+ CAmount debit = 0;
+ if (filter & ISMINE_SPENDABLE) {
+ debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
+ }
+ if (filter & ISMINE_WATCH_ONLY) {
+ debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
+ }
+ return debit;
+}
+
+CAmount CWalletTx::GetChange() const
+{
+ if (fChangeCached)
+ return nChangeCached;
+ nChangeCached = pwallet->GetChange(*tx);
+ fChangeCached = true;
+ return nChangeCached;
+}
+
+CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
+{
+ if (IsImmatureCoinBase() && IsInMainChain()) {
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
+ }
+
+ return 0;
+}
+
+CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
+{
+ if (IsImmatureCoinBase() && IsInMainChain()) {
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
+ }
+
+ return 0;
+}
+
+CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
+{
+ if (pwallet == nullptr)
+ return 0;
+
+ // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
+ bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
+
+ // Must wait until coinbase is safely deep enough in the chain before valuing it
+ if (IsImmatureCoinBase())
+ return 0;
+
+ if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
+ return m_amounts[AVAILABLE_CREDIT].m_value[filter];
+ }
+
+ bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
+ CAmount nCredit = 0;
+ uint256 hashTx = GetHash();
+ for (unsigned int i = 0; i < tx->vout.size(); i++)
+ {
+ if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
+ const CTxOut &txout = tx->vout[i];
+ nCredit += pwallet->GetCredit(txout, filter);
+ if (!MoneyRange(nCredit))
+ throw std::runtime_error(std::string(__func__) + " : value out of range");
+ }
+ }
+
+ if (allow_cache) {
+ m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
+ m_is_cache_empty = false;
+ }
+
+ return nCredit;
+}
+
+void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
+ std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const
+{
+ nFee = 0;
+ listReceived.clear();
+ listSent.clear();
+
+ // Compute fee:
+ CAmount nDebit = GetDebit(filter);
+ if (nDebit > 0) // debit>0 means we signed/sent this transaction
+ {
+ CAmount nValueOut = tx->GetValueOut();
+ nFee = nDebit - nValueOut;
+ }
+
+ LOCK(pwallet->cs_wallet);
+ // Sent/received.
+ for (unsigned int i = 0; i < tx->vout.size(); ++i)
+ {
+ const CTxOut& txout = tx->vout[i];
+ isminetype fIsMine = pwallet->IsMine(txout);
+ // Only need to handle txouts if AT LEAST one of these is true:
+ // 1) they debit from us (sent)
+ // 2) the output is to us (received)
+ if (nDebit > 0)
+ {
+ // Don't report 'change' txouts
+ if (pwallet->IsChange(txout))
+ continue;
+ }
+ else if (!(fIsMine & filter))
+ continue;
+
+ // In either case, we need to get the destination address
+ CTxDestination address;
+
+ if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
+ {
+ pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
+ this->GetHash().ToString());
+ address = CNoDestination();
+ }
+
+ COutputEntry output = {address, txout.nValue, (int)i};
+
+ // If we are debited by the transaction, add the output as a "sent" entry
+ if (nDebit > 0)
+ listSent.push_back(output);
+
+ // If we are receiving the output, add it as a "received" entry
+ if (fIsMine & filter)
+ listReceived.push_back(output);
+ }
+
+}
+
+bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
+{
+ AssertLockHeld(cs_wallet);
+ // Quick answer in most cases
+ if (!chain().checkFinalTx(*wtx.tx)) return false;
+ int nDepth = wtx.GetDepthInMainChain();
+ if (nDepth >= 1) return true;
+ if (nDepth < 0) return false;
+ // using wtx's cached debit
+ if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
+
+ // Don't trust unconfirmed transactions from us unless they are in the mempool.
+ if (!wtx.InMempool()) return false;
+
+ // Trusted if all inputs are from us and are in the mempool:
+ for (const CTxIn& txin : wtx.tx->vin)
+ {
+ // Transactions not sent by us: not trusted
+ const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
+ if (parent == nullptr) return false;
+ const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
+ // Check that this specific input being spent is trusted
+ if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ // If we've already trusted this parent, continue
+ if (trusted_parents.count(parent->GetHash())) continue;
+ // Recurse to check that the parent is also trusted
+ if (!IsTrusted(*parent, trusted_parents)) return false;
+ trusted_parents.insert(parent->GetHash());
+ }
+ return true;
+}
+
+bool CWalletTx::IsTrusted() const
+{
+ std::set<uint256> trusted_parents;
+ LOCK(pwallet->cs_wallet);
+ return pwallet->IsTrusted(*this, trusted_parents);
+}
+
+CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const
+{
+ Balance ret;
+ isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
+ {
+ LOCK(cs_wallet);
+ std::set<uint256> trusted_parents;
+ for (const auto& entry : mapWallet)
+ {
+ const CWalletTx& wtx = entry.second;
+ const bool is_trusted{IsTrusted(wtx, trusted_parents)};
+ const int tx_depth{wtx.GetDepthInMainChain()};
+ const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
+ const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
+ if (is_trusted && tx_depth >= min_depth) {
+ ret.m_mine_trusted += tx_credit_mine;
+ ret.m_watchonly_trusted += tx_credit_watchonly;
+ }
+ if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
+ ret.m_mine_untrusted_pending += tx_credit_mine;
+ ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
+ }
+ ret.m_mine_immature += wtx.GetImmatureCredit();
+ ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit();
+ }
+ }
+ return ret;
+}
+
+std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
+{
+ std::map<CTxDestination, CAmount> balances;
+
+ {
+ LOCK(cs_wallet);
+ std::set<uint256> trusted_parents;
+ for (const auto& walletEntry : mapWallet)
+ {
+ const CWalletTx& wtx = walletEntry.second;
+
+ if (!IsTrusted(wtx, trusted_parents))
+ continue;
+
+ if (wtx.IsImmatureCoinBase())
+ continue;
+
+ int nDepth = wtx.GetDepthInMainChain();
+ if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
+ continue;
+
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
+ {
+ CTxDestination addr;
+ if (!IsMine(wtx.tx->vout[i]))
+ continue;
+ if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
+ continue;
+
+ CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
+ balances[addr] += n;
+ }
+ }
+ }
+
+ return balances;
+}
+
+std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
+{
+ AssertLockHeld(cs_wallet);
+ std::set< std::set<CTxDestination> > groupings;
+ std::set<CTxDestination> grouping;
+
+ for (const auto& walletEntry : mapWallet)
+ {
+ const CWalletTx& wtx = walletEntry.second;
+
+ if (wtx.tx->vin.size() > 0)
+ {
+ bool any_mine = false;
+ // group all input addresses with each other
+ for (const CTxIn& txin : wtx.tx->vin)
+ {
+ CTxDestination address;
+ if(!IsMine(txin)) /* If this input isn't mine, ignore it */
+ continue;
+ if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
+ continue;
+ grouping.insert(address);
+ any_mine = true;
+ }
+
+ // group change with input addresses
+ if (any_mine)
+ {
+ for (const CTxOut& txout : wtx.tx->vout)
+ if (IsChange(txout))
+ {
+ CTxDestination txoutAddr;
+ if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
+ continue;
+ grouping.insert(txoutAddr);
+ }
+ }
+ if (grouping.size() > 0)
+ {
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+ }
+
+ // group lone addrs by themselves
+ for (const auto& txout : wtx.tx->vout)
+ if (IsMine(txout))
+ {
+ CTxDestination address;
+ if(!ExtractDestination(txout.scriptPubKey, address))
+ continue;
+ grouping.insert(address);
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+ }
+
+ std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
+ std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it
+ for (std::set<CTxDestination> _grouping : groupings)
+ {
+ // make a set of all the groups hit by this new group
+ std::set< std::set<CTxDestination>* > hits;
+ std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;
+ for (const CTxDestination& address : _grouping)
+ if ((it = setmap.find(address)) != setmap.end())
+ hits.insert((*it).second);
+
+ // merge all hit groups into a new single group and delete old groups
+ std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);
+ for (std::set<CTxDestination>* hit : hits)
+ {
+ merged->insert(hit->begin(), hit->end());
+ uniqueGroupings.erase(hit);
+ delete hit;
+ }
+ uniqueGroupings.insert(merged);
+
+ // update setmap
+ for (const CTxDestination& element : *merged)
+ setmap[element] = merged;
+ }
+
+ std::set< std::set<CTxDestination> > ret;
+ for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings)
+ {
+ ret.insert(*uniqueGrouping);
+ delete uniqueGrouping;
+ }
+
+ return ret;
+}
diff --git a/src/wallet/receive.h b/src/wallet/receive.h
new file mode 100644
index 0000000000..8eead32413
--- /dev/null
+++ b/src/wallet/receive.h
@@ -0,0 +1,20 @@
+// 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_WALLET_RECEIVE_H
+#define BITCOIN_WALLET_RECEIVE_H
+
+#include <amount.h>
+#include <wallet/ismine.h>
+#include <wallet/transaction.h>
+#include <wallet/wallet.h>
+
+struct COutputEntry
+{
+ CTxDestination destination;
+ CAmount amount;
+ int vout;
+};
+
+#endif // BITCOIN_WALLET_RECEIVE_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 769987ccf2..726b13beac 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -100,8 +100,8 @@ RPCHelpMan importprivkey()
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
- {"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
+ {"label", RPCArg::Type::STR, RPCArg::DefaultHint{"current label if address exists, otherwise \"\""}, "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -232,9 +232,9 @@ RPCHelpMan importaddress()
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
- {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
- {"p2sh", RPCArg::Type::BOOL, /* default */ "false", "Add the P2SH version of the script as well"},
+ {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
+ {"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false}, "Add the P2SH version of the script as well"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -426,8 +426,8 @@ RPCHelpMan importpubkey()
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
- {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
+ {"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -469,7 +469,7 @@ RPCHelpMan importpubkey()
if (!IsHex(request.params[0].get_str()))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
- CPubKey pubKey(data.begin(), data.end());
+ CPubKey pubKey(data);
if (!pubKey.IsFullyValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
@@ -557,7 +557,7 @@ RPCHelpMan importwallet()
// Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
// we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
- pwallet->chain().showProgress(strprintf("%s " + _("Importing...").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
+ pwallet->chain().showProgress(strprintf("%s " + _("Importing…").translated, pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
std::vector<std::pair<CScript, int64_t>> scripts;
while (file.good()) {
@@ -871,7 +871,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
switch (script_type) {
case TxoutType::PUBKEY: {
- CPubKey pubkey(solverdata[0].begin(), solverdata[0].end());
+ CPubKey pubkey(solverdata[0]);
import_data.used_keys.emplace(pubkey.GetID(), false);
return "";
}
@@ -893,7 +893,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
case TxoutType::MULTISIG: {
for (size_t i = 1; i + 1< solverdata.size(); ++i) {
- CPubKey pubkey(solverdata[i].begin(), solverdata[i].end());
+ CPubKey pubkey(solverdata[i]);
import_data.used_keys.emplace(pubkey.GetID(), false);
}
return "";
@@ -997,7 +997,7 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
}
auto parsed_pubkey = ParseHex(str);
- CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
+ CPubKey pubkey(parsed_pubkey);
if (!pubkey.IsFullyValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
}
@@ -1279,28 +1279,28 @@ RPCHelpMan importmulti()
},
{"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
{"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
- {"pubkeys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
+ {"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
{
{"pubKey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"keys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
+ {"keys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
{
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
- {"internal", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
- {"watchonly", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be considered watchonly."},
- {"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
- {"keypool", RPCArg::Type::BOOL, /* default */ "false", "Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
+ {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
+ {"watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether matching outputs should be considered watchonly."},
+ {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
+ {"keypool", RPCArg::Type::BOOL, RPCArg::Default{false}, "Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
},
},
},
"\"requests\""},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Stating if should rescan the blockchain after all imports"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Stating if should rescan the blockchain after all imports"},
},
"\"options\""},
},
@@ -1591,7 +1591,7 @@ RPCHelpMan importdescriptors()
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "Descriptor to import."},
- {"active", RPCArg::Type::BOOL, /* default */ "false", "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
+ {"active", RPCArg::Type::BOOL, RPCArg::Default{false}, "Set this descriptor to be the active descriptor for the corresponding output type/externality"},
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
{"next_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "If a ranged descriptor is set to active, this specifies the next index to generate addresses from"},
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Time from which to start rescanning the blockchain for this descriptor, in " + UNIX_EPOCH_TIME + "\n"
@@ -1601,8 +1601,8 @@ RPCHelpMan importdescriptors()
" of all descriptors being imported will be scanned.",
/* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
},
- {"internal", RPCArg::Type::BOOL, /* default */ "false", "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
- {"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
+ {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
+ {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"},
},
},
},
diff --git a/src/wallet/rpcsigner.cpp b/src/wallet/rpcsigner.cpp
deleted file mode 100644
index 696c74d665..0000000000
--- a/src/wallet/rpcsigner.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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
deleted file mode 100644
index f3ab83c428..0000000000
--- a/src/wallet/rpcsigner.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 df26f039a0..534c974178 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -216,7 +216,8 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un
if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
}
- cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN);
+ // Fee rates in sat/vB cannot represent more than 3 significant digits.
+ cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /* decimals */ 3)};
if (override_min_fee) cc.fOverrideFeeRate = true;
// Default RBF to true for explicit fee_rate, if unset.
if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true;
@@ -237,8 +238,8 @@ static RPCHelpMan getnewaddress()
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with 'label'.\n",
{
- {"label", RPCArg::Type::STR, /* default */ "\"\"", "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
- {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
+ {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
RPCResult::Type::STR, "address", "The new bitcoin address"
@@ -287,7 +288,7 @@ static RPCHelpMan getrawchangeaddress()
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
{
- {"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
RPCResult::Type::STR, "address", "The address"
@@ -439,16 +440,16 @@ static RPCHelpMan sendtoaddress()
{"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
"to which you're sending the transaction. This is not part of the \n"
"transaction, just kept in your wallet."},
- {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
+ {"subtractfeefromamount", RPCArg::Type::BOOL, RPCArg::Default{false}, "The fee will be deducted from the amount being sent.\n"
"The recipient will receive less bitcoins than you enter in the amount field."},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
- {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
- "dirty if they have previously been used in a transaction."},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra information about the transaction."},
+ {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
+ "dirty if they have previously been used in a transaction. If true, this also activates avoidpartialspends, grouping outputs by their addresses."},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."},
},
{
RPCResult{"if verbose is not set or set to false",
@@ -540,7 +541,7 @@ static RPCHelpMan listaddressgroupings()
{
{RPCResult::Type::ARR, "", "",
{
- {RPCResult::Type::ARR, "", "",
+ {RPCResult::Type::ARR_FIXED, "", "",
{
{RPCResult::Type::STR, "address", "The bitcoin address"},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
@@ -697,7 +698,7 @@ static RPCHelpMan getreceivedbyaddress()
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
},
RPCResult{
RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
@@ -735,7 +736,7 @@ static RPCHelpMan getreceivedbylabel()
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
},
RPCResult{
RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
@@ -775,9 +776,9 @@ static RPCHelpMan getbalance()
"thus affected by options which limit spendability such as -spendzeroconfchange.\n",
{
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
- {"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also include balance in watch-only addresses (see 'importaddress')"},
- {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
+ {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
@@ -853,7 +854,7 @@ static RPCHelpMan sendmany()
HELP_REQUIRING_PASSPHRASE,
{
{"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
- {"amounts", RPCArg::Type::OBJ, RPCArg::Optional::NO, "The addresses and amounts",
+ {"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
},
@@ -868,12 +869,12 @@ static RPCHelpMan sendmany()
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra infomration about the transaction."},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra infomration about the transaction."},
},
{
RPCResult{"if verbose is not set or set to false",
@@ -956,7 +957,7 @@ static RPCHelpMan addmultisigaddress()
},
},
{"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
- {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1185,9 +1186,9 @@ static RPCHelpMan listreceivedbyaddress()
return RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
- {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include addresses that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
@@ -1234,9 +1235,9 @@ static RPCHelpMan listreceivedbylabel()
return RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
- {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include labels that haven't received any payments."},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
+ {"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include labels that haven't received any payments."},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
},
RPCResult{
RPCResult::Type::ARR, "", "",
@@ -1281,7 +1282,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
/**
* List transactions based on the given criteria.
*
- * @param pwallet The wallet.
+ * @param wallet The wallet.
* @param wtx The wallet transaction.
* @param nMinDepth The minimum confirmation depth.
* @param fLong Whether to include the JSON version of the transaction.
@@ -1396,9 +1397,9 @@ static RPCHelpMan listtransactions()
{
{"label|dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n"
"with the specified label, or \"*\" to disable filtering and return all transactions."},
- {"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"},
- {"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
+ {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
RPCResult::Type::ARR, "", "",
@@ -1507,9 +1508,9 @@ static RPCHelpMan listsinceblock()
"Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
{
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
- {"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
- {"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
+ {"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
+ {"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
"(not guaranteed to work on pruned nodes)"},
},
RPCResult{
@@ -1645,9 +1646,9 @@ static RPCHelpMan gettransaction()
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false",
+ {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"},
"Whether to include watch-only addresses in balance calculation and details[]"},
- {"verbose", RPCArg::Type::BOOL, /* default */ "false",
+ {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
"Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
},
RPCResult{
@@ -1832,7 +1833,7 @@ static RPCHelpMan keypoolrefill()
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
{
- {"newsize", RPCArg::Type::NUM, /* default */ "100", "The new keypool size"},
+ {"newsize", RPCArg::Type::NUM, RPCArg::Default{100}, "The new keypool size"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -2124,7 +2125,7 @@ static RPCHelpMan lockunspent()
"Also see the listunspent call\n",
{
{"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
- {"transactions", RPCArg::Type::ARR, /* default */ "empty array", "The transaction outputs and within each, the txid (string) vout (numeric).",
+ {"transactions", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The transaction outputs and within each, the txid (string) vout (numeric).",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -2292,10 +2293,10 @@ static RPCHelpMan listlockunspent()
static RPCHelpMan settxfee()
{
return RPCHelpMan{"settxfee",
- "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
+ "\nSet the transaction fee rate in " + CURRENCY_UNIT + "/kvB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
{
- {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kvB"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_UNIT + "/kvB"},
},
RPCResult{
RPCResult::Type::BOOL, "", "Returns true if successful"
@@ -2567,7 +2568,7 @@ static RPCHelpMan loadwallet()
"\napplied to the new wallet (eg -rescan, etc).\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
- {"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."},
+ {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2630,7 +2631,7 @@ static RPCHelpMan setwalletflag()
"\nChange the state of the given wallet flag for a wallet.\n",
{
{"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
- {"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."},
+ {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2693,13 +2694,13 @@ static RPCHelpMan createwallet()
"\nCreates and loads a new wallet.\n",
{
{"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
- {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
- {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
- {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
- {"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."},
+ {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
+ {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
+ {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
+ {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
+ {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
+ {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "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, RPCArg::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, "", "",
@@ -2711,6 +2712,8 @@ static RPCHelpMan createwallet()
RPCExamples{
HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
+ + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
+ + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -2748,7 +2751,7 @@ static RPCHelpMan createwallet()
#ifdef ENABLE_EXTERNAL_SIGNER
flags |= WALLET_FLAG_EXTERNAL_SIGNER;
#else
- throw JSONRPCError(RPC_WALLET_ERROR, "Configure with --enable-external-signer to use this");
+ throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
#endif
}
@@ -2786,8 +2789,8 @@ static RPCHelpMan unloadwallet()
"Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
"Specifying the wallet name on a wallet endpoint is invalid.",
{
- {"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC endpoint", "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
- {"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."},
+ {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
+ {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
},
RPCResult{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
@@ -2838,21 +2841,21 @@ static RPCHelpMan listunspent()
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n",
{
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum confirmations to filter"},
- {"maxconf", RPCArg::Type::NUM, /* default */ "9999999", "The maximum confirmations to filter"},
- {"addresses", RPCArg::Type::ARR, /* default */ "empty array", "The bitcoin addresses to filter",
+ {"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
+ {"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
+ {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The bitcoin addresses to filter",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address"},
},
},
- {"include_unsafe", RPCArg::Type::BOOL, /* default */ "true", "Include outputs that are not safe to spend\n"
+ {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
"See description of \"safe\" attribute below."},
{"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
{
- {"minimumAmount", RPCArg::Type::AMOUNT, /* default */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
- {"maximumCount", RPCArg::Type::NUM, /* default */ "unlimited", "Maximum number of UTXOs"},
- {"minimumSumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
+ {"minimumAmount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)}, "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
+ {"maximumCount", RPCArg::Type::NUM, RPCArg::DefaultHint{"unlimited"}, "Maximum number of UTXOs"},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"unlimited"}, "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
},
"query_options"},
},
@@ -2967,8 +2970,9 @@ static RPCHelpMan listunspent()
cctl.m_avoid_address_reuse = false;
cctl.m_min_depth = nMinDepth;
cctl.m_max_depth = nMaxDepth;
+ cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
+ pwallet->AvailableCoins(vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
}
LOCK(pwallet->cs_wallet);
@@ -3073,6 +3077,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
RPCTypeCheckObj(options,
{
{"add_inputs", UniValueType(UniValue::VBOOL)},
+ {"include_unsafe", UniValueType(UniValue::VBOOL)},
{"add_to_wallet", UniValueType(UniValue::VBOOL)},
{"changeAddress", UniValueType(UniValue::VSTR)},
{"change_address", UniValueType(UniValue::VSTR)},
@@ -3133,6 +3138,10 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
}
+ if (options.exists("include_unsafe")) {
+ coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
+ }
+
if (options.exists("feeRate")) {
if (options.exists("fee_rate")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)");
@@ -3202,17 +3211,20 @@ static RPCHelpMan fundrawtransaction()
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
{
- {"add_inputs", RPCArg::Type::BOOL, /* default */ "true", "For a transaction with existing inputs, automatically include more if they are not enough."},
- {"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The bitcoin address to receive the change"},
- {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
- {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only.\n"
+ {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
+ {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
+ "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
+ "If that happens, you will need to fund the transaction with different inputs and republish it."},
+ {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
- {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The integers.\n"
+ {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The integers.\n"
"The fee will be equally deducted from the amount of each specified output.\n"
"Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
"If no outputs are specified here, the sender pays the fee.",
@@ -3220,14 +3232,14 @@ static RPCHelpMan fundrawtransaction()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
"Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
- {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
+ {"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
"If iswitness is not present, heuristic tests will be used in decoding.\n"
"If true, only witness deserialization will be tried.\n"
"If false, only non-witness deserialization will be tried.\n"
@@ -3308,7 +3320,7 @@ RPCHelpMan signrawtransactionwithwallet()
},
},
},
- {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -3400,30 +3412,28 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks\n"},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation",
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks\n"},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"},
"\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n"
"Must be at least " + incremental_fee + " higher than the current transaction fee rate.\n"
"WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB.\n"},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether the new transaction should still be\n"
"marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
"be left unchanged from the original. If false, any input sequence numbers in the\n"
"original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
"so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
"still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
"are replaceable).\n"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
"\"" + FeeModes("\"\n\"") + "\""},
},
"options"},
},
RPCResult{
- RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
- {
- {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."},
- },
- want_psbt ? std::vector<RPCResult>{} : std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."}}
- ),
+ RPCResult::Type::OBJ, "", "", Cat(
+ want_psbt ?
+ std::vector<RPCResult>{{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."}} :
+ std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction."}},
{
{RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
{RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
@@ -3434,7 +3444,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
})
},
RPCExamples{
- "\nBump the fee, get the new transaction\'s" + std::string(want_psbt ? "psbt" : "txid") + "\n" +
+ "\nBump the fee, get the new transaction\'s " + std::string(want_psbt ? "psbt" : "txid") + "\n" +
HelpExampleCli(method_name, "<txid>")
},
[want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
@@ -3516,8 +3526,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
UniValue result(UniValue::VOBJ);
- // If wallet private keys are enabled, return the new transaction id,
- // otherwise return the base64-encoded unsigned PSBT of the new transaction.
+ // For bumpfee, return the new transaction id.
+ // For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction.
if (!want_psbt) {
if (!feebumper::SignTransaction(*pwallet, mtx)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
@@ -3562,7 +3572,7 @@ static RPCHelpMan rescanblockchain()
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
- {"start_height", RPCArg::Type::NUM, /* default */ "0", "block height where the rescan should start"},
+ {"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "block height where the rescan should start"},
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
},
RPCResult{
@@ -3725,6 +3735,7 @@ public:
return obj;
}
+ UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
@@ -4009,7 +4020,7 @@ static RPCHelpMan send()
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
{
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
},
@@ -4021,35 +4032,38 @@ static RPCHelpMan send()
},
},
},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"add_inputs", RPCArg::Type::BOOL, /* default */ "false", "If inputs are specified, automatically include more if they are not enough."},
- {"add_to_wallet", RPCArg::Type::BOOL, /* default */ "true", "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
- {"change_address", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
- {"change_position", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
- {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
+ {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
+ "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
+ "If that happens, you will need to fund the transaction with different inputs and republish it."},
+ {"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
+ {"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"include_watching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only.\n"
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
- {"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
},
},
- {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
- {"lock_unspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
- {"psbt", RPCArg::Type::BOOL, /* default */ "automatic", "Always return a PSBT, implies add_to_wallet=false."},
- {"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "Outputs to subtract the fee from, specified as integer indices.\n"
+ {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
+ {"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
+ {"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
"The fee will be equally deducted from the amount of each specified output.\n"
"Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
"If no outputs are specified here, the sender pays the fee.",
@@ -4057,7 +4071,7 @@ static RPCHelpMan send()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
"Allows this transaction to be replaced by a transaction with higher fees"},
},
"options"},
@@ -4205,11 +4219,11 @@ static RPCHelpMan sethdseed()
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
HELP_REQUIRING_PASSPHRASE,
{
- {"newkeypool", RPCArg::Type::BOOL, /* default */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
+ {"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
"If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
"If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
"keypool will be used until it has been depleted."},
- {"seed", RPCArg::Type::STR, /* default */ "random seed", "The WIF private key to use as the new HD seed.\n"
+ {"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
"The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -4276,15 +4290,15 @@ static RPCHelpMan walletprocesspsbt()
HELP_REQUIRING_PASSPHRASE,
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
- {"sign", RPCArg::Type::BOOL, /* default */ "true", "Also sign the transaction when updating"},
- {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\""},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -4345,7 +4359,7 @@ static RPCHelpMan walletcreatefundedpsbt()
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
- {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'locktime' and 'options.replaceable' arguments", "The sequence number"},
+ {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"},
},
},
},
@@ -4355,7 +4369,7 @@ static RPCHelpMan walletcreatefundedpsbt()
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
"accepted as second parameter.",
{
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
{
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
},
@@ -4367,18 +4381,21 @@ static RPCHelpMan walletcreatefundedpsbt()
},
},
},
- {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"add_inputs", RPCArg::Type::BOOL, /* default */ "false", "If inputs are specified, automatically include more if they are not enough."},
- {"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
- {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
- {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
- {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
- {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
- {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
- {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The outputs to subtract the fee from.\n"
+ {"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
+ {"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
+ "Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
+ "If that happens, you will need to fund the transaction with different inputs and republish it."},
+ {"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
+ {"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
+ {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
+ {"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The outputs to subtract the fee from.\n"
"The fee will be equally deducted from the amount of each specified output.\n"
"Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
"If no outputs are specified here, the sender pays the fee.",
@@ -4386,14 +4403,14 @@ static RPCHelpMan walletcreatefundedpsbt()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
"Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -4466,7 +4483,7 @@ static RPCHelpMan upgradewallet()
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
"New keys may be generated and a new wallet backup will need to be made.",
{
- {"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version."}
+ {"version", RPCArg::Type::NUM, RPCArg::Default{FEATURE_LATEST}, "The version number to upgrade to. Default is the latest wallet version."}
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -4524,6 +4541,48 @@ static RPCHelpMan upgradewallet()
};
}
+#ifdef ENABLE_EXTERNAL_SIGNER
+static RPCHelpMan walletdisplayaddress()
+{
+ return RPCHelpMan{"walletdisplayaddress",
+ "Display address on an external signer for verification.",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ,"","",
+ {
+ {RPCResult::Type::STR, "address", "The address as confirmed by the signer"},
+ }
+ },
+ 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_MISC_ERROR, "Failed to display address");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("address", request.params[0].get_str());
+ return result;
+ }
+ };
+}
+#endif // ENABLE_EXTERNAL_SIGNER
+
RPCHelpMan abortrescan();
RPCHelpMan dumpprivkey();
RPCHelpMan importprivkey();
@@ -4600,6 +4659,9 @@ static const CRPCCommand commands[] =
{ "wallet", &unloadwallet, },
{ "wallet", &upgradewallet, },
{ "wallet", &walletcreatefundedpsbt, },
+#ifdef ENABLE_EXTERNAL_SIGNER
+ { "wallet", &walletdisplayaddress, },
+#endif // ENABLE_EXTERNAL_SIGNER
{ "wallet", &walletlock, },
{ "wallet", &walletpassphrase, },
{ "wallet", &walletpassphrasechange, },
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index 6d912be019..ea045eb6d8 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -154,8 +154,8 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
continue;
}
- Dbt datKey(&row.first[0], row.first.size());
- Dbt datValue(&row.second[0], row.second.size());
+ Dbt datKey(row.first.data(), row.first.size());
+ Dbt datValue(row.second.data(), row.second.size());
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
if (ret2 > 0)
fSuccess = false;
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 54319ca662..2eb9ca5c6d 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -13,7 +13,7 @@
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
-#include <wallet/external_signer.h>
+#include <external_signer.h>
#include <wallet/scriptpubkeyman.h>
#include <optional>
@@ -84,7 +84,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
//! Recursively solve script and return spendable/watchonly/invalid status.
//!
//! @param keystore legacy key and script store
-//! @param script script to solve
+//! @param scriptPubKey script to solve
//! @param sigversion script type (top-level / redeemscript / witnessscript)
//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
//! scripts or simply treat any script that has been
@@ -162,7 +162,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
break;
}
uint160 hash;
- CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
+ CRIPEMD160().Write(vSolutions[0].data(), vSolutions[0].size()).Finalize(hash.begin());
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
new file mode 100644
index 0000000000..97fc7acca5
--- /dev/null
+++ b/src/wallet/spend.cpp
@@ -0,0 +1,978 @@
+// 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 <consensus/validation.h>
+#include <interfaces/chain.h>
+#include <policy/policy.h>
+#include <util/check.h>
+#include <util/fees.h>
+#include <util/moneystr.h>
+#include <util/rbf.h>
+#include <util/translation.h>
+#include <wallet/coincontrol.h>
+#include <wallet/fees.h>
+#include <wallet/receive.h>
+#include <wallet/spend.h>
+#include <wallet/transaction.h>
+#include <wallet/wallet.h>
+
+using interfaces::FoundBlock;
+
+static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100};
+
+std::string COutput::ToString() const
+{
+ return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
+}
+
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
+{
+ CMutableTransaction txn;
+ txn.vin.push_back(CTxIn(COutPoint()));
+ if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
+ return -1;
+ }
+ return GetVirtualTransactionInputSize(txn.vin[0]);
+}
+
+// txouts needs to be in the order of tx.vin
+TxSize 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 TxSize{-1, -1};
+ }
+ CTransaction ctx(txNew);
+ int64_t vsize = GetVirtualTransactionSize(ctx);
+ int64_t weight = GetTransactionWeight(ctx);
+ return TxSize{vsize, weight};
+}
+
+TxSize 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 TxSize{-1, -1};
+ }
+ assert(input.prevout.n < mi->second.tx->vout.size());
+ txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
+ }
+ return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
+}
+
+void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
+{
+ AssertLockHeld(cs_wallet);
+
+ vCoins.clear();
+ CAmount nTotal = 0;
+ // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
+ // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
+ bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
+ const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
+ const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
+ const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true};
+
+ std::set<uint256> trusted_parents;
+ for (const auto& entry : mapWallet)
+ {
+ const uint256& wtxid = entry.first;
+ const CWalletTx& wtx = entry.second;
+
+ if (!chain().checkFinalTx(*wtx.tx)) {
+ continue;
+ }
+
+ if (wtx.IsImmatureCoinBase())
+ continue;
+
+ int nDepth = wtx.GetDepthInMainChain();
+ if (nDepth < 0)
+ continue;
+
+ // We should not consider coins which aren't at least in our mempool
+ // It's possible for these to be conflicted via ancestors which we may never be able to detect
+ if (nDepth == 0 && !wtx.InMempool())
+ continue;
+
+ bool safeTx = IsTrusted(wtx, trusted_parents);
+
+ // We should not consider coins from transactions that are replacing
+ // other transactions.
+ //
+ // Example: There is a transaction A which is replaced by bumpfee
+ // transaction B. In this case, we want to prevent creation of
+ // a transaction B' which spends an output of B.
+ //
+ // Reason: If transaction A were initially confirmed, transactions B
+ // and B' would no longer be valid, so the user would have to create
+ // a new transaction C to replace B'. However, in the case of a
+ // one-block reorg, transactions B' and C might BOTH be accepted,
+ // when the user only wanted one of them. Specifically, there could
+ // be a 1-block reorg away from the chain where transactions A and C
+ // were accepted to another chain where B, B', and C were all
+ // accepted.
+ if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
+ safeTx = false;
+ }
+
+ // Similarly, we should not consider coins from transactions that
+ // have been replaced. In the example above, we would want to prevent
+ // creation of a transaction A' spending an output of A, because if
+ // transaction B were initially confirmed, conflicting with A and
+ // A', we wouldn't want to the user to create a transaction D
+ // intending to replace A', but potentially resulting in a scenario
+ // where A, A', and D could all be accepted (instead of just B and
+ // D, or just A and A' like the user would want).
+ if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
+ safeTx = false;
+ }
+
+ if (only_safe && !safeTx) {
+ continue;
+ }
+
+ if (nDepth < min_depth || nDepth > max_depth) {
+ continue;
+ }
+
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
+ // Only consider selected coins if add_inputs is false
+ if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) {
+ continue;
+ }
+
+ if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
+ continue;
+
+ if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
+ continue;
+
+ if (IsLockedCoin(entry.first, i))
+ continue;
+
+ if (IsSpent(wtxid, i))
+ continue;
+
+ isminetype mine = IsMine(wtx.tx->vout[i]);
+
+ if (mine == ISMINE_NO) {
+ continue;
+ }
+
+ if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
+ continue;
+ }
+
+ std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
+
+ bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
+ bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
+
+ vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
+
+ // Checks the sum amount of all UTXO's.
+ if (nMinimumSumAmount != MAX_MONEY) {
+ nTotal += wtx.tx->vout[i].nValue;
+
+ if (nTotal >= nMinimumSumAmount) {
+ return;
+ }
+ }
+
+ // Checks the maximum number of UTXO's.
+ if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
+ return;
+ }
+ }
+ }
+}
+
+CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
+{
+ LOCK(cs_wallet);
+
+ CAmount balance = 0;
+ std::vector<COutput> vCoins;
+ AvailableCoins(vCoins, coinControl);
+ for (const COutput& out : vCoins) {
+ if (out.fSpendable) {
+ balance += out.tx->tx->vout[out.i].nValue;
+ }
+ }
+ return balance;
+}
+
+const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
+{
+ AssertLockHeld(cs_wallet);
+ const CTransaction* ptx = &tx;
+ int n = output;
+ while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
+ const COutPoint& prevout = ptx->vin[0].prevout;
+ auto it = mapWallet.find(prevout.hash);
+ if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
+ !IsMine(it->second.tx->vout[prevout.n])) {
+ break;
+ }
+ ptx = it->second.tx.get();
+ n = prevout.n;
+ }
+ return ptx->vout[n];
+}
+
+std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
+{
+ AssertLockHeld(cs_wallet);
+
+ std::map<CTxDestination, std::vector<COutput>> result;
+ std::vector<COutput> availableCoins;
+
+ AvailableCoins(availableCoins);
+
+ for (const COutput& coin : availableCoins) {
+ CTxDestination address;
+ if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
+ ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
+ result[address].emplace_back(std::move(coin));
+ }
+ }
+
+ std::vector<COutPoint> lockedCoins;
+ ListLockedCoins(lockedCoins);
+ // Include watch-only for LegacyScriptPubKeyMan wallets without private keys
+ const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
+ for (const COutPoint& output : lockedCoins) {
+ auto it = mapWallet.find(output.hash);
+ if (it != mapWallet.end()) {
+ int depth = it->second.GetDepthInMainChain();
+ if (depth >= 0 && output.n < it->second.tx->vout.size() &&
+ IsMine(it->second.tx->vout[output.n]) == is_mine_filter
+ ) {
+ CTxDestination address;
+ if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
+ result[address].emplace_back(
+ &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const
+{
+ std::vector<OutputGroup> groups_out;
+
+ if (!coin_sel_params.m_avoid_partial_spends) {
+ // Allowing partial spends means no grouping. Each COutput gets its own OutputGroup.
+ for (const COutput& output : outputs) {
+ // Skip outputs we cannot spend
+ if (!output.fSpendable) continue;
+
+ size_t ancestors, descendants;
+ chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ CInputCoin input_coin = output.GetInputCoin();
+
+ // Make an OutputGroup containing just this output
+ OutputGroup group{coin_sel_params};
+ group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
+
+ // Check the OutputGroup's eligibility. Only add the eligible ones.
+ if (positive_only && group.GetSelectionAmount() <= 0) continue;
+ if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group);
+ }
+ return groups_out;
+ }
+
+ // We want to combine COutputs that have the same scriptPubKey into single OutputGroups
+ // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup.
+ // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups.
+ // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added
+ // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has
+ // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector.
+ std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map;
+ for (const auto& output : outputs) {
+ // Skip outputs we cannot spend
+ if (!output.fSpendable) continue;
+
+ size_t ancestors, descendants;
+ chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ CInputCoin input_coin = output.GetInputCoin();
+ CScript spk = input_coin.txout.scriptPubKey;
+
+ std::vector<OutputGroup>& groups = spk_to_groups_map[spk];
+
+ if (groups.size() == 0) {
+ // No OutputGroups for this scriptPubKey yet, add one
+ groups.emplace_back(coin_sel_params);
+ }
+
+ // Get the last OutputGroup in the vector so that we can add the CInputCoin to it
+ // A pointer is used here so that group can be reassigned later if it is full.
+ OutputGroup* group = &groups.back();
+
+ // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends
+ // to avoid surprising users with very high fees.
+ if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) {
+ // The last output group is full, add a new group to the vector and use that group for the insertion
+ groups.emplace_back(coin_sel_params);
+ group = &groups.back();
+ }
+
+ // Add the input_coin to group
+ group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
+ }
+
+ // Now we go through the entire map and pull out the OutputGroups
+ for (const auto& spk_and_groups_pair: spk_to_groups_map) {
+ const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second;
+
+ // Go through the vector backwards. This allows for the first item we deal with being the partial group.
+ for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) {
+ const OutputGroup& group = *group_it;
+
+ // Don't include partial groups if there are full groups too and we don't want partial groups
+ if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) {
+ continue;
+ }
+
+ // Check the OutputGroup's eligibility. Only add the eligible ones.
+ if (positive_only && group.GetSelectionAmount() <= 0) continue;
+ if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group);
+ }
+ }
+
+ return groups_out;
+}
+
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const
+{
+ setCoinsRet.clear();
+ nValueRet = 0;
+
+ // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
+ std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */);
+ if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet)) {
+ return true;
+ }
+ // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
+ std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */);
+ // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
+ // So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
+ return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, setCoinsRet, nValueRet);
+}
+
+bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const
+{
+ std::vector<COutput> vCoins(vAvailableCoins);
+ CAmount value_to_select = nTargetValue;
+
+ // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
+ if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
+ {
+ for (const COutput& out : vCoins)
+ {
+ if (!out.fSpendable)
+ continue;
+ nValueRet += out.tx->tx->vout[out.i].nValue;
+ setCoinsRet.insert(out.GetInputCoin());
+ }
+ return (nValueRet >= nTargetValue);
+ }
+
+ // calculate value from preset inputs and store them
+ std::set<CInputCoin> setPresetCoins;
+ CAmount nValueFromPresetInputs = 0;
+
+ std::vector<COutPoint> vPresetInputs;
+ coin_control.ListSelected(vPresetInputs);
+ for (const COutPoint& outpoint : vPresetInputs)
+ {
+ std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
+ if (it != mapWallet.end())
+ {
+ const CWalletTx& wtx = it->second;
+ // Clearly invalid input, fail
+ if (wtx.tx->vout.size() <= outpoint.n) {
+ return false;
+ }
+ // Just to calculate the marginal byte size
+ CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false));
+ nValueFromPresetInputs += coin.txout.nValue;
+ 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.m_effective_feerate.GetFee(coin.m_input_bytes);
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ value_to_select -= coin.txout.nValue;
+ } else {
+ value_to_select -= coin.effective_value;
+ }
+ setPresetCoins.insert(coin);
+ } else {
+ return false; // TODO: Allow non-wallet inputs
+ }
+ }
+
+ // remove preset inputs from vCoins so that Coin Selection doesn't pick them.
+ for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
+ {
+ if (setPresetCoins.count(it->GetInputCoin()))
+ it = vCoins.erase(it);
+ else
+ ++it;
+ }
+
+ unsigned int limit_ancestor_count = 0;
+ unsigned int limit_descendant_count = 0;
+ chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
+ const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
+ const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
+ const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
+
+ // form groups from remaining coins; note that preset coins will not
+ // automatically have their associated (same address) coins included
+ if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) {
+ // Cases where we have 101+ outputs all pointing to the same destination may result in
+ // privacy leaks as they will potentially be deterministically sorted. We solve that by
+ // explicitly shuffling the outputs before processing
+ Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
+ }
+
+ // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the
+ // transaction at a target feerate. If an attempt fails, more attempts may be made using a more
+ // permissive CoinEligibilityFilter.
+ const bool res = [&] {
+ // Pre-selected inputs already cover the target amount.
+ if (value_to_select <= 0) return true;
+
+ // If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
+ // confirmations on outputs received from other wallets and only spend confirmed change.
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+
+ // Fall back to using zero confirmation change (but with as few ancestors in the mempool as
+ // possible) if we cannot fund the transaction otherwise.
+ if (m_spend_zero_conf_change) {
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
+ vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
+ return true;
+ }
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
+ vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
+ return true;
+ }
+ // If partial groups are allowed, relax the requirement of spending OutputGroups (groups
+ // of UTXOs sent to the same address, which are obviously controlled by a single wallet)
+ // in their entirety.
+ if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
+ vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
+ return true;
+ }
+ // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
+ // received from other wallets.
+ if (coin_control.m_include_unsafe_inputs
+ && SelectCoinsMinConf(value_to_select,
+ CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
+ vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
+ return true;
+ }
+ // Try with unlimited ancestors/descendants. The transaction will still need to meet
+ // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
+ // OutputGroups use heuristics that may overestimate ancestor/descendant counts.
+ if (!fRejectLongChains && SelectCoinsMinConf(value_to_select,
+ CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
+ vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
+ return true;
+ }
+ }
+ // Coin Selection failed.
+ return false;
+ }();
+
+ // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset
+ util::insert(setCoinsRet, setPresetCoins);
+
+ // add preset inputs to the total value selected
+ nValueRet += nValueFromPresetInputs;
+
+ return res;
+}
+
+static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)
+{
+ if (chain.isInitialBlockDownload()) {
+ return false;
+ }
+ constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds
+ int64_t block_time;
+ CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time)));
+ if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Return a height-based locktime for new transactions (uses the height of the
+ * current chain tip unless we are not synced with the current chain
+ */
+static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height)
+{
+ uint32_t locktime;
+ // Discourage fee sniping.
+ //
+ // For a large miner the value of the transactions in the best block and
+ // the mempool can exceed the cost of deliberately attempting to mine two
+ // blocks to orphan the current best block. By setting nLockTime such that
+ // only the next block can include the transaction, we discourage this
+ // practice as the height restricted and limited blocksize gives miners
+ // considering fee sniping fewer options for pulling off this attack.
+ //
+ // A simple way to think about this is from the wallet's point of view we
+ // always want the blockchain to move forward. By setting nLockTime this
+ // way we're basically making the statement that we only want this
+ // transaction to appear in the next block; we don't want to potentially
+ // encourage reorgs by allowing transactions to appear at lower heights
+ // than the next block in forks of the best chain.
+ //
+ // Of course, the subsidy is high enough, and transaction volume low
+ // enough, that fee sniping isn't a problem yet, but by implementing a fix
+ // now we ensure code won't be written that makes assumptions about
+ // nLockTime that preclude a fix later.
+ if (IsCurrentForAntiFeeSniping(chain, block_hash)) {
+ locktime = block_height;
+
+ // Secondly occasionally randomly pick a nLockTime even further back, so
+ // that transactions that are delayed after signing for whatever reason,
+ // e.g. high-latency mix networks and some CoinJoin implementations, have
+ // better privacy.
+ if (GetRandInt(10) == 0)
+ locktime = std::max(0, (int)locktime - GetRandInt(100));
+ } else {
+ // If our chain is lagging behind, we can't discourage fee sniping nor help
+ // the privacy of high-latency transactions. To avoid leaking a potentially
+ // unique "nLockTime fingerprint", set nLockTime to a constant.
+ locktime = 0;
+ }
+ assert(locktime < LOCKTIME_THRESHOLD);
+ return locktime;
+}
+
+bool CWallet::CreateTransactionInternal(
+ const std::vector<CRecipient>& vecSend,
+ CTransactionRef& tx,
+ CAmount& nFeeRet,
+ int& nChangePosInOut,
+ bilingual_str& error,
+ const CCoinControl& coin_control,
+ FeeCalculation& fee_calc_out,
+ bool sign)
+{
+ CAmount nValue = 0;
+ const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
+ ReserveDestination reservedest(this, change_type);
+ unsigned int nSubtractFeeFromAmount = 0;
+ for (const auto& recipient : vecSend)
+ {
+ if (nValue < 0 || recipient.nAmount < 0)
+ {
+ error = _("Transaction amounts must not be negative");
+ return false;
+ }
+ nValue += recipient.nAmount;
+
+ if (recipient.fSubtractFeeFromAmount)
+ nSubtractFeeFromAmount++;
+ }
+ if (vecSend.empty())
+ {
+ error = _("Transaction must have at least one recipient");
+ return false;
+ }
+
+ CMutableTransaction txNew;
+ FeeCalculation feeCalc;
+ TxSize tx_sizes;
+ int nBytes;
+ {
+ std::set<CInputCoin> setCoins;
+ LOCK(cs_wallet);
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
+ {
+ std::vector<COutput> vAvailableCoins;
+ AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
+ CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
+ coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
+
+ // Create change script that will be used if we need change
+ // TODO: pass in scriptChange instead of reservedest so
+ // change transaction isn't always pay-to-bitcoin-address
+ CScript scriptChange;
+
+ // coin control: send change to custom address
+ if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
+ scriptChange = GetScriptForDestination(coin_control.destChange);
+ } else { // no coin control: send change to newly generated address
+ // Note: We use a new key here to keep it from being obvious which side is the change.
+ // The drawback is that by not reusing a previous key, the change may be lost if a
+ // backup is restored, if the backup doesn't have the new private key for the change.
+ // If we reused the old key, it would be possible to add code to look for and
+ // rediscover unknown transactions that were written with keys of ours to recover
+ // post-backup change.
+
+ // Reserve a new key pair from key pool. If it fails, provide a dummy
+ // destination in case we don't need change.
+ CTxDestination dest;
+ if (!reservedest.GetReservedDestination(dest, true)) {
+ error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
+ }
+ scriptChange = GetScriptForDestination(dest);
+ // A valid destination implies a change script (and
+ // vice-versa). An empty change script will abort later, if the
+ // change keypool ran out, but change is required.
+ CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
+ }
+ CTxOut change_prototype_txout(0, scriptChange);
+ coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
+
+ // Get size of spending the change output
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
+ }
+
+ // Set discard feerate
+ coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+
+ // Get the fee rate to use effective values in coin selection
+ 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 && 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);
+
+ // Calculate the cost of change
+ // Cost of change is the cost of creating the change output + cost of spending the change output in the future.
+ // For creating the change output now, we use the effective feerate.
+ // For spending the change output in the future, we use the discard feerate for now.
+ // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
+ coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
+ coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+
+ coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
+
+ // vouts to the payees
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ }
+ for (const auto& recipient : vecSend)
+ {
+ CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+
+ // Include the fee cost for outputs.
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ }
+
+ if (IsDust(txout, chain().relayDustFee()))
+ {
+ error = _("Transaction amount too small");
+ return false;
+ }
+ txNew.vout.push_back(txout);
+ }
+
+ // Include the fees for things that aren't inputs, excluding the change output
+ const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
+ CAmount nValueToSelect = nValue + not_input_fees;
+
+ // Choose coins to use
+ CAmount inputs_sum = 0;
+ setCoins.clear();
+ if (!SelectCoins(vAvailableCoins, /* nTargetValue */ nValueToSelect, setCoins, inputs_sum, coin_control, coin_selection_params))
+ {
+ error = _("Insufficient funds");
+ return false;
+ }
+
+ // Always make a change output
+ // We will reduce the fee from this change output later, and remove the output if it is too small.
+ const CAmount change_and_fee = inputs_sum - nValue;
+ assert(change_and_fee >= 0);
+ CTxOut newTxOut(change_and_fee, scriptChange);
+
+ if (nChangePosInOut == -1)
+ {
+ // Insert change txn at random position:
+ nChangePosInOut = GetRandInt(txNew.vout.size()+1);
+ }
+ else if ((unsigned int)nChangePosInOut > txNew.vout.size())
+ {
+ error = _("Change index out of range");
+ return false;
+ }
+
+ assert(nChangePosInOut != -1);
+ auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
+
+ // Dummy fill vin for maximum size estimation
+ //
+ for (const auto& coin : setCoins) {
+ txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
+ }
+
+ // Calculate the transaction fee
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.vsize;
+ if (nBytes < 0) {
+ error = _("Signing transaction failed");
+ return false;
+ }
+ nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+
+ // Subtract fee from the change output if not subtrating it from recipient outputs
+ CAmount fee_needed = nFeeRet;
+ if (nSubtractFeeFromAmount == 0) {
+ change_position->nValue -= fee_needed;
+ }
+
+ // We want to drop the change to fees if:
+ // 1. The change output would be dust
+ // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
+ CAmount change_amount = change_position->nValue;
+ if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
+ {
+ nChangePosInOut = -1;
+ change_amount = 0;
+ txNew.vout.erase(change_position);
+
+ // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.vsize;
+ fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ }
+
+ // Update nFeeRet in case fee_needed changed due to dropping the change output
+ if (fee_needed <= change_and_fee - change_amount) {
+ nFeeRet = change_and_fee - change_amount;
+ }
+
+ // Reduce output values for subtractFeeFromAmount
+ if (nSubtractFeeFromAmount != 0) {
+ CAmount to_reduce = fee_needed + change_amount - change_and_fee;
+ int i = 0;
+ bool fFirst = true;
+ for (const auto& recipient : vecSend)
+ {
+ if (i == nChangePosInOut) {
+ ++i;
+ }
+ CTxOut& txout = txNew.vout[i];
+
+ if (recipient.fSubtractFeeFromAmount)
+ {
+ txout.nValue -= to_reduce / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
+
+ if (fFirst) // first receiver pays the remainder not divisible by output count
+ {
+ fFirst = false;
+ txout.nValue -= to_reduce % nSubtractFeeFromAmount;
+ }
+
+ // Error if this output is reduced to be below dust
+ if (IsDust(txout, chain().relayDustFee())) {
+ if (txout.nValue < 0) {
+ error = _("The transaction amount is too small to pay the fee");
+ } else {
+ error = _("The transaction amount is too small to send after the fee has been deducted");
+ }
+ return false;
+ }
+ }
+ ++i;
+ }
+ nFeeRet = fee_needed;
+ }
+
+ // Give up if change keypool ran out and change is required
+ if (scriptChange.empty() && nChangePosInOut != -1) {
+ return false;
+ }
+ }
+
+ // Shuffle selected coins and fill in final vin
+ txNew.vin.clear();
+ std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
+ Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
+
+ // Note how the sequence number is set to non-maxint so that
+ // the nLockTime set above actually works.
+ //
+ // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
+ // we use the highest possible value in that range (maxint-2)
+ // to avoid conflicting with other possible uses of nSequence,
+ // and in the spirit of "smallest possible change from prior
+ // behavior."
+ const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ for (const auto& coin : selected_coins) {
+ txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
+ }
+
+ if (sign && !SignTransaction(txNew)) {
+ error = _("Signing transaction failed");
+ return false;
+ }
+
+ // Return the constructed transaction data.
+ tx = MakeTransactionRef(std::move(txNew));
+
+ // Limit size
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
+ {
+ error = _("Transaction too large");
+ return false;
+ }
+ }
+
+ if (nFeeRet > m_default_max_tx_fee) {
+ error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
+ return false;
+ }
+
+ if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
+ // Lastly, ensure this tx will pass the mempool's chain limits
+ if (!chain().checkChainLimits(tx)) {
+ error = _("Transaction has too long of a mempool chain");
+ return false;
+ }
+ }
+
+ // Before we return success, we assume any change key will be used to prevent
+ // accidental re-use.
+ reservedest.KeepDestination();
+ fee_calc_out = feeCalc;
+
+ WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
+ nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
+ feeCalc.est.pass.start, feeCalc.est.pass.end,
+ (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0,
+ feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,
+ feeCalc.est.fail.start, feeCalc.est.fail.end,
+ (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
+ feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
+ return true;
+}
+
+bool CWallet::CreateTransaction(
+ const std::vector<CRecipient>& vecSend,
+ CTransactionRef& tx,
+ CAmount& nFeeRet,
+ int& nChangePosInOut,
+ bilingual_str& error,
+ const CCoinControl& coin_control,
+ FeeCalculation& fee_calc_out,
+ bool sign)
+{
+ int nChangePosIn = nChangePosInOut;
+ Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
+ bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
+ // try with avoidpartialspends unless it's enabled already
+ if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
+ CCoinControl tmp_cc = coin_control;
+ tmp_cc.m_avoid_partial_spends = true;
+ CAmount nFeeRet2;
+ CTransactionRef tx2;
+ int nChangePosInOut2 = nChangePosIn;
+ bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
+ if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
+ // if fee of this alternative one is within the range of the max fee, we use this one
+ const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee;
+ WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
+ if (use_aps) {
+ tx = tx2;
+ nFeeRet = nFeeRet2;
+ nChangePosInOut = nChangePosInOut2;
+ }
+ }
+ }
+ return res;
+}
+
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
+{
+ std::vector<CRecipient> vecSend;
+
+ // Turn the txout set into a CRecipient vector.
+ for (size_t idx = 0; idx < tx.vout.size(); idx++) {
+ const CTxOut& txOut = tx.vout[idx];
+ CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
+ vecSend.push_back(recipient);
+ }
+
+ coinControl.fAllowOtherInputs = true;
+
+ for (const CTxIn& txin : tx.vin) {
+ coinControl.Select(txin.prevout);
+ }
+
+ // Acquire the locks to prevent races to the new locked unspents between the
+ // CreateTransaction call and LockCoin calls (when lockUnspents is true).
+ LOCK(cs_wallet);
+
+ CTransactionRef tx_new;
+ FeeCalculation fee_calc_out;
+ if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
+ return false;
+ }
+
+ if (nChangePosInOut != -1) {
+ tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
+ }
+
+ // Copy output sizes from new transaction; they may have had the fee
+ // subtracted from them.
+ for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
+ tx.vout[idx].nValue = tx_new->vout[idx].nValue;
+ }
+
+ // Add new txins while keeping original txin scriptSig/order.
+ for (const CTxIn& txin : tx_new->vin) {
+ if (!coinControl.IsSelected(txin.prevout)) {
+ tx.vin.push_back(txin);
+
+ }
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
+ }
+
+ }
+
+ return true;
+}
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
new file mode 100644
index 0000000000..03f9a7c2b5
--- /dev/null
+++ b/src/wallet/spend.h
@@ -0,0 +1,64 @@
+// 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_WALLET_SPEND_H
+#define BITCOIN_WALLET_SPEND_H
+
+#include <wallet/coinselection.h>
+#include <wallet/transaction.h>
+#include <wallet/wallet.h>
+
+class COutput
+{
+public:
+ const CWalletTx *tx;
+
+ /** Index in tx->vout. */
+ int i;
+
+ /**
+ * Depth in block chain.
+ * If > 0: the tx is on chain and has this many confirmations.
+ * If = 0: the tx is waiting confirmation.
+ * If < 0: a conflicting tx is on chain and has this many confirmations. */
+ int nDepth;
+
+ /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
+ int nInputBytes;
+
+ /** Whether we have the private keys to spend this output */
+ bool fSpendable;
+
+ /** Whether we know how to spend this output, ignoring the lack of keys */
+ bool fSolvable;
+
+ /** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */
+ bool use_max_sig;
+
+ /**
+ * Whether this output is considered safe to spend. Unconfirmed transactions
+ * from outside keys and unconfirmed replacement transactions are considered
+ * unsafe and will not be used to fund new spending transactions.
+ */
+ bool fSafe;
+
+ COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
+ {
+ tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
+ // If known and signable by the given wallet, compute nInputBytes
+ // Failure will keep this value -1
+ if (fSpendable && tx) {
+ nInputBytes = tx->GetSpendSize(i, use_max_sig);
+ }
+ }
+
+ std::string ToString() const;
+
+ inline CInputCoin GetInputCoin() const
+ {
+ return CInputCoin(tx->tx, i, nInputBytes);
+ }
+};
+
+#endif // BITCOIN_WALLET_SPEND_H
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 975974cb6a..2e60aca017 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -16,6 +16,10 @@
#include <sqlite3.h>
#include <stdint.h>
+#include <optional>
+#include <utility>
+#include <vector>
+
static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
static Mutex g_sqlite_mutex;
@@ -32,6 +36,36 @@ static void ErrorLogCallback(void* arg, int code, const char* msg)
LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
}
+static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error)
+{
+ std::string stmt_text = strprintf("PRAGMA %s", key);
+ sqlite3_stmt* pragma_read_stmt{nullptr};
+ int ret = sqlite3_prepare_v2(db, stmt_text.c_str(), -1, &pragma_read_stmt, nullptr);
+ if (ret != SQLITE_OK) {
+ sqlite3_finalize(pragma_read_stmt);
+ error = Untranslated(strprintf("SQLiteDatabase: Failed to prepare the statement to fetch %s: %s", description, sqlite3_errstr(ret)));
+ return std::nullopt;
+ }
+ ret = sqlite3_step(pragma_read_stmt);
+ if (ret != SQLITE_ROW) {
+ sqlite3_finalize(pragma_read_stmt);
+ error = Untranslated(strprintf("SQLiteDatabase: Failed to fetch %s: %s", description, sqlite3_errstr(ret)));
+ return std::nullopt;
+ }
+ int result = sqlite3_column_int(pragma_read_stmt, 0);
+ sqlite3_finalize(pragma_read_stmt);
+ return result;
+}
+
+static void SetPragma(sqlite3* db, const std::string& key, const std::string& value, const std::string& err_msg)
+{
+ std::string stmt_text = strprintf("PRAGMA %s = %s", key, value);
+ int ret = sqlite3_exec(db, stmt_text.c_str(), nullptr, nullptr, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: %s: %s\n", err_msg, sqlite3_errstr(ret)));
+ }
+}
+
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock)
: WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
{
@@ -69,30 +103,21 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa
void SQLiteBatch::SetupSQLStatements()
{
- int res;
- if (!m_read_stmt) {
- if ((res = sqlite3_prepare_v2(m_database.m_db, "SELECT value FROM main WHERE key = ?", -1, &m_read_stmt, nullptr)) != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
- }
- }
- if (!m_insert_stmt) {
- if ((res = sqlite3_prepare_v2(m_database.m_db, "INSERT INTO main VALUES(?, ?)", -1, &m_insert_stmt, nullptr)) != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
- }
- }
- if (!m_overwrite_stmt) {
- if ((res = sqlite3_prepare_v2(m_database.m_db, "INSERT or REPLACE into main values(?, ?)", -1, &m_overwrite_stmt, nullptr)) != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
- }
- }
- if (!m_delete_stmt) {
- if ((res = sqlite3_prepare_v2(m_database.m_db, "DELETE FROM main WHERE key = ?", -1, &m_delete_stmt, nullptr)) != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
- }
- }
- if (!m_cursor_stmt) {
- if ((res = sqlite3_prepare_v2(m_database.m_db, "SELECT key, value FROM main", -1, &m_cursor_stmt, nullptr)) != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements : %s\n", sqlite3_errstr(res)));
+ const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
+ {&m_read_stmt, "SELECT value FROM main WHERE key = ?"},
+ {&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"},
+ {&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"},
+ {&m_delete_stmt, "DELETE FROM main WHERE key = ?"},
+ {&m_cursor_stmt, "SELECT key, value FROM main"},
+ };
+
+ for (const auto& [stmt_prepared, stmt_text] : statements) {
+ if (*stmt_prepared == nullptr) {
+ int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, stmt_prepared, nullptr);
+ if (res != SQLITE_OK) {
+ throw std::runtime_error(strprintf(
+ "SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
+ }
}
}
}
@@ -120,21 +145,9 @@ bool SQLiteDatabase::Verify(bilingual_str& error)
assert(m_db);
// Check the application ID matches our network magic
- sqlite3_stmt* app_id_stmt{nullptr};
- int ret = sqlite3_prepare_v2(m_db, "PRAGMA application_id", -1, &app_id_stmt, nullptr);
- if (ret != SQLITE_OK) {
- sqlite3_finalize(app_id_stmt);
- error = strprintf(_("SQLiteDatabase: Failed to prepare the statement to fetch the application id: %s"), sqlite3_errstr(ret));
- return false;
- }
- ret = sqlite3_step(app_id_stmt);
- if (ret != SQLITE_ROW) {
- sqlite3_finalize(app_id_stmt);
- error = strprintf(_("SQLiteDatabase: Failed to fetch the application id: %s"), sqlite3_errstr(ret));
- return false;
- }
- uint32_t app_id = static_cast<uint32_t>(sqlite3_column_int(app_id_stmt, 0));
- sqlite3_finalize(app_id_stmt);
+ auto read_result = ReadPragmaInteger(m_db, "application_id", "the application id", error);
+ if (!read_result.has_value()) return false;
+ uint32_t app_id = static_cast<uint32_t>(read_result.value());
uint32_t net_magic = ReadBE32(Params().MessageStart());
if (app_id != net_magic) {
error = strprintf(_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), net_magic, app_id);
@@ -142,28 +155,16 @@ bool SQLiteDatabase::Verify(bilingual_str& error)
}
// Check our schema version
- sqlite3_stmt* user_ver_stmt{nullptr};
- ret = sqlite3_prepare_v2(m_db, "PRAGMA user_version", -1, &user_ver_stmt, nullptr);
- if (ret != SQLITE_OK) {
- sqlite3_finalize(user_ver_stmt);
- error = strprintf(_("SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet schema version: %s"), sqlite3_errstr(ret));
- return false;
- }
- ret = sqlite3_step(user_ver_stmt);
- if (ret != SQLITE_ROW) {
- sqlite3_finalize(user_ver_stmt);
- error = strprintf(_("SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s"), sqlite3_errstr(ret));
- return false;
- }
- int32_t user_ver = sqlite3_column_int(user_ver_stmt, 0);
- sqlite3_finalize(user_ver_stmt);
+ read_result = ReadPragmaInteger(m_db, "user_version", "sqlite wallet schema version", error);
+ if (!read_result.has_value()) return false;
+ int32_t user_ver = read_result.value();
if (user_ver != WALLET_SCHEMA_VERSION) {
error = strprintf(_("SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported"), user_ver, WALLET_SCHEMA_VERSION);
return false;
}
sqlite3_stmt* stmt{nullptr};
- ret = sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr);
+ int ret = sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr);
if (ret != SQLITE_OK) {
sqlite3_finalize(stmt);
error = strprintf(_("SQLiteDatabase: Failed to prepare statement to verify database: %s"), sqlite3_errstr(ret));
@@ -219,12 +220,9 @@ void SQLiteDatabase::Open()
// Acquire an exclusive lock on the database
// First change the locking mode to exclusive
- int ret = sqlite3_exec(m_db, "PRAGMA locking_mode = exclusive", nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Unable to change database locking mode to exclusive: %s\n", sqlite3_errstr(ret)));
- }
+ SetPragma(m_db, "locking_mode", "exclusive", "Unable to change database locking mode to exclusive");
// Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.
- ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
+ int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
}
@@ -234,9 +232,12 @@ void SQLiteDatabase::Open()
}
// Enable fullfsync for the platforms that use it
- ret = sqlite3_exec(m_db, "PRAGMA fullfsync = true", nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable fullfsync: %s\n", sqlite3_errstr(ret)));
+ SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync");
+
+ if (gArgs.GetBoolArg("-unsafesqlitesync", false)) {
+ // Use normal synchronous mode for the journal
+ LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n");
+ SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
}
// Make the table for our key-value pairs
@@ -268,18 +269,12 @@ void SQLiteDatabase::Open()
// Set the application id
uint32_t app_id = ReadBE32(Params().MessageStart());
- std::string set_app_id = strprintf("PRAGMA application_id = %d", static_cast<int32_t>(app_id));
- ret = sqlite3_exec(m_db, set_app_id.c_str(), nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to set the application id: %s\n", sqlite3_errstr(ret)));
- }
+ SetPragma(m_db, "application_id", strprintf("%d", static_cast<int32_t>(app_id)),
+ "Failed to set the application id");
// Set the user version
- std::string set_user_ver = strprintf("PRAGMA user_version = %d", WALLET_SCHEMA_VERSION);
- ret = sqlite3_exec(m_db, set_user_ver.c_str(), nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- throw std::runtime_error(strprintf("SQLiteDatabase: Failed to set the wallet schema version: %s\n", sqlite3_errstr(ret)));
- }
+ SetPragma(m_db, "user_version", strprintf("%d", WALLET_SCHEMA_VERSION),
+ "Failed to set the wallet schema version");
}
}
@@ -353,31 +348,22 @@ void SQLiteBatch::Close()
}
// Free all of the prepared statements
- int ret = sqlite3_finalize(m_read_stmt);
- if (ret != SQLITE_OK) {
- LogPrintf("SQLiteBatch: Batch closed but could not finalize read statement: %s\n", sqlite3_errstr(ret));
- }
- ret = sqlite3_finalize(m_insert_stmt);
- if (ret != SQLITE_OK) {
- LogPrintf("SQLiteBatch: Batch closed but could not finalize insert statement: %s\n", sqlite3_errstr(ret));
- }
- ret = sqlite3_finalize(m_overwrite_stmt);
- if (ret != SQLITE_OK) {
- LogPrintf("SQLiteBatch: Batch closed but could not finalize overwrite statement: %s\n", sqlite3_errstr(ret));
- }
- ret = sqlite3_finalize(m_delete_stmt);
- if (ret != SQLITE_OK) {
- LogPrintf("SQLiteBatch: Batch closed but could not finalize delete statement: %s\n", sqlite3_errstr(ret));
- }
- ret = sqlite3_finalize(m_cursor_stmt);
- if (ret != SQLITE_OK) {
- LogPrintf("SQLiteBatch: Batch closed but could not finalize cursor statement: %s\n", sqlite3_errstr(ret));
+ const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
+ {&m_read_stmt, "read"},
+ {&m_insert_stmt, "insert"},
+ {&m_overwrite_stmt, "overwrite"},
+ {&m_delete_stmt, "delete"},
+ {&m_cursor_stmt, "cursor"},
+ };
+
+ for (const auto& [stmt_prepared, stmt_description] : statements) {
+ int res = sqlite3_finalize(*stmt_prepared);
+ if (res != SQLITE_OK) {
+ LogPrintf("SQLiteBatch: Batch closed but could not finalize %s statement: %s\n",
+ stmt_description, sqlite3_errstr(res));
+ }
+ *stmt_prepared = nullptr;
}
- m_read_stmt = nullptr;
- m_insert_stmt = nullptr;
- m_overwrite_stmt = nullptr;
- m_delete_stmt = nullptr;
- m_cursor_stmt = nullptr;
}
bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 7eff6e592d..14c3578473 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -35,7 +35,7 @@ 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(/* use_bnb= */ false, /* change_output_size= */ 0,
+CoinSelectionParams coin_selection_params(/* 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);
@@ -148,14 +148,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CoinSet selection;
CoinSet actual_selection;
CAmount value_ret = 0;
- CAmount not_input_fees = 0;
/////////////////////////
// Known Outcome tests //
/////////////////////////
// Empty utxo pool
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret));
selection.clear();
// Add utxos
@@ -166,7 +165,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Select 1 Cent
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret));
BOOST_CHECK(equal_sets(selection, actual_selection));
BOOST_CHECK_EQUAL(value_ret, 1 * CENT);
actual_selection.clear();
@@ -174,7 +173,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Select 2 Cent
add_coin(2 * CENT, 2, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret));
BOOST_CHECK(equal_sets(selection, actual_selection));
BOOST_CHECK_EQUAL(value_ret, 2 * CENT);
actual_selection.clear();
@@ -183,27 +182,27 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Select 5 Cent
add_coin(4 * CENT, 4, actual_selection);
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret));
BOOST_CHECK(equal_sets(selection, actual_selection));
BOOST_CHECK_EQUAL(value_ret, 5 * CENT);
actual_selection.clear();
selection.clear();
// Select 11 Cent, not possible
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT, selection, value_ret));
actual_selection.clear();
selection.clear();
// Cost of change is greater than the difference between target value and utxo sum
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret));
BOOST_CHECK_EQUAL(value_ret, 1 * CENT);
BOOST_CHECK(equal_sets(selection, actual_selection));
actual_selection.clear();
selection.clear();
// Cost of change is less than the difference between target value and utxo sum
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret));
actual_selection.clear();
selection.clear();
@@ -212,7 +211,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
add_coin(5 * CENT, 5, actual_selection);
add_coin(4 * CENT, 4, actual_selection);
add_coin(1 * CENT, 1, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret));
BOOST_CHECK(equal_sets(selection, actual_selection));
BOOST_CHECK_EQUAL(value_ret, 10 * CENT);
actual_selection.clear();
@@ -223,21 +222,21 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
add_coin(5 * CENT, 5, actual_selection);
add_coin(3 * CENT, 3, actual_selection);
add_coin(2 * CENT, 2, actual_selection);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret));
BOOST_CHECK_EQUAL(value_ret, 10 * CENT);
// FIXME: this test is redundant with the above, because 1 Cent is selected, not "too small"
// BOOST_CHECK(equal_sets(selection, actual_selection));
// Select 0.25 Cent, not possible
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret));
actual_selection.clear();
selection.clear();
// Iteration exhaustion test
CAmount target = make_hard_case(17, utxo_pool);
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should exhaust
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret)); // Should exhaust
target = make_hard_case(14, utxo_pool);
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should not exhaust
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret)); // Should not exhaust
// Test same value early bailout optimization
utxo_pool.clear();
@@ -254,7 +253,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
for (int i = 0; i < 50000; ++i) {
add_coin(5 * CENT, 7, utxo_pool);
}
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret, not_input_fees));
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret));
BOOST_CHECK_EQUAL(value_ret, 30 * CENT);
BOOST_CHECK(equal_sets(selection, actual_selection));
@@ -268,37 +267,34 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
// Run 100 times, to make sure it is never finding a solution
for (int i = 0; i < 100; ++i) {
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret));
}
// Make sure that effective value is working in SelectCoinsMinConf when BnB is used
- CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0,
+ CoinSelectionParams coin_selection_params_bnb(/* 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;
empty_wallet();
add_coin(1);
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
// Test fees subtracted from output:
empty_wallet();
add_coin(1 * CENT);
vCoins.at(0).nInputBytes = 40;
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
coin_selection_params_bnb.m_subtract_fee_outputs = true;
- BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
// Make sure that can use BnB when there are preset inputs
empty_wallet();
{
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
- bool firstRun;
- wallet->LoadWallet(firstRun);
+ wallet->LoadWallet();
wallet->SetupLegacyScriptPubKeyMan();
LOCK(wallet->cs_wallet);
add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true);
@@ -308,9 +304,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_control.fAllowOtherInputs = true;
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
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);
+ BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
}
}
@@ -318,7 +312,6 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
CoinSet setCoinsRet, setCoinsRet2;
CAmount nValueRet;
- bool bnb_used;
LOCK(testWallet.cs_wallet);
testWallet.SetupLegacyScriptPubKeyMan();
@@ -329,24 +322,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can find a new 1 cent
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can make 3 cents of new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -356,33 +349,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -396,30 +389,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -428,11 +421,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -447,14 +440,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -462,7 +455,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -471,7 +464,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -484,7 +477,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -494,7 +487,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -505,12 +498,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
}
@@ -524,7 +517,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
for (int i = 0; i < RUN_TESTS; i++) {
- BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
@@ -550,17 +543,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
for (int i = 0; i < RUN_TESTS; i++) {
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
for (int j = 0; j < RANDOM_REPEATS; j++)
{
- // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
- // run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ // Test that the KnapsackSolver selects randomly from equivalent coins (same value and same input size).
+ // When choosing 1 from 100 identical coins, 1% of the time, this test will choose the same coin twice
+ // which will cause it to fail.
+ // To avoid that issue, run the test RANDOM_REPEATS times and only complain if all of them fail
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -580,10 +575,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
int fails = 0;
for (int j = 0; j < RANDOM_REPEATS; j++)
{
- // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
- // run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used));
- BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -598,7 +591,6 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
{
CoinSet setCoinsRet;
CAmount nValueRet;
- bool bnb_used;
LOCK(testWallet.cs_wallet);
testWallet.SetupLegacyScriptPubKeyMan();
@@ -610,7 +602,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -620,6 +612,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
+ LOCK(testWallet.cs_wallet);
testWallet.SetupLegacyScriptPubKeyMan();
// Random generator stuff
@@ -645,19 +638,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CAmount target = rand.randrange(balance - 1000) + 1000;
// Perform selection
- 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);
+ CoinSelectionParams cs_params(/* 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;
- BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) ||
- testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used));
+ CCoinControl cc;
+ BOOST_CHECK(testWallet.SelectCoins(vCoins, target, out_set, out_value, cc, cs_params));
BOOST_CHECK_GE(out_value, target);
}
}
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index b2eb8e4bca..17f5264b45 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -23,7 +23,7 @@ static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, s
BOOST_AUTO_TEST_CASE(getwalletenv_file)
{
std::string test_name = "test_name.dat";
- const fs::path datadir = GetDataDir();
+ const fs::path datadir = gArgs.GetDataDirNet();
fs::path file_path = datadir / test_name;
std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR);
f.close();
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
{
std::string expected_name = "wallet.dat";
- const fs::path datadir = GetDataDir();
+ const fs::path datadir = gArgs.GetDataDirNet();
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
@@ -47,8 +47,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
{
- fs::path datadir = GetDataDir() / "1";
- fs::path datadir_2 = GetDataDir() / "2";
+ fs::path datadir = gArgs.GetDataDirNet() / "1";
+ fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
@@ -61,8 +61,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
{
- fs::path datadir = GetDataDir() / "1";
- fs::path datadir_2 = GetDataDir() / "2";
+ fs::path datadir = gArgs.GetDataDirNet() / "1";
+ fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
std::string filename;
std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index f035a70a20..dd9354848d 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -16,7 +16,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
std::string sep;
sep += fs::path::preferred_separator;
- m_datadir = GetDataDir();
+ m_datadir = gArgs.GetDataDirNet();
m_cwd = fs::current_path();
m_walletdir_path_cases["default"] = m_datadir / "wallets";
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index badf2eb459..fc744ebe5b 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -8,8 +8,7 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase())
{
- bool fFirstRun;
- m_wallet.LoadWallet(fFirstRun);
+ m_wallet.LoadWallet();
m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
m_wallet_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index ba2e17d62a..6a791748b4 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -11,6 +11,7 @@
#include <vector>
#include <interfaces/chain.h>
+#include <node/blockstorage.h>
#include <node/context.h>
#include <policy/policy.h>
#include <rpc/server.h>
@@ -37,7 +38,7 @@ static_assert(WALLET_INCREMENTAL_RELAY_FEE >= DEFAULT_INCREMENTAL_RELAY_FEE, "wa
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
-static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
+static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain* chain)
{
DatabaseOptions options;
DatabaseStatus status;
@@ -45,7 +46,9 @@ static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
std::vector<bilingual_str> warnings;
auto database = MakeWalletDatabase("", options, status, error);
auto wallet = CWallet::Create(chain, "", std::move(database), options.create_flags, error, warnings);
- wallet->postInitProcess();
+ if (chain) {
+ wallet->postInitProcess();
+ }
return wallet;
}
@@ -213,8 +216,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
key.pushKV("internal", UniValue(true));
keys.push_back(key);
- std::any context;
- JSONRPCRequest request(context);
+ JSONRPCRequest request;
request.params.setArray();
request.params.push_back(keys);
@@ -251,7 +253,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
- std::string backup_file = (GetDataDir() / "wallet.backup").string();
+ std::string backup_file = (gArgs.GetDataDirNet() / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file.
{
@@ -265,8 +267,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
- std::any context;
- JSONRPCRequest request(context);
+ JSONRPCRequest request;
request.params.setArray();
request.params.push_back(backup_file);
@@ -281,8 +282,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
- std::any context;
- JSONRPCRequest request(context);
+ JSONRPCRequest request;
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
@@ -298,8 +298,6 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_CHECK_EQUAL(found, expected);
}
}
-
- SetMockTime(0);
}
// Check that GetImmatureCredit() returns a newly calculated value instead of
@@ -380,9 +378,6 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
// If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time.
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
-
- // Reset mock time for other tests.
- SetMockTime(0);
}
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
@@ -390,11 +385,11 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
CTxDestination dest = PKHash();
LOCK(m_wallet.cs_wallet);
WalletBatch batch{m_wallet.GetDatabase()};
- m_wallet.AddDestData(batch, dest, "misc", "val_misc");
- m_wallet.AddDestData(batch, dest, "rr0", "val_rr0");
- m_wallet.AddDestData(batch, dest, "rr1", "val_rr1");
+ m_wallet.SetAddressUsed(batch, dest, true);
+ m_wallet.SetAddressReceiveRequest(batch, dest, "0", "val_rr0");
+ m_wallet.SetAddressReceiveRequest(batch, dest, "1", "val_rr1");
- auto values = m_wallet.GetDestValues("rr");
+ auto values = m_wallet.GetAddressReceiveRequests();
BOOST_CHECK_EQUAL(values.size(), 2U);
BOOST_CHECK_EQUAL(values[0], "val_rr0");
BOOST_CHECK_EQUAL(values[1], "val_rr1");
@@ -490,8 +485,7 @@ public:
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
}
- bool firstRun;
- wallet->LoadWallet(firstRun);
+ wallet->LoadWallet();
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(*wallet);
reserver.reserve();
@@ -695,8 +689,9 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup)
//! rescanning where new transactions in new blocks could be lost.
BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
{
+ gArgs.ForceSetArg("-unsafesqlitesync", "1");
// Create new wallet with known key and unload it.
- auto wallet = TestLoadWallet(*m_node.chain);
+ auto wallet = TestLoadWallet(m_node.chain.get());
CKey key;
key.MakeNewKey(true);
AddKey(*wallet, key);
@@ -736,7 +731,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
// Reload wallet and make sure new transactions are detected despite events
// being blocked
- wallet = TestLoadWallet(*m_node.chain);
+ wallet = TestLoadWallet(m_node.chain.get());
BOOST_CHECK(rescan_completed);
BOOST_CHECK_EQUAL(addtx_count, 2);
{
@@ -776,7 +771,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
ENTER_CRITICAL_SECTION(wallet->wallet()->cs_wallet);
ENTER_CRITICAL_SECTION(cs_wallets);
});
- wallet = TestLoadWallet(*m_node.chain);
+ wallet = TestLoadWallet(m_node.chain.get());
BOOST_CHECK_EQUAL(addtx_count, 4);
{
LOCK(wallet->cs_wallet);
@@ -788,9 +783,17 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
TestUnloadWallet(std::move(wallet));
}
+BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup)
+{
+ auto wallet = TestLoadWallet(nullptr);
+ BOOST_CHECK(wallet);
+ UnloadWallet(std::move(wallet));
+}
+
BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
{
- auto wallet = TestLoadWallet(*m_node.chain);
+ gArgs.ForceSetArg("-unsafesqlitesync", "1");
+ auto wallet = TestLoadWallet(m_node.chain.get());
CKey key;
key.MakeNewKey(true);
AddKey(*wallet, key);
diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp
new file mode 100644
index 0000000000..cf98b516f1
--- /dev/null
+++ b/src/wallet/transaction.cpp
@@ -0,0 +1,25 @@
+// 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 <wallet/transaction.h>
+
+bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
+{
+ CMutableTransaction tx1 {*this->tx};
+ CMutableTransaction tx2 {*_tx.tx};
+ for (auto& txin : tx1.vin) txin.scriptSig = CScript();
+ for (auto& txin : tx2.vin) txin.scriptSig = CScript();
+ return CTransaction(tx1) == CTransaction(tx2);
+}
+
+bool CWalletTx::InMempool() const
+{
+ return fInMempool;
+}
+
+int64_t CWalletTx::GetTxTime() const
+{
+ int64_t n = nTimeSmart;
+ return n ? n : nTimeReceived;
+}
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
new file mode 100644
index 0000000000..131faefe0b
--- /dev/null
+++ b/src/wallet/transaction.h
@@ -0,0 +1,358 @@
+// 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_WALLET_TRANSACTION_H
+#define BITCOIN_WALLET_TRANSACTION_H
+
+#include <amount.h>
+#include <primitives/transaction.h>
+#include <serialize.h>
+#include <wallet/ismine.h>
+#include <threadsafety.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+
+#include <list>
+#include <vector>
+
+struct COutputEntry;
+
+typedef std::map<std::string, std::string> mapValue_t;
+
+//Get the marginal bytes of spending the specified output
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
+
+static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
+{
+ if (!mapValue.count("n"))
+ {
+ nOrderPos = -1; // TODO: calculate elsewhere
+ return;
+ }
+ nOrderPos = atoi64(mapValue["n"]);
+}
+
+static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
+{
+ if (nOrderPos == -1)
+ return;
+ mapValue["n"] = ToString(nOrderPos);
+}
+
+/** Legacy class used for deserializing vtxPrev for backwards compatibility.
+ * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
+ * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
+ * These need to get deserialized for field alignment when deserializing
+ * a CWalletTx, but the deserialized values are discarded.**/
+class CMerkleTx
+{
+public:
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ CTransactionRef tx;
+ uint256 hashBlock;
+ std::vector<uint256> vMerkleBranch;
+ int nIndex;
+
+ s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
+ }
+};
+
+/**
+ * A transaction with a bunch of additional info that only the owner cares about.
+ * It includes any unrecorded transactions needed to link it back to the block chain.
+ */
+class CWalletTx
+{
+private:
+ const CWallet* const pwallet;
+
+ /** Constant used in hashBlock to indicate tx has been abandoned, only used at
+ * serialization/deserialization to avoid ambiguity with conflicted.
+ */
+ static constexpr const uint256& ABANDON_HASH = uint256::ONE;
+
+public:
+ /**
+ * Key/value map with information about the transaction.
+ *
+ * The following keys can be read and written through the map and are
+ * serialized in the wallet database:
+ *
+ * "comment", "to" - comment strings provided to sendtoaddress,
+ * and sendmany wallet RPCs
+ * "replaces_txid" - txid (as HexStr) of transaction replaced by
+ * bumpfee on transaction created by bumpfee
+ * "replaced_by_txid" - txid (as HexStr) of transaction created by
+ * bumpfee on transaction replaced by bumpfee
+ * "from", "message" - obsolete fields that could be set in UI prior to
+ * 2011 (removed in commit 4d9b223)
+ *
+ * The following keys are serialized in the wallet database, but shouldn't
+ * be read or written through the map (they will be temporarily added and
+ * removed from the map during serialization):
+ *
+ * "fromaccount" - serialized strFromAccount value
+ * "n" - serialized nOrderPos value
+ * "timesmart" - serialized nTimeSmart value
+ * "spent" - serialized vfSpent value that existed prior to
+ * 2014 (removed in commit 93a18a3)
+ */
+ mapValue_t mapValue;
+ std::vector<std::pair<std::string, std::string> > vOrderForm;
+ unsigned int fTimeReceivedIsTxTime;
+ unsigned int nTimeReceived; //!< time received by this node
+ /**
+ * Stable timestamp that never changes, and reflects the order a transaction
+ * was added to the wallet. Timestamp is based on the block time for a
+ * transaction added as part of a block, or else the time when the
+ * transaction was received if it wasn't part of a block, with the timestamp
+ * adjusted in both cases so timestamp order matches the order transactions
+ * were added to the wallet. More details can be found in
+ * CWallet::ComputeTimeSmart().
+ */
+ unsigned int nTimeSmart;
+ /**
+ * From me flag is set to 1 for transactions that were created by the wallet
+ * on this bitcoin node, and set to 0 for transactions that were created
+ * externally and came in through the network or sendrawtransaction RPC.
+ */
+ bool fFromMe;
+ int64_t nOrderPos; //!< position in ordered transaction list
+ std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
+
+ // memory only
+ enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
+ CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
+ mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
+ /**
+ * This flag is true if all m_amounts caches are empty. This is particularly
+ * useful in places where MarkDirty is conditionally called and the
+ * condition can be expensive and thus can be skipped if the flag is true.
+ * See MarkDestinationsDirty.
+ */
+ mutable bool m_is_cache_empty{true};
+ mutable bool fChangeCached;
+ mutable bool fInMempool;
+ mutable CAmount nChangeCached;
+
+ CWalletTx(const CWallet* wallet, CTransactionRef arg)
+ : pwallet(wallet),
+ tx(std::move(arg))
+ {
+ Init();
+ }
+
+ void Init()
+ {
+ mapValue.clear();
+ vOrderForm.clear();
+ fTimeReceivedIsTxTime = false;
+ nTimeReceived = 0;
+ nTimeSmart = 0;
+ fFromMe = false;
+ fChangeCached = false;
+ fInMempool = false;
+ nChangeCached = 0;
+ nOrderPos = -1;
+ m_confirm = Confirmation{};
+ }
+
+ CTransactionRef tx;
+
+ /** New transactions start as UNCONFIRMED. At BlockConnected,
+ * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected,
+ * they roll back to UNCONFIRMED. If we detect a conflicting transaction at
+ * block connection, we update conflicted tx and its dependencies as CONFLICTED.
+ * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED
+ * by using the abandontransaction call. This last status may be override by a CONFLICTED
+ * or CONFIRMED transition.
+ */
+ enum Status {
+ UNCONFIRMED,
+ CONFIRMED,
+ CONFLICTED,
+ ABANDONED
+ };
+
+ /** Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
+ * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
+ * Meaning of these fields changes with CONFLICTED state where they instead point to block hash
+ * and block height of the deepest conflicting tx.
+ */
+ struct Confirmation {
+ Status status;
+ int block_height;
+ uint256 hashBlock;
+ int nIndex;
+ Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
+ };
+
+ Confirmation m_confirm;
+
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ mapValue_t mapValueCopy = mapValue;
+
+ mapValueCopy["fromaccount"] = "";
+ WriteOrderPos(nOrderPos, mapValueCopy);
+ if (nTimeSmart) {
+ mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
+ }
+
+ std::vector<uint8_t> dummy_vector1; //!< Used to be vMerkleBranch
+ std::vector<uint8_t> dummy_vector2; //!< Used to be vtxPrev
+ bool dummy_bool = false; //!< Used to be fSpent
+ uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock;
+ int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex;
+ s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ Init();
+
+ std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
+ std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
+ bool dummy_bool; //! Used to be fSpent
+ int serializedIndex;
+ s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
+
+ /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to
+ * the earliest block in the chain we know this or any in-wallet ancestor conflicts
+ * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned.
+ * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or
+ * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward
+ * compatibility (pre-commit 9ac63d6).
+ */
+ if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
+ setAbandoned();
+ } else if (serializedIndex == -1) {
+ setConflicted();
+ } else if (!m_confirm.hashBlock.IsNull()) {
+ m_confirm.nIndex = serializedIndex;
+ setConfirmed();
+ }
+
+ ReadOrderPos(nOrderPos, mapValue);
+ nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
+
+ mapValue.erase("fromaccount");
+ mapValue.erase("spent");
+ mapValue.erase("n");
+ mapValue.erase("timesmart");
+ }
+
+ void SetTx(CTransactionRef arg)
+ {
+ tx = std::move(arg);
+ }
+
+ //! make sure balances are recalculated
+ void MarkDirty()
+ {
+ m_amounts[DEBIT].Reset();
+ m_amounts[CREDIT].Reset();
+ m_amounts[IMMATURE_CREDIT].Reset();
+ m_amounts[AVAILABLE_CREDIT].Reset();
+ fChangeCached = false;
+ m_is_cache_empty = true;
+ }
+
+ //! filter decides which addresses will count towards the debit
+ CAmount GetDebit(const isminefilter& filter) const;
+ CAmount GetCredit(const isminefilter& filter) const;
+ CAmount GetImmatureCredit(bool fUseCache = true) const;
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
+ // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
+ // having to resolve the issue of member access into incomplete type CWallet.
+ CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
+ CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
+ CAmount GetChange() const;
+
+ /** Get the marginal bytes if spending the specified output from this transaction */
+ int GetSpendSize(unsigned int out, bool use_max_sig = false) const
+ {
+ return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
+ }
+
+ void GetAmounts(std::list<COutputEntry>& listReceived,
+ std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const;
+
+ bool IsFromMe(const isminefilter& filter) const
+ {
+ return (GetDebit(filter) > 0);
+ }
+
+ /** True if only scriptSigs are different */
+ bool IsEquivalentTo(const CWalletTx& tx) const;
+
+ bool InMempool() const;
+ bool IsTrusted() const;
+
+ int64_t GetTxTime() const;
+
+ /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
+ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
+
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
+
+ /**
+ * Return depth of transaction in blockchain:
+ * <0 : conflicts with a transaction this deep in the blockchain
+ * 0 : in memory pool, waiting to be included in a block
+ * >=1 : this many blocks deep in the main chain
+ */
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
+ bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
+
+ /**
+ * @return number of blocks to maturity for this transaction:
+ * 0 : is not a coinbase transaction, or is a mature coinbase transaction
+ * >0 : is a coinbase transaction which matures in this many blocks
+ */
+ int GetBlocksToMaturity() const;
+ bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
+ void setAbandoned()
+ {
+ m_confirm.status = CWalletTx::ABANDONED;
+ m_confirm.hashBlock = uint256();
+ m_confirm.block_height = 0;
+ m_confirm.nIndex = 0;
+ }
+ bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
+ void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
+ bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
+ void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
+ bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
+ void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
+ const uint256& GetHash() const { return tx->GetHash(); }
+ bool IsCoinBase() const { return tx->IsCoinBase(); }
+ bool IsImmatureCoinBase() const;
+
+ // Disable copying of CWalletTx objects to prevent bugs where instances get
+ // copied in and out of the mapWallet map, and fields are updated in the
+ // wrong copy.
+ CWalletTx(CWalletTx const &) = delete;
+ void operator=(CWalletTx const &x) = delete;
+};
+
+#endif // BITCOIN_WALLET_TRANSACTION_H
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b00fa851fd..4b6630de3c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -53,8 +53,6 @@ const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
},
};
-static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10;
-
RecursiveMutex cs_wallets;
static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
static std::list<LoadWalletFn> g_load_wallet_fns GUARDED_BY(cs_wallets);
@@ -213,7 +211,8 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std:
return nullptr;
}
- std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings);
+ chain.initMessage(_("Loading wallet…").translated);
+ std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_LOAD;
@@ -292,7 +291,8 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
}
// Make the wallet
- std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), wallet_creation_flags, error, warnings);
+ chain.initMessage(_("Loading wallet…").translated);
+ std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_CREATE;
@@ -349,11 +349,6 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
* @{
*/
-std::string COutput::ToString() const
-{
- return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
-}
-
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
AssertLockHeld(cs_wallet);
@@ -603,12 +598,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
CKeyingMaterial _vMasterKey;
_vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
- GetStrongRandBytes(&_vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
+ GetStrongRandBytes(_vMasterKey.data(), WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey;
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
- GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
+ GetStrongRandBytes(kMasterKey.vchSalt.data(), WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
@@ -819,12 +814,11 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned
CTxDestination dst;
if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
if (IsMine(dst)) {
- if (used && !GetDestData(dst, "used", nullptr)) {
- if (AddDestData(batch, dst, "used", "p")) { // p for "present", opposite of absent (null)
+ if (used != IsAddressUsed(dst)) {
+ if (used) {
tx_destinations.insert(dst);
}
- } else if (!used && GetDestData(dst, "used", nullptr)) {
- EraseDestData(batch, dst, "used");
+ SetAddressUsed(batch, dst, used);
}
}
}
@@ -840,7 +834,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
if (!ExtractDestination(srctx->tx->vout[n].scriptPubKey, dest)) {
return false;
}
- if (GetDestData(dest, "used", nullptr)) {
+ if (IsAddressUsed(dest)) {
return true;
}
if (IsLegacy()) {
@@ -848,15 +842,15 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
assert(spk_man != nullptr);
for (const auto& keyid : GetAffectedKeys(srctx->tx->vout[n].scriptPubKey, *spk_man)) {
WitnessV0KeyHash wpkh_dest(keyid);
- if (GetDestData(wpkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(wpkh_dest)) {
return true;
}
ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest));
- if (GetDestData(sh_wpkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(sh_wpkh_dest)) {
return true;
}
PKHash pkh_dest(keyid);
- if (GetDestData(pkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(pkh_dest)) {
return true;
}
}
@@ -1281,20 +1275,6 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
chain().waitForNotificationsIfTipChanged(last_block_hash);
}
-
-isminetype CWallet::IsMine(const CTxIn &txin) const
-{
- AssertLockHeld(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
- {
- const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.tx->vout.size())
- return IsMine(prev.tx->vout[txin.prevout.n]);
- }
- return ISMINE_NO;
-}
-
// Note that this function doesn't distinguish between a 0-valued input,
// and a not-"is mine" (according to the filter) input.
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
@@ -1335,49 +1315,6 @@ isminetype CWallet::IsMine(const CScript& script) const
return result;
}
-CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
-{
- if (!MoneyRange(txout.nValue))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- LOCK(cs_wallet);
- return ((IsMine(txout) & filter) ? txout.nValue : 0);
-}
-
-bool CWallet::IsChange(const CTxOut& txout) const
-{
- return IsChange(txout.scriptPubKey);
-}
-
-bool CWallet::IsChange(const CScript& script) const
-{
- // TODO: fix handling of 'change' outputs. The assumption is that any
- // payment to a script that is ours, but is not in the address book
- // is change. That assumption is likely to break when we implement multisignature
- // wallets that return change back into a multi-signature-protected address;
- // a better way of identifying which outputs are 'the send' and which are
- // 'the change' will need to be implemented (maybe extend CWalletTx to remember
- // which output, if any, was change).
- AssertLockHeld(cs_wallet);
- if (IsMine(script))
- {
- CTxDestination address;
- if (!ExtractDestination(script, address))
- return true;
- if (!FindAddressBookEntry(address)) {
- return true;
- }
- }
- return false;
-}
-
-CAmount CWallet::GetChange(const CTxOut& txout) const
-{
- AssertLockHeld(cs_wallet);
- if (!MoneyRange(txout.nValue))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- return (IsChange(txout) ? txout.nValue : 0);
-}
-
bool CWallet::IsMine(const CTransaction& tx) const
{
AssertLockHeld(cs_wallet);
@@ -1404,52 +1341,6 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
return nDebit;
}
-bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
-{
- LOCK(cs_wallet);
-
- for (const CTxIn& txin : tx.vin)
- {
- auto mi = mapWallet.find(txin.prevout.hash);
- if (mi == mapWallet.end())
- return false; // any unknown inputs can't be from us
-
- const CWalletTx& prev = (*mi).second;
-
- if (txin.prevout.n >= prev.tx->vout.size())
- return false; // invalid input!
-
- if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
- return false;
- }
- return true;
-}
-
-CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
-{
- CAmount nCredit = 0;
- for (const CTxOut& txout : tx.vout)
- {
- nCredit += GetCredit(txout, filter);
- if (!MoneyRange(nCredit))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- }
- return nCredit;
-}
-
-CAmount CWallet::GetChange(const CTransaction& tx) const
-{
- LOCK(cs_wallet);
- CAmount nChange = 0;
- for (const CTxOut& txout : tx.vout)
- {
- nChange += GetChange(txout);
- if (!MoneyRange(nChange))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- }
- return nChange;
-}
-
bool CWallet::IsHDEnabled() const
{
// All Active ScriptPubKeyMans must be HD for this to be true
@@ -1529,12 +1420,6 @@ bool CWallet::AddWalletFlags(uint64_t flags)
return LoadWalletFlags(flags);
}
-int64_t CWalletTx::GetTxTime() const
-{
- int64_t n = nTimeSmart;
- return n ? n : nTimeReceived;
-}
-
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const
@@ -1625,101 +1510,6 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
return true;
}
-// 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 std::make_pair(-1, -1);
- }
- assert(input.prevout.n < mi->second.tx->vout.size());
- txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
- }
- return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
-}
-
-// txouts needs to be in the order of tx.vin
-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 std::make_pair(-1, -1);
- }
- 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)
-{
- CMutableTransaction txn;
- txn.vin.push_back(CTxIn(COutPoint()));
- if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
- return -1;
- }
- return GetVirtualTransactionInputSize(txn.vin[0]);
-}
-
-void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
- std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const
-{
- nFee = 0;
- listReceived.clear();
- listSent.clear();
-
- // Compute fee:
- CAmount nDebit = GetDebit(filter);
- if (nDebit > 0) // debit>0 means we signed/sent this transaction
- {
- CAmount nValueOut = tx->GetValueOut();
- nFee = nDebit - nValueOut;
- }
-
- LOCK(pwallet->cs_wallet);
- // Sent/received.
- for (unsigned int i = 0; i < tx->vout.size(); ++i)
- {
- const CTxOut& txout = tx->vout[i];
- isminetype fIsMine = pwallet->IsMine(txout);
- // Only need to handle txouts if AT LEAST one of these is true:
- // 1) they debit from us (sent)
- // 2) the output is to us (received)
- if (nDebit > 0)
- {
- // Don't report 'change' txouts
- if (pwallet->IsChange(txout))
- continue;
- }
- else if (!(fIsMine & filter))
- continue;
-
- // In either case, we need to get the destination address
- CTxDestination address;
-
- if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
- {
- pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
- this->GetHash().ToString());
- address = CNoDestination();
- }
-
- COutputEntry output = {address, txout.nValue, (int)i};
-
- // If we are debited by the transaction, add the output as a "sent" entry
- if (nDebit > 0)
- listSent.push_back(output);
-
- // If we are receiving the output, add it as a "received" entry
- if (fIsMine & filter)
- listReceived.push_back(output);
- }
-
-}
-
/**
* Scan active chain for relevant transactions after importing keys. This should
* be called whenever new keys are added to the wallet, with the oldest key
@@ -1784,7 +1574,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
fAbortRescan = false;
- ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
+ ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
uint256 end_hash = tip_hash;
if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
@@ -1799,7 +1589,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
m_scanning_progress = 0;
}
if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
- ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
+ ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
@@ -1861,7 +1651,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}
}
}
- ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 100); // hide progress dialog in GUI
+ ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 100); // hide progress dialog in GUI
if (block_height && fAbortRescan) {
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height, progress_current);
result.status = ScanResult::USER_ABORT;
@@ -1942,165 +1732,6 @@ std::set<uint256> CWalletTx::GetConflicts() const
return result;
}
-CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
-{
- auto& amount = m_amounts[type];
- if (recalculate || !amount.m_cached[filter]) {
- amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
- m_is_cache_empty = false;
- }
- return amount.m_value[filter];
-}
-
-CAmount CWalletTx::GetDebit(const isminefilter& filter) const
-{
- if (tx->vin.empty())
- return 0;
-
- CAmount debit = 0;
- if (filter & ISMINE_SPENDABLE) {
- debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
- }
- if (filter & ISMINE_WATCH_ONLY) {
- debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
- }
- return debit;
-}
-
-CAmount CWalletTx::GetCredit(const isminefilter& filter) const
-{
- // Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
- return 0;
-
- CAmount credit = 0;
- if (filter & ISMINE_SPENDABLE) {
- // GetBalance can assume transactions in mapWallet won't change
- credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
- }
- if (filter & ISMINE_WATCH_ONLY) {
- credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
- }
- return credit;
-}
-
-CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
-{
- if (IsImmatureCoinBase() && IsInMainChain()) {
- return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
- }
-
- return 0;
-}
-
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
-{
- if (pwallet == nullptr)
- return 0;
-
- // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
- bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
-
- // Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
- return 0;
-
- if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
- return m_amounts[AVAILABLE_CREDIT].m_value[filter];
- }
-
- bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
- CAmount nCredit = 0;
- uint256 hashTx = GetHash();
- for (unsigned int i = 0; i < tx->vout.size(); i++)
- {
- if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
- const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, filter);
- if (!MoneyRange(nCredit))
- throw std::runtime_error(std::string(__func__) + " : value out of range");
- }
- }
-
- if (allow_cache) {
- m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
- m_is_cache_empty = false;
- }
-
- return nCredit;
-}
-
-CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
-{
- if (IsImmatureCoinBase() && IsInMainChain()) {
- return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
- }
-
- return 0;
-}
-
-CAmount CWalletTx::GetChange() const
-{
- if (fChangeCached)
- return nChangeCached;
- nChangeCached = pwallet->GetChange(*tx);
- fChangeCached = true;
- return nChangeCached;
-}
-
-bool CWalletTx::InMempool() const
-{
- return fInMempool;
-}
-
-bool CWalletTx::IsTrusted() const
-{
- std::set<uint256> trusted_parents;
- LOCK(pwallet->cs_wallet);
- return pwallet->IsTrusted(*this, trusted_parents);
-}
-
-bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
-{
- AssertLockHeld(cs_wallet);
- // Quick answer in most cases
- if (!chain().checkFinalTx(*wtx.tx)) return false;
- int nDepth = wtx.GetDepthInMainChain();
- if (nDepth >= 1) return true;
- if (nDepth < 0) return false;
- // using wtx's cached debit
- if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
-
- // Don't trust unconfirmed transactions from us unless they are in the mempool.
- if (!wtx.InMempool()) return false;
-
- // Trusted if all inputs are from us and are in the mempool:
- for (const CTxIn& txin : wtx.tx->vin)
- {
- // Transactions not sent by us: not trusted
- const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
- if (parent == nullptr) return false;
- const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
- // Check that this specific input being spent is trusted
- if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
- // If we've already trusted this parent, continue
- if (trusted_parents.count(parent->GetHash())) continue;
- // Recurse to check that the parent is also trusted
- if (!IsTrusted(*parent, trusted_parents)) return false;
- trusted_parents.insert(parent->GetHash());
- }
- return true;
-}
-
-bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
-{
- CMutableTransaction tx1 {*this->tx};
- CMutableTransaction tx2 {*_tx.tx};
- for (auto& txin : tx1.vin) txin.scriptSig = CScript();
- for (auto& txin : tx2.vin) txin.scriptSig = CScript();
- return CTransaction(tx1) == CTransaction(tx2);
-}
-
// Rebroadcast transactions from the wallet. We do this on a random timer
// to slightly obfuscate which transactions come from our wallet.
//
@@ -2161,365 +1792,6 @@ void MaybeResendWalletTxs()
* @{
*/
-
-CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const
-{
- Balance ret;
- isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
- {
- LOCK(cs_wallet);
- std::set<uint256> trusted_parents;
- for (const auto& entry : mapWallet)
- {
- const CWalletTx& wtx = entry.second;
- const bool is_trusted{IsTrusted(wtx, trusted_parents)};
- const int tx_depth{wtx.GetDepthInMainChain()};
- const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
- const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
- if (is_trusted && tx_depth >= min_depth) {
- ret.m_mine_trusted += tx_credit_mine;
- ret.m_watchonly_trusted += tx_credit_watchonly;
- }
- if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
- ret.m_mine_untrusted_pending += tx_credit_mine;
- ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
- }
- ret.m_mine_immature += wtx.GetImmatureCredit();
- ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit();
- }
- }
- return ret;
-}
-
-CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
-{
- LOCK(cs_wallet);
-
- CAmount balance = 0;
- std::vector<COutput> vCoins;
- AvailableCoins(vCoins, true, coinControl);
- for (const COutput& out : vCoins) {
- if (out.fSpendable) {
- balance += out.tx->tx->vout[out.i].nValue;
- }
- }
- return balance;
-}
-
-void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const
-{
- AssertLockHeld(cs_wallet);
-
- vCoins.clear();
- CAmount nTotal = 0;
- // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
- // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
- bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
- const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
- const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
-
- std::set<uint256> trusted_parents;
- for (const auto& entry : mapWallet)
- {
- const uint256& wtxid = entry.first;
- const CWalletTx& wtx = entry.second;
-
- if (!chain().checkFinalTx(*wtx.tx)) {
- continue;
- }
-
- if (wtx.IsImmatureCoinBase())
- continue;
-
- int nDepth = wtx.GetDepthInMainChain();
- if (nDepth < 0)
- continue;
-
- // We should not consider coins which aren't at least in our mempool
- // It's possible for these to be conflicted via ancestors which we may never be able to detect
- if (nDepth == 0 && !wtx.InMempool())
- continue;
-
- bool safeTx = IsTrusted(wtx, trusted_parents);
-
- // We should not consider coins from transactions that are replacing
- // other transactions.
- //
- // Example: There is a transaction A which is replaced by bumpfee
- // transaction B. In this case, we want to prevent creation of
- // a transaction B' which spends an output of B.
- //
- // Reason: If transaction A were initially confirmed, transactions B
- // and B' would no longer be valid, so the user would have to create
- // a new transaction C to replace B'. However, in the case of a
- // one-block reorg, transactions B' and C might BOTH be accepted,
- // when the user only wanted one of them. Specifically, there could
- // be a 1-block reorg away from the chain where transactions A and C
- // were accepted to another chain where B, B', and C were all
- // accepted.
- if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
- safeTx = false;
- }
-
- // Similarly, we should not consider coins from transactions that
- // have been replaced. In the example above, we would want to prevent
- // creation of a transaction A' spending an output of A, because if
- // transaction B were initially confirmed, conflicting with A and
- // A', we wouldn't want to the user to create a transaction D
- // intending to replace A', but potentially resulting in a scenario
- // where A, A', and D could all be accepted (instead of just B and
- // D, or just A and A' like the user would want).
- if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
- safeTx = false;
- }
-
- if (fOnlySafe && !safeTx) {
- continue;
- }
-
- if (nDepth < min_depth || nDepth > max_depth) {
- continue;
- }
-
- for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
- // Only consider selected coins if add_inputs is false
- if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) {
- continue;
- }
-
- if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
- continue;
-
- if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
- continue;
-
- if (IsLockedCoin(entry.first, i))
- continue;
-
- if (IsSpent(wtxid, i))
- continue;
-
- isminetype mine = IsMine(wtx.tx->vout[i]);
-
- if (mine == ISMINE_NO) {
- continue;
- }
-
- if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
- continue;
- }
-
- std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
-
- bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
- bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
-
- vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
-
- // Checks the sum amount of all UTXO's.
- if (nMinimumSumAmount != MAX_MONEY) {
- nTotal += wtx.tx->vout[i].nValue;
-
- if (nTotal >= nMinimumSumAmount) {
- return;
- }
- }
-
- // Checks the maximum number of UTXO's.
- if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
- return;
- }
- }
- }
-}
-
-std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
-{
- AssertLockHeld(cs_wallet);
-
- std::map<CTxDestination, std::vector<COutput>> result;
- std::vector<COutput> availableCoins;
-
- AvailableCoins(availableCoins);
-
- for (const COutput& coin : availableCoins) {
- CTxDestination address;
- if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
- ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
- result[address].emplace_back(std::move(coin));
- }
- }
-
- std::vector<COutPoint> lockedCoins;
- ListLockedCoins(lockedCoins);
- // Include watch-only for LegacyScriptPubKeyMan wallets without private keys
- const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
- const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
- for (const COutPoint& output : lockedCoins) {
- auto it = mapWallet.find(output.hash);
- if (it != mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain();
- if (depth >= 0 && output.n < it->second.tx->vout.size() &&
- IsMine(it->second.tx->vout[output.n]) == is_mine_filter
- ) {
- CTxDestination address;
- if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
- result[address].emplace_back(
- &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
- }
- }
- }
- }
-
- return result;
-}
-
-const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
-{
- AssertLockHeld(cs_wallet);
- const CTransaction* ptx = &tx;
- int n = output;
- while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
- const COutPoint& prevout = ptx->vin[0].prevout;
- auto it = mapWallet.find(prevout.hash);
- if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
- !IsMine(it->second.tx->vout[prevout.n])) {
- break;
- }
- ptx = it->second.tx.get();
- n = prevout.n;
- }
- return ptx->vout[n];
-}
-
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
- std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const
-{
- setCoinsRet.clear();
- nValueRet = 0;
-
- if (coin_selection_params.use_bnb) {
- // 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.m_effective_feerate;
- }
-
- 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 = 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.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 {
- std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, CFeeRate(0), CFeeRate(0), eligibility_filter, false /* positive_only */);
-
- bnb_used = false;
- return KnapsackSolver(nTargetValue, groups, setCoinsRet, nValueRet);
- }
-}
-
-bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const
-{
- std::vector<COutput> vCoins(vAvailableCoins);
- CAmount value_to_select = nTargetValue;
-
- // Default to bnb was not used. If we use it, we set it later
- bnb_used = false;
-
- // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
- if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
- {
- for (const COutput& out : vCoins)
- {
- if (!out.fSpendable)
- continue;
- nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(out.GetInputCoin());
- }
- return (nValueRet >= nTargetValue);
- }
-
- // calculate value from preset inputs and store them
- std::set<CInputCoin> setPresetCoins;
- CAmount nValueFromPresetInputs = 0;
-
- std::vector<COutPoint> vPresetInputs;
- coin_control.ListSelected(vPresetInputs);
- for (const COutPoint& outpoint : vPresetInputs)
- {
- std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
- if (it != mapWallet.end())
- {
- const CWalletTx& wtx = it->second;
- // Clearly invalid input, fail
- if (wtx.tx->vout.size() <= outpoint.n) {
- return false;
- }
- // Just to calculate the marginal byte size
- CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false));
- nValueFromPresetInputs += coin.txout.nValue;
- 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.m_effective_feerate.GetFee(coin.m_input_bytes);
- if (coin_selection_params.use_bnb) {
- value_to_select -= coin.effective_value;
- } else {
- value_to_select -= coin.txout.nValue;
- }
- setPresetCoins.insert(coin);
- } else {
- return false; // TODO: Allow non-wallet inputs
- }
- }
-
- // remove preset inputs from vCoins
- for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
- {
- if (setPresetCoins.count(it->GetInputCoin()))
- it = vCoins.erase(it);
- else
- ++it;
- }
-
- unsigned int limit_ancestor_count = 0;
- unsigned int limit_descendant_count = 0;
- chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
- size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
- size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
- bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
-
- // form groups from remaining coins; note that preset coins will not
- // automatically have their associated (same address) coins included
- if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) {
- // Cases where we have 11+ outputs all pointing to the same destination may result in
- // privacy leaks as they will potentially be deterministically sorted. We solve that by
- // explicitly shuffling the outputs before processing
- Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
- }
- bool res = value_to_select <= 0 ||
- SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
-
- // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
- util::insert(setCoinsRet, setPresetCoins);
-
- // add preset inputs to the total value selected
- nValueRet += nValueFromPresetInputs;
-
- return res;
-}
-
bool CWallet::SignTransaction(CMutableTransaction& tx) const
{
AssertLockHeld(cs_wallet);
@@ -2615,118 +1887,6 @@ SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkh
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
-{
- std::vector<CRecipient> vecSend;
-
- // Turn the txout set into a CRecipient vector.
- for (size_t idx = 0; idx < tx.vout.size(); idx++) {
- const CTxOut& txOut = tx.vout[idx];
- CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
- vecSend.push_back(recipient);
- }
-
- coinControl.fAllowOtherInputs = true;
-
- for (const CTxIn& txin : tx.vin) {
- coinControl.Select(txin.prevout);
- }
-
- // Acquire the locks to prevent races to the new locked unspents between the
- // CreateTransaction call and LockCoin calls (when lockUnspents is true).
- LOCK(cs_wallet);
-
- CTransactionRef tx_new;
- FeeCalculation fee_calc_out;
- if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
- return false;
- }
-
- if (nChangePosInOut != -1) {
- tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
- }
-
- // Copy output sizes from new transaction; they may have had the fee
- // subtracted from them.
- for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
- tx.vout[idx].nValue = tx_new->vout[idx].nValue;
- }
-
- // Add new txins while keeping original txin scriptSig/order.
- for (const CTxIn& txin : tx_new->vin) {
- if (!coinControl.IsSelected(txin.prevout)) {
- tx.vin.push_back(txin);
-
- }
- if (lockUnspents) {
- LockCoin(txin.prevout);
- }
-
- }
-
- return true;
-}
-
-static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)
-{
- if (chain.isInitialBlockDownload()) {
- return false;
- }
- constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds
- int64_t block_time;
- CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time)));
- if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
- return false;
- }
- return true;
-}
-
-/**
- * Return a height-based locktime for new transactions (uses the height of the
- * current chain tip unless we are not synced with the current chain
- */
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height)
-{
- uint32_t locktime;
- // Discourage fee sniping.
- //
- // For a large miner the value of the transactions in the best block and
- // the mempool can exceed the cost of deliberately attempting to mine two
- // blocks to orphan the current best block. By setting nLockTime such that
- // only the next block can include the transaction, we discourage this
- // practice as the height restricted and limited blocksize gives miners
- // considering fee sniping fewer options for pulling off this attack.
- //
- // A simple way to think about this is from the wallet's point of view we
- // always want the blockchain to move forward. By setting nLockTime this
- // way we're basically making the statement that we only want this
- // transaction to appear in the next block; we don't want to potentially
- // encourage reorgs by allowing transactions to appear at lower heights
- // than the next block in forks of the best chain.
- //
- // Of course, the subsidy is high enough, and transaction volume low
- // enough, that fee sniping isn't a problem yet, but by implementing a fix
- // now we ensure code won't be written that makes assumptions about
- // nLockTime that preclude a fix later.
- if (IsCurrentForAntiFeeSniping(chain, block_hash)) {
- locktime = block_height;
-
- // Secondly occasionally randomly pick a nLockTime even further back, so
- // that transactions that are delayed after signing for whatever reason,
- // e.g. high-latency mix networks and some CoinJoin implementations, have
- // better privacy.
- if (GetRandInt(10) == 0)
- locktime = std::max(0, (int)locktime - GetRandInt(100));
- } else {
- // If our chain is lagging behind, we can't discourage fee sniping nor help
- // the privacy of high-latency transactions. To avoid leaking a potentially
- // unique "nLockTime fingerprint", set nLockTime to a constant.
- locktime = 0;
- }
- assert(locktime < LOCKTIME_THRESHOLD);
- return locktime;
-}
-
OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const
{
// If -changetype is specified, always use that change type.
@@ -2755,413 +1915,6 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang
return m_default_address_type;
}
-bool CWallet::CreateTransactionInternal(
- const std::vector<CRecipient>& vecSend,
- CTransactionRef& tx,
- CAmount& nFeeRet,
- int& nChangePosInOut,
- bilingual_str& error,
- const CCoinControl& coin_control,
- FeeCalculation& fee_calc_out,
- bool sign)
-{
- CAmount nValue = 0;
- const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
- ReserveDestination reservedest(this, change_type);
- int nChangePosRequest = nChangePosInOut;
- unsigned int nSubtractFeeFromAmount = 0;
- for (const auto& recipient : vecSend)
- {
- if (nValue < 0 || recipient.nAmount < 0)
- {
- error = _("Transaction amounts must not be negative");
- return false;
- }
- nValue += recipient.nAmount;
-
- if (recipient.fSubtractFeeFromAmount)
- nSubtractFeeFromAmount++;
- }
- if (vecSend.empty())
- {
- error = _("Transaction must have at least one recipient");
- return false;
- }
-
- CMutableTransaction txNew;
- FeeCalculation feeCalc;
- CAmount nFeeNeeded;
- std::pair<int64_t, int64_t> tx_sizes;
- int nBytes;
- {
- std::set<CInputCoin> setCoins;
- LOCK(cs_wallet);
- txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
- {
- std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
- CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
- coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
-
- // Create change script that will be used if we need change
- // TODO: pass in scriptChange instead of reservedest so
- // change transaction isn't always pay-to-bitcoin-address
- CScript scriptChange;
-
- // coin control: send change to custom address
- if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
- scriptChange = GetScriptForDestination(coin_control.destChange);
- } else { // no coin control: send change to newly generated address
- // Note: We use a new key here to keep it from being obvious which side is the change.
- // The drawback is that by not reusing a previous key, the change may be lost if a
- // backup is restored, if the backup doesn't have the new private key for the change.
- // If we reused the old key, it would be possible to add code to look for and
- // rediscover unknown transactions that were written with keys of ours to recover
- // post-backup change.
-
- // Reserve a new key pair from key pool. If it fails, provide a dummy
- // destination in case we don't need change.
- CTxDestination dest;
- if (!reservedest.GetReservedDestination(dest, true)) {
- error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
- }
- scriptChange = GetScriptForDestination(dest);
- // A valid destination implies a change script (and
- // vice-versa). An empty change script will abort later, if the
- // change keypool ran out, but change is required.
- CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
- }
- CTxOut change_prototype_txout(0, scriptChange);
- coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
-
- // Set discard feerate
- coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
-
- // Get the fee rate to use effective values in coin selection
- 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 && 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;
-
- // BnB selector is the only selector used when this is true.
- // That should only happen on the first pass through the loop.
- coin_selection_params.use_bnb = true;
- coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
- // Start with no fee and loop until there is enough fee
- while (true)
- {
- nChangePosInOut = nChangePosRequest;
- txNew.vin.clear();
- txNew.vout.clear();
- bool fFirst = true;
-
- CAmount nValueToSelect = nValue;
- if (nSubtractFeeFromAmount == 0)
- nValueToSelect += nFeeRet;
-
- // vouts to the payees
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
- }
- for (const auto& recipient : vecSend)
- {
- CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
-
- if (recipient.fSubtractFeeFromAmount)
- {
- assert(nSubtractFeeFromAmount != 0);
- txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
-
- if (fFirst) // first receiver pays the remainder not divisible by output count
- {
- fFirst = false;
- txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
- }
- }
- // Include the fee cost for outputs. Note this is only used for BnB right now
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
- }
-
- if (IsDust(txout, chain().relayDustFee()))
- {
- if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
- {
- if (txout.nValue < 0)
- error = _("The transaction amount is too small to pay the fee");
- else
- error = _("The transaction amount is too small to send after the fee has been deducted");
- }
- else
- error = _("Transaction amount too small");
- return false;
- }
- txNew.vout.push_back(txout);
- }
-
- // Choose coins to use
- bool bnb_used = false;
- if (pick_new_inputs) {
- nValueIn = 0;
- setCoins.clear();
- int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
- // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
- // as lower-bound to allow BnB to do it's thing
- if (change_spend_size == -1) {
- coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
- } else {
- coin_selection_params.change_spend_size = (size_t)change_spend_size;
- }
- 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.
- if (bnb_used) {
- coin_selection_params.use_bnb = false;
- continue;
- }
- else {
- error = _("Insufficient funds");
- return false;
- }
- }
- } else {
- bnb_used = false;
- }
-
- const CAmount nChange = nValueIn - nValueToSelect;
- if (nChange > 0)
- {
- // Fill a vout to ourself
- CTxOut newTxOut(nChange, scriptChange);
-
- // 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, coin_selection_params.m_discard_feerate) || bnb_used)
- {
- nChangePosInOut = -1;
- nFeeRet += nChange;
- }
- else
- {
- if (nChangePosInOut == -1)
- {
- // Insert change txn at random position:
- nChangePosInOut = GetRandInt(txNew.vout.size()+1);
- }
- else if ((unsigned int)nChangePosInOut > txNew.vout.size())
- {
- error = _("Change index out of range");
- return false;
- }
-
- std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
- txNew.vout.insert(position, newTxOut);
- }
- } else {
- nChangePosInOut = -1;
- }
-
- // Dummy fill vin for maximum size estimation
- //
- for (const auto& coin : setCoins) {
- txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
- }
-
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
- nBytes = tx_sizes.first;
- if (nBytes < 0) {
- error = _("Signing transaction failed");
- 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
- // selected to meet nFeeNeeded result in a transaction that
- // requires less fee than the prior iteration.
-
- // If we have no change and a big enough excess fee, then
- // try to construct transaction again only without picking
- // new inputs. We now know we only need the smaller fee
- // (because of reduced tx size) and so we should add a
- // 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 = 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;
- continue;
- }
- }
-
- // If we have change output already, just increase it
- if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
- CAmount extraFeePaid = nFeeRet - nFeeNeeded;
- std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
- change_position->nValue += extraFeePaid;
- nFeeRet -= extraFeePaid;
- }
- break; // Done, enough fee included.
- }
- else if (!pick_new_inputs) {
- // This shouldn't happen, we should have had enough excess
- // fee to pay for the new output and still meet nFeeNeeded
- // Or we should have just subtracted fee from recipients and
- // nFeeNeeded should not have changed
- error = _("Transaction fee and change calculation failed");
- return false;
- }
-
- // Try to reduce change to include necessary fee
- if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
- CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
- std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
- // Only reduce change if remaining amount is still a large enough output.
- if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
- change_position->nValue -= additionalFeeNeeded;
- nFeeRet += additionalFeeNeeded;
- break; // Done, able to increase fee from change
- }
- }
-
- // If subtracting fee from recipients, we now know what fee we
- // need to subtract, we have no reason to reselect inputs
- if (nSubtractFeeFromAmount > 0) {
- pick_new_inputs = false;
- }
-
- // Include more fee and try again.
- nFeeRet = nFeeNeeded;
- coin_selection_params.use_bnb = false;
- continue;
- }
-
- // Give up if change keypool ran out and change is required
- if (scriptChange.empty() && nChangePosInOut != -1) {
- return false;
- }
- }
-
- // Shuffle selected coins and fill in final vin
- txNew.vin.clear();
- std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
- Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
-
- // Note how the sequence number is set to non-maxint so that
- // the nLockTime set above actually works.
- //
- // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
- // we use the highest possible value in that range (maxint-2)
- // to avoid conflicting with other possible uses of nSequence,
- // and in the spirit of "smallest possible change from prior
- // behavior."
- const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
- for (const auto& coin : selected_coins) {
- txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
- }
-
- if (sign && !SignTransaction(txNew)) {
- error = _("Signing transaction failed");
- return false;
- }
-
- // Return the constructed transaction data.
- tx = MakeTransactionRef(std::move(txNew));
-
- // Limit size
- if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
- (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT))
- {
- error = _("Transaction too large");
- return false;
- }
- }
-
- if (nFeeRet > m_default_max_tx_fee) {
- error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
- return false;
- }
-
- if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
- // Lastly, ensure this tx will pass the mempool's chain limits
- if (!chain().checkChainLimits(tx)) {
- error = _("Transaction has too long of a mempool chain");
- return false;
- }
- }
-
- // Before we return success, we assume any change key will be used to prevent
- // accidental re-use.
- reservedest.KeepDestination();
- fee_calc_out = feeCalc;
-
- WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
- nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
- feeCalc.est.pass.start, feeCalc.est.pass.end,
- (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0,
- feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,
- feeCalc.est.fail.start, feeCalc.est.fail.end,
- (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
- feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
- return true;
-}
-
-bool CWallet::CreateTransaction(
- const std::vector<CRecipient>& vecSend,
- CTransactionRef& tx,
- CAmount& nFeeRet,
- int& nChangePosInOut,
- bilingual_str& error,
- const CCoinControl& coin_control,
- FeeCalculation& fee_calc_out,
- bool sign)
-{
- int nChangePosIn = nChangePosInOut;
- Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
- bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
- // try with avoidpartialspends unless it's enabled already
- if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
- CCoinControl tmp_cc = coin_control;
- tmp_cc.m_avoid_partial_spends = true;
- CAmount nFeeRet2;
- CTransactionRef tx2;
- int nChangePosInOut2 = nChangePosIn;
- bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
- if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
- // if fee of this alternative one is within the range of the max fee, we use this one
- const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee;
- WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
- if (use_aps) {
- tx = tx2;
- nFeeRet = nFeeRet2;
- nChangePosInOut = nChangePosInOut2;
- }
- }
- }
- return res;
-}
-
void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm)
{
LOCK(cs_wallet);
@@ -3202,11 +1955,10 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
}
-DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
+DBErrors CWallet::LoadWallet()
{
LOCK(cs_wallet);
- fFirstRunRet = false;
DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this);
if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
@@ -3218,9 +1970,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
}
}
- // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
- fFirstRunRet = m_spk_managers.empty() && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
- if (fFirstRunRet) {
+ if (m_spk_managers.empty()) {
assert(m_external_spk_managers.empty());
assert(m_internal_spk_managers.empty());
}
@@ -3408,137 +2158,6 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations
}
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
-{
- std::map<CTxDestination, CAmount> balances;
-
- {
- LOCK(cs_wallet);
- std::set<uint256> trusted_parents;
- for (const auto& walletEntry : mapWallet)
- {
- const CWalletTx& wtx = walletEntry.second;
-
- if (!IsTrusted(wtx, trusted_parents))
- continue;
-
- if (wtx.IsImmatureCoinBase())
- continue;
-
- int nDepth = wtx.GetDepthInMainChain();
- if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
- continue;
-
- for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
- {
- CTxDestination addr;
- if (!IsMine(wtx.tx->vout[i]))
- continue;
- if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
- continue;
-
- CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
- balances[addr] += n;
- }
- }
- }
-
- return balances;
-}
-
-std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
-{
- AssertLockHeld(cs_wallet);
- std::set< std::set<CTxDestination> > groupings;
- std::set<CTxDestination> grouping;
-
- for (const auto& walletEntry : mapWallet)
- {
- const CWalletTx& wtx = walletEntry.second;
-
- if (wtx.tx->vin.size() > 0)
- {
- bool any_mine = false;
- // group all input addresses with each other
- for (const CTxIn& txin : wtx.tx->vin)
- {
- CTxDestination address;
- if(!IsMine(txin)) /* If this input isn't mine, ignore it */
- continue;
- if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
- continue;
- grouping.insert(address);
- any_mine = true;
- }
-
- // group change with input addresses
- if (any_mine)
- {
- for (const CTxOut& txout : wtx.tx->vout)
- if (IsChange(txout))
- {
- CTxDestination txoutAddr;
- if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
- continue;
- grouping.insert(txoutAddr);
- }
- }
- if (grouping.size() > 0)
- {
- groupings.insert(grouping);
- grouping.clear();
- }
- }
-
- // group lone addrs by themselves
- for (const auto& txout : wtx.tx->vout)
- if (IsMine(txout))
- {
- CTxDestination address;
- if(!ExtractDestination(txout.scriptPubKey, address))
- continue;
- grouping.insert(address);
- groupings.insert(grouping);
- grouping.clear();
- }
- }
-
- std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
- std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it
- for (std::set<CTxDestination> _grouping : groupings)
- {
- // make a set of all the groups hit by this new group
- std::set< std::set<CTxDestination>* > hits;
- std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;
- for (const CTxDestination& address : _grouping)
- if ((it = setmap.find(address)) != setmap.end())
- hits.insert((*it).second);
-
- // merge all hit groups into a new single group and delete old groups
- std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);
- for (std::set<CTxDestination>* hit : hits)
- {
- merged->insert(hit->begin(), hit->end());
- uniqueGroupings.erase(hit);
- delete hit;
- }
- uniqueGroupings.insert(merged);
-
- // update setmap
- for (const CTxDestination& element : *merged)
- setmap[element] = merged;
- }
-
- std::set< std::set<CTxDestination> > ret;
- for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings)
- {
- ret.insert(*uniqueGrouping);
- delete uniqueGrouping;
- }
-
- return ret;
-}
-
std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) const
{
LOCK(cs_wallet);
@@ -3594,19 +2213,6 @@ 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
@@ -3619,7 +2225,7 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
if (signer_spk_man == nullptr) {
return false;
}
- ExternalSigner signer = GetExternalSigner(); // TODO: move signer in spk_man
+ ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
#else
return false;
@@ -3780,45 +2386,45 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
return nTimeSmart;
}
-bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value)
+bool CWallet::SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used)
{
+ const std::string key{"used"};
if (std::get_if<CNoDestination>(&dest))
return false;
+ if (!used) {
+ if (auto* data = util::FindKey(m_address_book, dest)) data->destdata.erase(key);
+ return batch.EraseDestData(EncodeDestination(dest), key);
+ }
+
+ const std::string value{"1"};
m_address_book[dest].destdata.insert(std::make_pair(key, value));
return batch.WriteDestData(EncodeDestination(dest), key, value);
}
-bool CWallet::EraseDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key)
-{
- if (!m_address_book[dest].destdata.erase(key))
- return false;
- return batch.EraseDestData(EncodeDestination(dest), key);
-}
-
void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
m_address_book[dest].destdata.insert(std::make_pair(key, value));
}
-bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
+bool CWallet::IsAddressUsed(const CTxDestination& dest) const
{
+ const std::string key{"used"};
std::map<CTxDestination, CAddressBookData>::const_iterator i = m_address_book.find(dest);
if(i != m_address_book.end())
{
CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
if(j != i->second.destdata.end())
{
- if(value)
- *value = j->second;
return true;
}
}
return false;
}
-std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
+std::vector<std::string> CWallet::GetAddressReceiveRequests() const
{
+ const std::string prefix{"rr"};
std::vector<std::string> values;
for (const auto& address : m_address_book) {
for (const auto& data : address.second.destdata) {
@@ -3830,6 +2436,20 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
+bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value)
+{
+ const std::string key{"rr" + id}; // "rr" prefix = "receive request" in destdata
+ CAddressBookData& data = m_address_book.at(dest);
+ if (value.empty()) {
+ if (!batch.EraseDestData(EncodeDestination(dest), key)) return false;
+ data.destdata.erase(key);
+ } else {
+ if (!batch.WriteDestData(EncodeDestination(dest), key, value)) return false;
+ data.destdata[key] = value;
+ }
+ return true;
+}
+
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
{
// Do some checking on wallet path. It should be either a:
@@ -3854,18 +2474,15 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
return MakeDatabase(wallet_path, options, status, error_string);
}
-std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
const std::string& walletFile = database->Filename();
- chain.initMessage(_("Loading wallet...").translated);
-
int64_t nStart = GetTimeMillis();
- bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, name, std::move(database)), ReleaseWallet);
- DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
+ DBErrors nLoadWalletRet = walletInstance->LoadWallet();
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
@@ -3892,6 +2509,10 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
}
}
+ // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
+ const bool fFirstRun = walletInstance->m_spk_managers.empty() &&
+ !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
+ !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
if (fFirstRun)
{
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
@@ -3920,7 +2541,9 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
}
}
- walletInstance->chainStateFlushed(chain.getTipLocator());
+ if (chain) {
+ walletInstance->chainStateFlushed(chain->getTipLocator());
+ }
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile);
@@ -4017,9 +2640,9 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
_("This is the transaction fee you will pay if you send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
- if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) {
+ if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) {
error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString());
+ gArgs.GetArg("-paytxfee", ""), chain->relayMinFee().ToString());
return nullptr;
}
}
@@ -4033,15 +2656,15 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
if (nMaxFee > HIGH_MAX_TX_FEE) {
warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
}
- if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) {
+ if (chain && CFeeRate(nMaxFee, 1000) < chain->relayMinFee()) {
error = strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString());
+ gArgs.GetArg("-maxtxfee", ""), chain->relayMinFee().ToString());
return nullptr;
}
walletInstance->m_default_max_tx_fee = nMaxFee;
}
- if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) {
+ if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) {
warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") +
_("The wallet will avoid paying less than the minimum relay fee."));
}
@@ -4057,6 +2680,35 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
LOCK(walletInstance->cs_wallet);
+ if (chain && !AttachChain(walletInstance, *chain, error, warnings)) {
+ return nullptr;
+ }
+
+ {
+ LOCK(cs_wallets);
+ for (auto& load_wallet : g_load_wallet_fns) {
+ load_wallet(interfaces::MakeWallet(walletInstance));
+ }
+ }
+
+ walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
+
+ {
+ walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
+ walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
+ walletInstance->WalletLogPrintf("m_address_book.size() = %u\n", walletInstance->m_address_book.size());
+ }
+
+ return walletInstance;
+}
+
+bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings)
+{
+ LOCK(walletInstance->cs_wallet);
+ // allow setting the chain if it hasn't been set already but prevent changing it
+ assert(!walletInstance->m_chain || walletInstance->m_chain == &chain);
+ walletInstance->m_chain = &chain;
+
// Register wallet with validationinterface. It's done before rescan to avoid
// missing block connections between end of rescan and validation subscribing.
// Because of wallet lock being hold, block connection notifications are going to
@@ -4090,25 +2742,25 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
if (tip_height && *tip_height != rescan_height)
{
- // We can't rescan beyond non-pruned blocks, stop and throw an error.
- // This might happen if a user uses an old wallet within a pruned node
- // or if they ran -disablewallet for a longer time, then decided to re-enable
if (chain.havePruned()) {
- // Exit early and print an error.
- // If a block is pruned after this check, we will load the wallet,
- // but fail the rescan with a generic error.
int block_height = *tip_height;
while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
}
if (rescan_height != block_height) {
+ // We can't rescan beyond non-pruned blocks, stop and throw an error.
+ // This might happen if a user uses an old wallet within a pruned node
+ // or if they ran -disablewallet for a longer time, then decided to re-enable
+ // Exit early and print an error.
+ // If a block is pruned after this check, we will load the wallet,
+ // but fail the rescan with a generic error.
error = _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)");
- return nullptr;
+ return false;
}
}
- chain.initMessage(_("Rescanning...").translated);
+ chain.initMessage(_("Rescanning…").translated);
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
// No need to read and scan block if block was created before
@@ -4126,29 +2778,14 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
WalletRescanReserver reserver(*walletInstance);
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, {} /* max height */, reserver, true /* update */).status)) {
error = _("Failed to rescan the wallet during initialization");
- return nullptr;
+ return false;
}
}
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->GetDatabase().IncrementUpdateCounter();
}
- {
- LOCK(cs_wallets);
- for (auto& load_wallet : g_load_wallet_fns) {
- load_wallet(interfaces::MakeWallet(walletInstance));
- }
- }
-
- walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
-
- {
- walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
- walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
- walletInstance->WalletLogPrintf("m_address_book.size() = %u\n", walletInstance->m_address_book.size());
- }
-
- return walletInstance;
+ return true;
}
const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const
@@ -4250,92 +2887,6 @@ bool CWalletTx::IsImmatureCoinBase() const
return GetBlocksToMaturity() > 0;
}
-std::vector<OutputGroup> CWallet::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
-{
- std::vector<OutputGroup> groups_out;
-
- if (separate_coins) {
- // Single coin means no grouping. Each COutput gets its own OutputGroup.
- for (const COutput& output : outputs) {
- // Skip outputs we cannot spend
- if (!output.fSpendable) continue;
-
- size_t ancestors, descendants;
- chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- CInputCoin input_coin = output.GetInputCoin();
-
- // Make an OutputGroup containing just this output
- OutputGroup group{effective_feerate, long_term_feerate};
- group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
-
- // Check the OutputGroup's eligibility. Only add the eligible ones.
- if (positive_only && group.effective_value <= 0) continue;
- if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group);
- }
- return groups_out;
- }
-
- // We want to combine COutputs that have the same scriptPubKey into single OutputGroups
- // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup.
- // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups.
- // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added
- // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has
- // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector.
- std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map;
- for (const auto& output : outputs) {
- // Skip outputs we cannot spend
- if (!output.fSpendable) continue;
-
- size_t ancestors, descendants;
- chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- CInputCoin input_coin = output.GetInputCoin();
- CScript spk = input_coin.txout.scriptPubKey;
-
- std::vector<OutputGroup>& groups = spk_to_groups_map[spk];
-
- if (groups.size() == 0) {
- // No OutputGroups for this scriptPubKey yet, add one
- groups.emplace_back(effective_feerate, long_term_feerate);
- }
-
- // Get the last OutputGroup in the vector so that we can add the CInputCoin to it
- // A pointer is used here so that group can be reassigned later if it is full.
- OutputGroup* group = &groups.back();
-
- // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends
- // to avoid surprising users with very high fees.
- if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) {
- // The last output group is full, add a new group to the vector and use that group for the insertion
- groups.emplace_back(effective_feerate, long_term_feerate);
- group = &groups.back();
- }
-
- // Add the input_coin to group
- group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only);
- }
-
- // Now we go through the entire map and pull out the OutputGroups
- for (const auto& spk_and_groups_pair: spk_to_groups_map) {
- const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second;
-
- // Go through the vector backwards. This allows for the first item we deal with being the partial group.
- for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) {
- const OutputGroup& group = *group_it;
-
- // Don't include partial groups if there are full groups too and we don't want partial groups
- if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) {
- continue;
- }
-
- // Check the OutputGroup's eligibility. Only add the eligible ones.
- if (positive_only && group.effective_value <= 0) continue;
- if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group);
- }
- }
-
- return groups_out;
-}
-
bool CWallet::IsCrypted() const
{
return HasEncryptionKeys();
@@ -4516,7 +3067,7 @@ void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
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");
+ throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
#endif
} else {
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
@@ -4585,8 +3136,8 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
}
}
#else
- throw std::runtime_error(std::string(__func__) + ": Wallets with external signers require Boost::Process library.");
-#endif
+ throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
+#endif // ENABLE_EXTERNAL_SIGNER
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 6031d36b97..788a901f95 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,8 +21,11 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
+#include <external_signer.h>
+#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
-#include <wallet/external_signer.h>
+#include <wallet/spend.h>
+#include <wallet/transaction.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -215,422 +218,6 @@ struct CRecipient
bool fSubtractFeeFromAmount;
};
-typedef std::map<std::string, std::string> mapValue_t;
-
-
-static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (!mapValue.count("n"))
- {
- nOrderPos = -1; // TODO: calculate elsewhere
- return;
- }
- nOrderPos = atoi64(mapValue["n"]);
-}
-
-
-static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (nOrderPos == -1)
- return;
- mapValue["n"] = ToString(nOrderPos);
-}
-
-struct COutputEntry
-{
- CTxDestination destination;
- CAmount amount;
- int vout;
-};
-
-/** Legacy class used for deserializing vtxPrev for backwards compatibility.
- * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
- * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
- * These need to get deserialized for field alignment when deserializing
- * a CWalletTx, but the deserialized values are discarded.**/
-class CMerkleTx
-{
-public:
- template<typename Stream>
- void Unserialize(Stream& s)
- {
- CTransactionRef tx;
- uint256 hashBlock;
- std::vector<uint256> vMerkleBranch;
- int nIndex;
-
- s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
- }
-};
-
-//Get the marginal bytes of spending the specified output
-int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
-
-/**
- * A transaction with a bunch of additional info that only the owner cares about.
- * It includes any unrecorded transactions needed to link it back to the block chain.
- */
-class CWalletTx
-{
-private:
- const CWallet* const pwallet;
-
- /** Constant used in hashBlock to indicate tx has been abandoned, only used at
- * serialization/deserialization to avoid ambiguity with conflicted.
- */
- static constexpr const uint256& ABANDON_HASH = uint256::ONE;
-
-public:
- /**
- * Key/value map with information about the transaction.
- *
- * The following keys can be read and written through the map and are
- * serialized in the wallet database:
- *
- * "comment", "to" - comment strings provided to sendtoaddress,
- * and sendmany wallet RPCs
- * "replaces_txid" - txid (as HexStr) of transaction replaced by
- * bumpfee on transaction created by bumpfee
- * "replaced_by_txid" - txid (as HexStr) of transaction created by
- * bumpfee on transaction replaced by bumpfee
- * "from", "message" - obsolete fields that could be set in UI prior to
- * 2011 (removed in commit 4d9b223)
- *
- * The following keys are serialized in the wallet database, but shouldn't
- * be read or written through the map (they will be temporarily added and
- * removed from the map during serialization):
- *
- * "fromaccount" - serialized strFromAccount value
- * "n" - serialized nOrderPos value
- * "timesmart" - serialized nTimeSmart value
- * "spent" - serialized vfSpent value that existed prior to
- * 2014 (removed in commit 93a18a3)
- */
- mapValue_t mapValue;
- std::vector<std::pair<std::string, std::string> > vOrderForm;
- unsigned int fTimeReceivedIsTxTime;
- unsigned int nTimeReceived; //!< time received by this node
- /**
- * Stable timestamp that never changes, and reflects the order a transaction
- * was added to the wallet. Timestamp is based on the block time for a
- * transaction added as part of a block, or else the time when the
- * transaction was received if it wasn't part of a block, with the timestamp
- * adjusted in both cases so timestamp order matches the order transactions
- * were added to the wallet. More details can be found in
- * CWallet::ComputeTimeSmart().
- */
- unsigned int nTimeSmart;
- /**
- * From me flag is set to 1 for transactions that were created by the wallet
- * on this bitcoin node, and set to 0 for transactions that were created
- * externally and came in through the network or sendrawtransaction RPC.
- */
- bool fFromMe;
- int64_t nOrderPos; //!< position in ordered transaction list
- std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
-
- // memory only
- enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
- CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
- mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
- /**
- * This flag is true if all m_amounts caches are empty. This is particularly
- * useful in places where MarkDirty is conditionally called and the
- * condition can be expensive and thus can be skipped if the flag is true.
- * See MarkDestinationsDirty.
- */
- mutable bool m_is_cache_empty{true};
- mutable bool fChangeCached;
- mutable bool fInMempool;
- mutable CAmount nChangeCached;
-
- CWalletTx(const CWallet* wallet, CTransactionRef arg)
- : pwallet(wallet),
- tx(std::move(arg))
- {
- Init();
- }
-
- void Init()
- {
- mapValue.clear();
- vOrderForm.clear();
- fTimeReceivedIsTxTime = false;
- nTimeReceived = 0;
- nTimeSmart = 0;
- fFromMe = false;
- fChangeCached = false;
- fInMempool = false;
- nChangeCached = 0;
- nOrderPos = -1;
- m_confirm = Confirmation{};
- }
-
- CTransactionRef tx;
-
- /* New transactions start as UNCONFIRMED. At BlockConnected,
- * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected,
- * they roll back to UNCONFIRMED. If we detect a conflicting transaction at
- * block connection, we update conflicted tx and its dependencies as CONFLICTED.
- * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED
- * by using the abandontransaction call. This last status may be override by a CONFLICTED
- * or CONFIRMED transition.
- */
- enum Status {
- UNCONFIRMED,
- CONFIRMED,
- CONFLICTED,
- ABANDONED
- };
-
- /* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
- * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
- * Meaning of these fields changes with CONFLICTED state where they instead point to block hash
- * and block height of the deepest conflicting tx.
- */
- struct Confirmation {
- Status status;
- int block_height;
- uint256 hashBlock;
- int nIndex;
- Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
- };
-
- Confirmation m_confirm;
-
- template<typename Stream>
- void Serialize(Stream& s) const
- {
- mapValue_t mapValueCopy = mapValue;
-
- mapValueCopy["fromaccount"] = "";
- WriteOrderPos(nOrderPos, mapValueCopy);
- if (nTimeSmart) {
- mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
- }
-
- std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch
- std::vector<char> dummy_vector2; //!< Used to be vtxPrev
- bool dummy_bool = false; //!< Used to be fSpent
- uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock;
- int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex;
- s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
- }
-
- template<typename Stream>
- void Unserialize(Stream& s)
- {
- Init();
-
- std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
- std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
- bool dummy_bool; //! Used to be fSpent
- int serializedIndex;
- s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
-
- /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to
- * the earliest block in the chain we know this or any in-wallet ancestor conflicts
- * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned.
- * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or
- * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward
- * compatibility (pre-commit 9ac63d6).
- */
- if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
- setAbandoned();
- } else if (serializedIndex == -1) {
- setConflicted();
- } else if (!m_confirm.hashBlock.IsNull()) {
- m_confirm.nIndex = serializedIndex;
- setConfirmed();
- }
-
- ReadOrderPos(nOrderPos, mapValue);
- nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
-
- mapValue.erase("fromaccount");
- mapValue.erase("spent");
- mapValue.erase("n");
- mapValue.erase("timesmart");
- }
-
- void SetTx(CTransactionRef arg)
- {
- tx = std::move(arg);
- }
-
- //! make sure balances are recalculated
- void MarkDirty()
- {
- m_amounts[DEBIT].Reset();
- m_amounts[CREDIT].Reset();
- m_amounts[IMMATURE_CREDIT].Reset();
- m_amounts[AVAILABLE_CREDIT].Reset();
- fChangeCached = false;
- m_is_cache_empty = true;
- }
-
- //! filter decides which addresses will count towards the debit
- CAmount GetDebit(const isminefilter& filter) const;
- CAmount GetCredit(const isminefilter& filter) const;
- CAmount GetImmatureCredit(bool fUseCache = true) const;
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
- // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
- // having to resolve the issue of member access into incomplete type CWallet.
- CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
- CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
- CAmount GetChange() const;
-
- // Get the marginal bytes if spending the specified output from this transaction
- int GetSpendSize(unsigned int out, bool use_max_sig = false) const
- {
- return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
- }
-
- void GetAmounts(std::list<COutputEntry>& listReceived,
- std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const;
-
- bool IsFromMe(const isminefilter& filter) const
- {
- return (GetDebit(filter) > 0);
- }
-
- // True if only scriptSigs are different
- bool IsEquivalentTo(const CWalletTx& tx) const;
-
- bool InMempool() const;
- bool IsTrusted() const;
-
- int64_t GetTxTime() const;
-
- // Pass this transaction to node for mempool insertion and relay to peers if flag set to true
- bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
-
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
-
- /**
- * Return depth of transaction in blockchain:
- * <0 : conflicts with a transaction this deep in the blockchain
- * 0 : in memory pool, waiting to be included in a block
- * >=1 : this many blocks deep in the main chain
- */
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
- bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
-
- /**
- * @return number of blocks to maturity for this transaction:
- * 0 : is not a coinbase transaction, or is a mature coinbase transaction
- * >0 : is a coinbase transaction which matures in this many blocks
- */
- int GetBlocksToMaturity() const;
- bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
- void setAbandoned()
- {
- m_confirm.status = CWalletTx::ABANDONED;
- m_confirm.hashBlock = uint256();
- m_confirm.block_height = 0;
- m_confirm.nIndex = 0;
- }
- bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
- void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
- bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
- void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
- bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
- void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
- const uint256& GetHash() const { return tx->GetHash(); }
- bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase() const;
-
- // Disable copying of CWalletTx objects to prevent bugs where instances get
- // copied in and out of the mapWallet map, and fields are updated in the
- // wrong copy.
- CWalletTx(CWalletTx const &) = delete;
- void operator=(CWalletTx const &x) = delete;
-};
-
-class COutput
-{
-public:
- const CWalletTx *tx;
- int i;
- int nDepth;
-
- /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
- int nInputBytes;
-
- /** Whether we have the private keys to spend this output */
- bool fSpendable;
-
- /** Whether we know how to spend this output, ignoring the lack of keys */
- bool fSolvable;
-
- /** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */
- bool use_max_sig;
-
- /**
- * Whether this output is considered safe to spend. Unconfirmed transactions
- * from outside keys and unconfirmed replacement transactions are considered
- * unsafe and will not be used to fund new spending transactions.
- */
- bool fSafe;
-
- COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
- {
- tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
- // If known and signable by the given wallet, compute nInputBytes
- // Failure will keep this value -1
- if (fSpendable && tx) {
- nInputBytes = tx->GetSpendSize(i, use_max_sig);
- }
- }
-
- std::string ToString() const;
-
- inline CInputCoin GetInputCoin() const
- {
- return CInputCoin(tx->tx, i, nInputBytes);
- }
-};
-
-struct CoinSelectionParams
-{
- bool use_bnb = true;
- size_t change_output_size = 0;
- size_t change_spend_size = 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_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),
- 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)
- {}
- CoinSelectionParams() {}
-};
-
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
* A CWallet maintains a set of transactions and balances, and provides the ability to create new transactions.
@@ -640,7 +227,6 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
private:
CKeyingMaterial vMasterKey GUARDED_BY(cs_wallet);
-
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
std::atomic<bool> fAbortRescan{false};
@@ -652,7 +238,10 @@ private:
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE};
+ /** The next scheduled rebroadcast of wallet transactions. */
int64_t nNextResend = 0;
+ /** Whether this wallet will submit newly created transactions to the node's mempool and
+ * prompt rebroadcasts (see ResendWalletTransactions()). */
bool fBroadcastTransactions = false;
// Local time that the tip block was received. Used to schedule wallet rebroadcasts.
std::atomic<int64_t> m_best_block_time {0};
@@ -682,10 +271,10 @@ private:
*/
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
+ /** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
- /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
+ /** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -694,6 +283,7 @@ private:
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** WalletFlags set on this wallet. */
std::atomic<uint64_t> m_wallet_flags{0};
bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
@@ -722,7 +312,7 @@ private:
*/
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
- /* Height of last block processed is used by wallet to know depth of transactions
+ /** Height of last block processed is used by wallet to know depth of transactions
* without relying on Chain interface beyond asynchronous updates. For safety, we
* initialize it to -1. Height is a pointer on node's tip and doesn't imply
* that the wallet has scanned sequentially all blocks up to this one.
@@ -738,8 +328,15 @@ private:
bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign);
+ /**
+ * Catch wallet up to current chain, scanning new blocks, updating the best
+ * block locator and m_last_block_processed, and registering for
+ * notifications about new blocks and transactions.
+ */
+ static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings);
+
public:
- /*
+ /**
* Main wallet lock.
* This lock protects all the fields added by CWallet.
*/
@@ -753,11 +350,14 @@ public:
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
- * all coins from coinControl are selected; Never select unconfirmed coins
- * if they are not ours
+ * all coins from coin_control are selected; never select unconfirmed coins if they are not ours
+ * param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
+ * coin_control and Coin Selection if successful.
+ * param@[out] nValueRet Total value of selected coins including pre-selected ones
+ * from coin_control and Coin Selection if successful.
*/
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
- const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Get a name for this wallet for logging/debugging purposes.
*/
@@ -788,6 +388,8 @@ public:
/** Interface to assert chain access */
bool HaveChain() const { return m_chain ? true : false; }
+ /** Map from txid to CWalletTx for all transactions this wallet is
+ * interested in, including received and sent transactions. */
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
@@ -799,6 +401,10 @@ public:
std::map<CTxDestination, CAddressBookData> m_address_book GUARDED_BY(cs_wallet);
const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** Set of Coins owned by this wallet that we won't try to spend from. A
+ * Coin may be locked if it has already been used to fund a transaction
+ * that hasn't confirmed yet. We wouldn't consider the Coin spent already,
+ * but also shouldn't try to use it again. */
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
/** Registered interfaces::Chain::Notifications handler. */
@@ -816,7 +422,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe = true, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
@@ -833,9 +439,14 @@ public:
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled
+ * param@[in] coins Set of UTXOs to consider. These will be categorized into
+ * OutputGroups and filtered using eligibility_filter before
+ * selecting coins.
+ * param@[out] setCoinsRet Populated with the coins selected if successful.
+ * param@[out] nValueRet Used to return the total value of selected coins.
*/
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
- std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -843,11 +454,8 @@ public:
bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- 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;
+ std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, 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);
@@ -871,19 +479,8 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; }
- /**
- * Adds a destination data tuple to the store, and saves it to disk
- * When adding new fields, take care to consider how DelAddressBook should handle it!
- */
- bool AddDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Erases a destination data tuple in the store and on disk
- bool EraseDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a destination data tuple to the store, without saving it to disk
void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Look up a destination data tuple in the store, return true if found false otherwise
- bool GetDestData(const CTxDestination& dest, const std::string& key, std::string* value) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Get all destination values matching a prefix.
- std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime GUARDED_BY(cs_wallet){0};
@@ -959,9 +556,9 @@ public:
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
- // Fetch the inputs and sign with SIGHASH_ALL.
+ /** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- // Sign the tx given the input coins and sighash.
+ /** Sign the tx given the input coins and sighash. */
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const;
@@ -1018,6 +615,8 @@ public:
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
+ /** Allow Coin Selection to pick unconfirmed UTXOs that were sent from our own wallet if it
+ * cannot fund the transaction otherwise. */
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
bool m_signal_rbf{DEFAULT_WALLET_RBF};
bool m_allow_fallback_fee{true}; //!< will be false if -fallbackfee=0
@@ -1028,7 +627,12 @@ public:
* Override with -fallbackfee
*/
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
+
+ /** If the cost to spend a change output at this feerate is greater than the value of the
+ * output itself, just drop it to fees. */
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
+
+ /** The maximum fee amount we're willing to pay to prioritize partial spend avoidance. */
CAmount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; //!< note: this is absolute fee, not fee rate
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
/**
@@ -1083,13 +687,19 @@ public:
CAmount GetChange(const CTransaction& tx) const;
void chainStateFlushed(const CBlockLocator& loc) override;
- DBErrors LoadWallet(bool& fFirstRunRet);
+ DBErrors LoadWallet();
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
bool DelAddressBook(const CTxDestination& address);
+ bool IsAddressUsed(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ std::vector<std::string> GetAddressReceiveRequests() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! signify that a particular wallet feature is now used.
@@ -1159,7 +769,7 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
+ static std::shared_ptr<CWallet> Create(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
/**
* Wallet post-init setup
@@ -1181,7 +791,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() const EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1336,12 +946,17 @@ public:
}
};
-// Calculate the size of the transaction assuming all signatures are max size
-// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
-// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
-// be IsAllFromMe).
-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);
+struct TxSize {
+ int64_t vsize{-1};
+ int64_t weight{-1};
+};
+
+/** Calculate the size of the transaction assuming all signatures are max size
+* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
+* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+* be IsAllFromMe). */
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+TxSize 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 3d9248009f..c06b319b0b 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -155,7 +155,7 @@ bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMet
if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
return false;
}
- return WriteIC(std::make_pair(DBKeys::WATCHS, dest), '1');
+ return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
}
bool WalletBatch::EraseWatchOnly(const CScript &dest)
@@ -308,8 +308,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{
if (!ssValue.empty())
{
- char fTmp;
- char fUnused;
+ uint8_t fTmp;
+ uint8_t fUnused;
std::string unused_string;
ssValue >> fTmp >> fUnused >> unused_string;
strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
@@ -336,7 +336,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.nWatchKeys++;
CScript script;
ssKey >> script;
- char fYes;
+ uint8_t fYes;
ssValue >> fYes;
if (fYes == '1') {
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index b2cb0bf479..50b6c9d29f 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -54,8 +54,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
- bool first_run;
- load_wallet_ret = wallet_instance->LoadWallet(first_run);
+ load_wallet_ret = wallet_instance->LoadWallet();
} catch (const std::runtime_error&) {
tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name);
return nullptr;
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index dd2f071b6c..1c518daba6 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -19,7 +19,7 @@ fs::path GetWalletDir()
path = "";
}
} else {
- path = GetDataDir();
+ path = gArgs.GetDataDirNet();
// If a wallets directory exists, use that, otherwise default to GetDataDir
if (fs::is_directory(path / "wallets")) {
path /= "wallets";
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 168ba841c8..6ae866cc07 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -6,10 +6,11 @@
#include <chain.h>
#include <chainparams.h>
+#include <node/blockstorage.h>
#include <rpc/server.h>
#include <streams.h>
#include <util/system.h>
-#include <validation.h>
+#include <validation.h> // For cs_main
#include <zmq/zmqutil.h>
#include <zmq.h>
@@ -167,7 +168,7 @@ bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void
/* send three parts, command & data & a LE 4byte sequence number */
unsigned char msgseq[sizeof(uint32_t)];
- WriteLE32(&msgseq[0], nSequence);
+ WriteLE32(msgseq, nSequence);
int rc = zmq_send_multipart(psocket, command, strlen(command), data, size, msgseq, (size_t)sizeof(uint32_t), nullptr);
if (rc == -1)
return false;
diff --git a/test/README.md b/test/README.md
index 17bf8a1406..ab34ce42dc 100644
--- a/test/README.md
+++ b/test/README.md
@@ -262,7 +262,7 @@ Use the `-v` option for verbose output.
|-----------|:----------:|:-------------------------------------------:|--------------
| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8) | [3.8.3](https://github.com/bitcoin/bitcoin/pull/19348) | `pip3 install flake8==3.8.3`
| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy) | [0.781](https://github.com/bitcoin/bitcoin/pull/19348) | `pip3 install mypy==0.781`
-| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [0.7.1](https://github.com/bitcoin/bitcoin/pull/19348) | [details...](https://github.com/koalaman/shellcheck#installing)
+| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [0.7.2](https://github.com/bitcoin/bitcoin/pull/21749) | [details...](https://github.com/koalaman/shellcheck#installing)
| [`lint-shell.sh`](lint/lint-shell.sh) | [yq](https://github.com/kislyuk/yq) | default | `pip3 install yq`
| [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell) | [2.0.0](https://github.com/bitcoin/bitcoin/pull/20817) | `pip3 install codespell==2.0.0`
@@ -273,7 +273,7 @@ Please be aware that on Linux distributions all dependencies are usually availab
Individual tests can be run by directly calling the test script, e.g.:
```
-test/lint/lint-filenames.sh
+test/lint/lint-files.sh
```
You can run all the shell-based lint tests by running:
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 1a148f04f4..a4480307a7 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -31,6 +31,7 @@ Start three nodes:
"""
from test_framework.blocktools import (
+ COINBASE_MATURITY,
create_block,
create_coinbase,
)
@@ -161,8 +162,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node0. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p0)
- self.wait_until(lambda: self.nodes[0].getblockcount() >= 101)
- assert_equal(self.nodes[0].getblockcount(), 101)
+ self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1)
+ assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
@@ -173,8 +174,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node2. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p2)
- self.wait_until(lambda: self.nodes[2].getblockcount() >= 101)
- assert_equal(self.nodes[2].getblockcount(), 101)
+ self.wait_until(lambda: self.nodes[2].getblockcount() >= COINBASE_MATURITY + 1)
+ assert_equal(self.nodes[2].getblockcount(), COINBASE_MATURITY + 1)
if __name__ == '__main__':
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index e6a53b52db..c712f7141c 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -22,6 +22,7 @@ needs an older patch version.
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
@@ -64,13 +65,13 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.import_deterministic_coinbase_privkeys()
def run_test(self):
- self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
self.sync_blocks()
# Sanity check the test framework:
res = self.nodes[self.num_nodes - 1].getblockchaininfo()
- assert_equal(res['blocks'], 101)
+ assert_equal(res['blocks'], COINBASE_MATURITY + 1)
node_master = self.nodes[self.num_nodes - 5]
node_v19 = self.nodes[self.num_nodes - 4]
diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py
index d13d191b20..28d8f2fbbc 100755
--- a/test/functional/feature_blockfilterindex_prune.py
+++ b/test/functional/feature_blockfilterindex_prune.py
@@ -33,7 +33,7 @@ class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
self.log.info("prune some blocks")
pruneheight = self.nodes[0].pruneblockchain(400)
- assert_equal(pruneheight, 250)
+ assert_equal(pruneheight, 248)
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)
@@ -54,11 +54,13 @@ class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
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.nodes[0].assert_start_raises_init_error(
+ extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"],
+ expected_msg="Error: 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.log.info("make sure the node starts again with the -reindex arg")
- self.start_node(0, extra_args = ["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"])
+ self.start_node(0, extra_args=["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"])
if __name__ == '__main__':
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index b7c2887ee8..10d2072dba 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -8,47 +8,74 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height
1351.
"""
-from test_framework.blocktools import create_coinbase, create_block, create_transaction
-from test_framework.messages import CTransaction, msg_block, ToHex
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
+from test_framework.messages import (
+ CTransaction,
+ msg_block,
+)
from test_framework.p2p import P2PInterface
-from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP, CScriptNum
+from test_framework.script import (
+ CScript,
+ CScriptNum,
+ OP_1NEGATE,
+ OP_CHECKLOCKTIMEVERIFY,
+ OP_DROP,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- hex_str_to_bytes,
+from test_framework.util import assert_equal
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
)
-from io import BytesIO
-
CLTV_HEIGHT = 1351
-def cltv_invalidate(tx):
- '''Modify the signature in vin 0 of the tx to fail CLTV
+# Helper function to modify a transaction by
+# 1) prepending a given script to the scriptSig of vin 0 and
+# 2) (optionally) modify the nSequence of vin 0 and the tx's nLockTime
+def cltv_modify_tx(tx, prepend_scriptsig, nsequence=None, nlocktime=None):
+ assert_equal(len(tx.vin), 1)
+ if nsequence is not None:
+ tx.vin[0].nSequence = nsequence
+ tx.nLockTime = nlocktime
+
+ tx.vin[0].scriptSig = CScript(prepend_scriptsig + list(CScript(tx.vin[0].scriptSig)))
+ tx.rehash()
- Prepends -1 CLTV DROP in the scriptSig itself.
- TODO: test more ways that transactions using CLTV could be invalid (eg
- locktime requirements fail, sequence time requirements fail, etc).
- '''
- tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
- list(CScript(tx.vin[0].scriptSig)))
+def cltv_invalidate(tx, failure_reason):
+ # Modify the signature in vin 0 and nSequence/nLockTime of the tx to fail CLTV
+ #
+ # According to BIP65, OP_CHECKLOCKTIMEVERIFY can fail due the following reasons:
+ # 1) the stack is empty
+ # 2) the top item on the stack is less than 0
+ # 3) the lock-time type (height vs. timestamp) of the top stack item and the
+ # nLockTime field are not the same
+ # 4) the top stack item is greater than the transaction's nLockTime field
+ # 5) the nSequence field of the txin is 0xffffffff
+ assert failure_reason in range(5)
+ scheme = [
+ # | Script to prepend to scriptSig | nSequence | nLockTime |
+ # +-------------------------------------------------+------------+--------------+
+ [[OP_CHECKLOCKTIMEVERIFY], None, None],
+ [[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None],
+ [[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block
+ [[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 500],
+ [[CScriptNum(500), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 500],
+ ][failure_reason]
-def cltv_validate(node, tx, height):
- '''Modify the signature in vin 0 of the tx to pass CLTV
- Prepends <height> CLTV DROP in the scriptSig, and sets
- the locktime to height'''
- tx.vin[0].nSequence = 0
- tx.nLockTime = height
+ cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
- # Need to re-sign, since nSequence and nLockTime changed
- signed_result = node.signrawtransactionwithwallet(ToHex(tx))
- new_tx = CTransaction()
- new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex'])))
- new_tx.vin[0].scriptSig = CScript([CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
- list(CScript(new_tx.vin[0].scriptSig)))
- return new_tx
+def cltv_validate(tx, height):
+ # Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV
+ scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height]
+
+ cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
class BIP65Test(BitcoinTestFramework):
@@ -62,12 +89,8 @@ class BIP65Test(BitcoinTestFramework):
self.setup_clean_chain = True
self.rpc_timeout = 480
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def test_cltv_info(self, *, is_active):
- assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'],
- {
+ assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'], {
"active": is_active,
"height": CLTV_HEIGHT,
"type": "buried",
@@ -76,25 +99,28 @@ class BIP65Test(BitcoinTestFramework):
def run_test(self):
peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE)
self.test_cltv_info(is_active=False)
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
- self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2)]
- self.nodeaddress = self.nodes[0].getnewaddress()
+ wallet.generate(10)
+ self.nodes[0].generate(CLTV_HEIGHT - 2 - 10)
- self.log.info("Test that an invalid-according-to-CLTV transaction can still appear in a block")
+ self.log.info("Test that invalid-according-to-CLTV transactions can still appear in a block")
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0],
- self.nodeaddress, amount=1.0)
- cltv_invalidate(spendtx)
- spendtx.rehash()
+ # create one invalid tx per CLTV failure reason (5 in total) and collect them
+ invalid_cltv_txs = []
+ for i in range(5):
+ spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
+ cltv_invalidate(spendtx, i)
+ invalid_cltv_txs.append(spendtx)
tip = self.nodes[0].getbestblockhash()
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time)
block.nVersion = 3
- block.vtx.append(spendtx)
+ block.vtx.extend(invalid_cltv_txs)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
@@ -115,39 +141,47 @@ class BIP65Test(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
- self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
+ self.log.info("Test that invalid-according-to-CLTV transactions cannot appear in a block")
block.nVersion = 4
-
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1],
- self.nodeaddress, amount=1.0)
- cltv_invalidate(spendtx)
- spendtx.rehash()
-
- # First we show that this tx is valid except for CLTV by getting it
- # rejected from the mempool for exactly that reason.
- assert_equal(
- [{
- 'txid': spendtx.hash,
- 'wtxid': spendtx.getwtxid(),
- 'allowed': False,
- 'reject-reason': 'non-mandatory-script-verify-flag (Negative locktime)',
- }],
- self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
- )
-
- # Now we verify that a block with this transaction is also invalid.
- block.vtx.append(spendtx)
- block.hashMerkleRoot = block.calc_merkle_root()
- block.solve()
-
- with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]):
- peer.send_and_ping(msg_block(block))
- assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- peer.sync_with_ping()
+ block.vtx.append(CTransaction()) # dummy tx after coinbase that will be replaced later
+
+ # create and test one invalid tx per CLTV failure reason (5 in total)
+ for i in range(5):
+ spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
+ cltv_invalidate(spendtx, i)
+
+ expected_cltv_reject_reason = [
+ "non-mandatory-script-verify-flag (Operation not valid with the current stack size)",
+ "non-mandatory-script-verify-flag (Negative locktime)",
+ "non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
+ "non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
+ "non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
+ ][i]
+ # First we show that this tx is valid except for CLTV by getting it
+ # rejected from the mempool for exactly that reason.
+ assert_equal(
+ [{
+ 'txid': spendtx.hash,
+ 'wtxid': spendtx.getwtxid(),
+ 'allowed': False,
+ 'reject-reason': expected_cltv_reject_reason,
+ }],
+ self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
+ )
+
+ # Now we verify that a block with this transaction is also invalid.
+ block.vtx[1] = spendtx
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+
+ with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with {}'.format(
+ block.vtx[-1].hash, expected_cltv_reject_reason)]):
+ peer.send_and_ping(msg_block(block))
+ assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
+ peer.sync_with_ping()
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
- spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
- spendtx.rehash()
+ cltv_validate(spendtx, CLTV_HEIGHT - 1)
block.vtx.pop(1)
block.vtx.append(spendtx)
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
new file mode 100755
index 0000000000..cf270b25ee
--- /dev/null
+++ b/test/functional/feature_coinstatsindex.py
@@ -0,0 +1,314 @@
+#!/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 coinstatsindex across nodes.
+
+Test that the values returned by gettxoutsetinfo are consistent
+between a node running the coinstatsindex and a node without
+the index.
+"""
+
+from decimal import Decimal
+
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ create_block,
+ create_coinbase,
+)
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ ToHex,
+)
+from test_framework.script import (
+ CScript,
+ OP_FALSE,
+ OP_RETURN,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ try_rpc,
+)
+
+class CoinStatsIndexTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+ self.supports_cli = False
+ self.extra_args = [
+ [],
+ ["-coinstatsindex"]
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ self._test_coin_stats_index()
+ self._test_use_index_option()
+ self._test_reorg_index()
+ self._test_index_rejects_hash_serialized()
+
+ def block_sanity_check(self, block_info):
+ block_subsidy = 50
+ assert_equal(
+ block_info['prevout_spent'] + block_subsidy,
+ block_info['new_outputs_ex_coinbase'] + block_info['coinbase'] + block_info['unspendable']
+ )
+
+ def _test_coin_stats_index(self):
+ node = self.nodes[0]
+ index_node = self.nodes[1]
+ # Both none and muhash options allow the usage of the index
+ index_hash_options = ['none', 'muhash']
+
+ # Generate a normal transaction and mine it
+ node.generate(COINBASE_MATURITY + 1)
+ address = self.nodes[0].get_deterministic_priv_key().address
+ node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
+ node.generate(1)
+
+ self.sync_blocks(timeout=120)
+
+ self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo))
+ res0 = node.gettxoutsetinfo('none')
+
+ # The fields 'disk_size' and 'transactions' do not exist on the index
+ del res0['disk_size'], res0['transactions']
+
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ for hash_option in index_hash_options:
+ res1 = index_node.gettxoutsetinfo(hash_option)
+ # The fields 'block_info' and 'total_unspendable_amount' only exist on the index
+ del res1['block_info'], res1['total_unspendable_amount']
+ res1.pop('muhash', None)
+
+ # Everything left should be the same
+ assert_equal(res1, res0)
+
+ self.log.info("Test that gettxoutsetinfo() can get fetch data on specific heights with index")
+
+ # Generate a new tip
+ node.generate(5)
+
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ for hash_option in index_hash_options:
+ # Fetch old stats by height
+ res2 = index_node.gettxoutsetinfo(hash_option, 102)
+ del res2['block_info'], res2['total_unspendable_amount']
+ res2.pop('muhash', None)
+ assert_equal(res0, res2)
+
+ # Fetch old stats by hash
+ res3 = index_node.gettxoutsetinfo(hash_option, res0['bestblock'])
+ del res3['block_info'], res3['total_unspendable_amount']
+ res3.pop('muhash', None)
+ assert_equal(res0, res3)
+
+ # It does not work without coinstatsindex
+ assert_raises_rpc_error(-8, "Querying specific block heights requires coinstatsindex", node.gettxoutsetinfo, hash_option, 102)
+
+ self.log.info("Test gettxoutsetinfo() with index and verbose flag")
+
+ for hash_option in index_hash_options:
+ # Genesis block is unspendable
+ res4 = index_node.gettxoutsetinfo(hash_option, 0)
+ assert_equal(res4['total_unspendable_amount'], 50)
+ assert_equal(res4['block_info'], {
+ 'unspendable': 50,
+ 'prevout_spent': 0,
+ 'new_outputs_ex_coinbase': 0,
+ 'coinbase': 0,
+ 'unspendables': {
+ 'genesis_block': 50,
+ 'bip30': 0,
+ 'scripts': 0,
+ 'unclaimed_rewards': 0
+ }
+ })
+ self.block_sanity_check(res4['block_info'])
+
+ # Test an older block height that included a normal tx
+ res5 = index_node.gettxoutsetinfo(hash_option, 102)
+ assert_equal(res5['total_unspendable_amount'], 50)
+ assert_equal(res5['block_info'], {
+ 'unspendable': 0,
+ 'prevout_spent': 50,
+ 'new_outputs_ex_coinbase': Decimal('49.99995560'),
+ 'coinbase': Decimal('50.00004440'),
+ 'unspendables': {
+ 'genesis_block': 0,
+ 'bip30': 0,
+ 'scripts': 0,
+ 'unclaimed_rewards': 0
+ }
+ })
+ self.block_sanity_check(res5['block_info'])
+
+ # Generate and send a normal tx with two outputs
+ tx1_inputs = []
+ tx1_outputs = {self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42}
+ raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs)
+ funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1)
+ signed_tx1 = self.nodes[0].signrawtransactionwithwallet(funded_tx1['hex'])
+ tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
+
+ # Find the right position of the 21 BTC output
+ tx1_final = self.nodes[0].gettransaction(tx1_txid)
+ for output in tx1_final['details']:
+ if output['amount'] == Decimal('21.00000000') and output['category'] == 'receive':
+ n = output['vout']
+
+ # Generate and send another tx with an OP_RETURN output (which is unspendable)
+ tx2 = CTransaction()
+ tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b''))
+ tx2.vout.append(CTxOut(int(20.99 * COIN), CScript([OP_RETURN] + [OP_FALSE]*30)))
+ tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))['hex']
+ self.nodes[0].sendrawtransaction(tx2_hex)
+
+ # Include both txs in a block
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ for hash_option in index_hash_options:
+ # Check all amounts were registered correctly
+ res6 = index_node.gettxoutsetinfo(hash_option, 108)
+ assert_equal(res6['total_unspendable_amount'], Decimal('70.98999999'))
+ assert_equal(res6['block_info'], {
+ 'unspendable': Decimal('20.98999999'),
+ 'prevout_spent': 111,
+ 'new_outputs_ex_coinbase': Decimal('89.99993620'),
+ 'coinbase': Decimal('50.01006381'),
+ 'unspendables': {
+ 'genesis_block': 0,
+ 'bip30': 0,
+ 'scripts': Decimal('20.98999999'),
+ 'unclaimed_rewards': 0
+ }
+ })
+ self.block_sanity_check(res6['block_info'])
+
+ # Create a coinbase that does not claim full subsidy and also
+ # has two outputs
+ cb = create_coinbase(109, nValue=35)
+ cb.vout.append(CTxOut(5 * COIN, CScript([OP_FALSE])))
+ cb.rehash()
+
+ # Generate a block that includes previous coinbase
+ tip = self.nodes[0].getbestblockhash()
+ block_time = self.nodes[0].getblock(tip)['time'] + 1
+ block = create_block(int(tip, 16), cb, block_time)
+ block.solve()
+ self.nodes[0].submitblock(ToHex(block))
+ self.sync_all()
+
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ for hash_option in index_hash_options:
+ res7 = index_node.gettxoutsetinfo(hash_option, 109)
+ assert_equal(res7['total_unspendable_amount'], Decimal('80.98999999'))
+ assert_equal(res7['block_info'], {
+ 'unspendable': 10,
+ 'prevout_spent': 0,
+ 'new_outputs_ex_coinbase': 0,
+ 'coinbase': 40,
+ 'unspendables': {
+ 'genesis_block': 0,
+ 'bip30': 0,
+ 'scripts': 0,
+ 'unclaimed_rewards': 10
+ }
+ })
+ self.block_sanity_check(res7['block_info'])
+
+ self.log.info("Test that the index is robust across restarts")
+
+ res8 = index_node.gettxoutsetinfo('muhash')
+ self.restart_node(1, extra_args=self.extra_args[1])
+ res9 = index_node.gettxoutsetinfo('muhash')
+ assert_equal(res8, res9)
+
+ index_node.generate(1)
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ res10 = index_node.gettxoutsetinfo('muhash')
+ assert(res8['txouts'] < res10['txouts'])
+
+ def _test_use_index_option(self):
+ self.log.info("Test use_index option for nodes running the index")
+
+ self.connect_nodes(0, 1)
+ self.nodes[0].waitforblockheight(110)
+ res = self.nodes[0].gettxoutsetinfo('muhash')
+ option_res = self.nodes[1].gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
+ del res['disk_size'], option_res['disk_size']
+ assert_equal(res, option_res)
+
+ def _test_reorg_index(self):
+ self.log.info("Test that index can handle reorgs")
+
+ # Generate two block, let the index catch up, then invalidate the blocks
+ index_node = self.nodes[1]
+ reorg_blocks = index_node.generatetoaddress(2, index_node.getnewaddress())
+ reorg_block = reorg_blocks[1]
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ res_invalid = index_node.gettxoutsetinfo('muhash')
+ index_node.invalidateblock(reorg_blocks[0])
+ assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
+
+ # Add two new blocks
+ block = index_node.generate(2)[1]
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
+
+ # Test that the result of the reorged block is not returned for its old block height
+ res2 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112)
+ assert_equal(res["bestblock"], block)
+ assert_equal(res["muhash"], res2["muhash"])
+ assert(res["muhash"] != res_invalid["muhash"])
+
+ # Test that requesting reorged out block by hash is still returning correct results
+ res_invalid2 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=reorg_block)
+ assert_equal(res_invalid2["muhash"], res_invalid["muhash"])
+ assert(res["muhash"] != res_invalid2["muhash"])
+
+ # Add another block, so we don't depend on reconsiderblock remembering which
+ # blocks were touched by invalidateblock
+ index_node.generate(1)
+
+ # Ensure that removing and re-adding blocks yields consistent results
+ block = index_node.getblockhash(99)
+ index_node.invalidateblock(block)
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ index_node.reconsiderblock(block)
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
+ res3 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112)
+ assert_equal(res2, res3)
+
+ self.log.info("Test that a node aware of stale blocks syncs them as well")
+ node = self.nodes[0]
+ # Ensure the node is aware of a stale block prior to restart
+ node.getblock(reorg_block)
+
+ self.restart_node(0, ["-coinstatsindex"])
+ self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash'))
+ assert_raises_rpc_error(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash', reorg_block)
+
+ def _test_index_rejects_hash_serialized(self):
+ self.log.info("Test that the rpc raises if the legacy hash is passed with the index")
+
+ msg = "hash_serialized_2 hash type cannot be queried for a specific block"
+ assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111)
+
+ for use_index in {True, False, None}:
+ assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111, use_index=use_index)
+
+
+if __name__ == '__main__':
+ CoinStatsIndexTest().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index a0bcd9f12a..de9d0d2e80 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -165,6 +165,7 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"0 addresses found from DNS seeds",
+ "opencon thread start", # Ensure ThreadOpenConnections::start time is properly set
]):
self.start_node(0, extra_args=['-dnsseed=1', '-fixedseeds=1', f'-mocktime={start}'])
with self.nodes[0].assert_debug_log(expected_msgs=[
@@ -206,6 +207,7 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"DNS seeding disabled",
+ "opencon thread start", # Ensure ThreadOpenConnections::start time is properly set
]):
self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1', '-addnode=fakenodeaddr', f'-mocktime={start}'])
with self.nodes[0].assert_debug_log(expected_msgs=[
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 46ba18b9b5..5081867319 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -9,8 +9,8 @@ BIP 68 - nSequence relative lock times
BIP 112 - CHECKSEQUENCEVERIFY
BIP 113 - MedianTimePast semantics for nLockTime
-mine 82 blocks whose coinbases will be used to generate inputs for our tests
-mine 345 blocks and seed block chain with the 82 inputs will use for our tests at height 427
+mine 83 blocks whose coinbases will be used to generate inputs for our tests
+mine 344 blocks and seed block chain with the 83 inputs used for our tests at height 427
mine 2 blocks and verify soft fork not yet activated
mine 1 block and test that soft fork is activated (rules enforced for next block)
Test BIP 113 is enforced
@@ -37,13 +37,13 @@ bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {r
bip112tx_special - test negative argument to OP_CSV
bip112tx_emptystack - test empty stack (= no argument) OP_CSV
"""
-from decimal import Decimal
from itertools import product
-from io import BytesIO
import time
-from test_framework.blocktools import create_coinbase, create_block, create_transaction
-from test_framework.messages import ToHex, CTransaction
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
from test_framework.p2p import P2PDataStore
from test_framework.script import (
CScript,
@@ -53,9 +53,12 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- hex_str_to_bytes,
softfork_active,
)
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
+)
TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
@@ -83,66 +86,6 @@ def relative_locktime(sdf, srhb, stf, srlb):
def all_rlt_txs(txs):
return [tx['tx'] for tx in txs]
-def sign_transaction(node, unsignedtx):
- rawtx = ToHex(unsignedtx)
- signresult = node.signrawtransactionwithwallet(rawtx)
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(signresult['hex']))
- tx.deserialize(f)
- return tx
-
-def create_bip112special(node, input, txversion, address):
- tx = create_transaction(node, input, address, amount=Decimal("49.98"))
- tx.nVersion = txversion
- signtx = sign_transaction(node, tx)
- signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- return signtx
-
-def create_bip112emptystack(node, input, txversion, address):
- tx = create_transaction(node, input, address, amount=Decimal("49.98"))
- tx.nVersion = txversion
- signtx = sign_transaction(node, tx)
- signtx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(signtx.vin[0].scriptSig)))
- return signtx
-
-def send_generic_input_tx(node, coinbases, address):
- return node.sendrawtransaction(ToHex(sign_transaction(node, create_transaction(node, node.getblock(coinbases.pop())['tx'][0], address, amount=Decimal("49.99")))))
-
-def create_bip68txs(node, bip68inputs, txversion, address, locktime_delta=0):
- """Returns a list of bip68 transactions with different bits set."""
- txs = []
- assert len(bip68inputs) >= 16
- for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
- locktime = relative_locktime(sdf, srhb, stf, srlb)
- tx = create_transaction(node, bip68inputs[i], address, amount=Decimal("49.98"))
- tx.nVersion = txversion
- tx.vin[0].nSequence = locktime + locktime_delta
- tx = sign_transaction(node, tx)
- tx.rehash()
- txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
-
- return txs
-
-def create_bip112txs(node, bip112inputs, varyOP_CSV, txversion, address, locktime_delta=0):
- """Returns a list of bip68 transactions with different bits set."""
- txs = []
- assert len(bip112inputs) >= 16
- for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
- locktime = relative_locktime(sdf, srhb, stf, srlb)
- tx = create_transaction(node, bip112inputs[i], address, amount=Decimal("49.98"))
- if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
- tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
- else: # vary nSequence instead, OP_CSV is fixed
- tx.vin[0].nSequence = locktime + locktime_delta
- tx.nVersion = txversion
- signtx = sign_transaction(node, tx)
- if (varyOP_CSV):
- signtx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- else:
- signtx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
- tx.rehash()
- txs.append({'tx': signtx, 'sdf': sdf, 'stf': stf})
- return txs
class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
@@ -150,13 +93,69 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.setup_clean_chain = True
self.extra_args = [[
'-whitelist=noban@127.0.0.1',
- '-addresstype=legacy',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def create_self_transfer_from_utxo(self, input_tx):
+ utxo = self.miniwallet.get_utxo(txid=input_tx.rehash(), mark_as_spent=False)
+ tx = self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo)['tx']
+ return tx
+
+ def create_bip112special(self, input, txversion):
+ tx = self.create_self_transfer_from_utxo(input)
+ tx.nVersion = txversion
+ self.miniwallet.sign_tx(tx)
+ tx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
+ return tx
+
+ def create_bip112emptystack(self, input, txversion):
+ tx = self.create_self_transfer_from_utxo(input)
+ tx.nVersion = txversion
+ self.miniwallet.sign_tx(tx)
+ tx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(tx.vin[0].scriptSig)))
+ return tx
+
+ def send_generic_input_tx(self, coinbases):
+ input_txid = self.nodes[0].getblock(coinbases.pop(), 2)['tx'][0]['txid']
+ utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid)
+ return self.miniwallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
+
+ def create_bip68txs(self, bip68inputs, txversion, locktime_delta=0):
+ """Returns a list of bip68 transactions with different bits set."""
+ txs = []
+ assert len(bip68inputs) >= 16
+ for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
+ locktime = relative_locktime(sdf, srhb, stf, srlb)
+ tx = self.create_self_transfer_from_utxo(bip68inputs[i])
+ tx.nVersion = txversion
+ tx.vin[0].nSequence = locktime + locktime_delta
+ self.miniwallet.sign_tx(tx)
+ tx.rehash()
+ txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
+
+ return txs
+
+ def create_bip112txs(self, bip112inputs, varyOP_CSV, txversion, locktime_delta=0):
+ """Returns a list of bip68 transactions with different bits set."""
+ txs = []
+ assert len(bip112inputs) >= 16
+ for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
+ locktime = relative_locktime(sdf, srhb, stf, srlb)
+ tx = self.create_self_transfer_from_utxo(bip112inputs[i])
+ if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
+ tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
+ else: # vary nSequence instead, OP_CSV is fixed
+ tx.vin[0].nSequence = locktime + locktime_delta
+ tx.nVersion = txversion
+ self.miniwallet.sign_tx(tx)
+ if (varyOP_CSV):
+ tx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
+ else:
+ tx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
+ tx.rehash()
+ txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
+ return txs
def generate_blocks(self, number):
test_blocks = []
@@ -185,16 +184,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
def run_test(self):
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
self.log.info("Generate blocks in the past for coinbase outputs.")
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
self.nodes[0].setmocktime(long_past_time - 100) # enough so that the generated blocks will still all be before long_past_time
- self.coinbase_blocks = self.nodes[0].generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
+ self.coinbase_blocks = self.miniwallet.generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
self.nodes[0].setmocktime(0) # set time back to present so yielded blocks aren't in the future as we advance last_block_time
self.tipheight = COINBASE_BLOCK_COUNT # height of the next block to build
self.last_block_time = long_past_time
self.tip = int(self.nodes[0].getbestblockhash(), 16)
- self.nodeaddress = self.nodes[0].getnewaddress()
# Activation height is hardcoded
# We advance to block height five below BIP112 activation for the following tests
@@ -209,14 +208,14 @@ class BIP68_112_113Test(BitcoinTestFramework):
# 16 normal inputs
bip68inputs = []
for _ in range(16):
- bip68inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
+ bip68inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
# 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
bip112basicinputs = []
for _ in range(2):
inputs = []
for _ in range(16):
- inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
+ inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
bip112basicinputs.append(inputs)
# 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
@@ -224,16 +223,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
for _ in range(2):
inputs = []
for _ in range(16):
- inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
+ inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
bip112diverseinputs.append(inputs)
# 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
- bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
+ bip112specialinput = self.send_generic_input_tx(self.coinbase_blocks)
# 1 special input with (empty stack) OP_CSV (actually will be prepended to spending scriptSig)
- bip112emptystackinput = send_generic_input_tx(self.nodes[0],self.coinbase_blocks, self.nodeaddress)
+ bip112emptystackinput = self.send_generic_input_tx(self.coinbase_blocks)
# 1 normal input
- bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
+ bip113input = self.send_generic_input_tx(self.coinbase_blocks)
self.nodes[0].setmocktime(self.last_block_time + 600)
inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431
@@ -253,36 +252,36 @@ class BIP68_112_113Test(BitcoinTestFramework):
# Test both version 1 and version 2 transactions for all tests
# BIP113 test transaction will be modified before each use to put in appropriate block time
- bip113tx_v1 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, amount=Decimal("49.98"))
+ bip113tx_v1 = self.create_self_transfer_from_utxo(bip113input)
bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
bip113tx_v1.nVersion = 1
- bip113tx_v2 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, amount=Decimal("49.98"))
+ bip113tx_v2 = self.create_self_transfer_from_utxo(bip113input)
bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
bip113tx_v2.nVersion = 2
# For BIP68 test all 16 relative sequence locktimes
- bip68txs_v1 = create_bip68txs(self.nodes[0], bip68inputs, 1, self.nodeaddress)
- bip68txs_v2 = create_bip68txs(self.nodes[0], bip68inputs, 2, self.nodeaddress)
+ bip68txs_v1 = self.create_bip68txs(bip68inputs, 1)
+ bip68txs_v2 = self.create_bip68txs(bip68inputs, 2)
# For BIP112 test:
# 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
- bip112txs_vary_nSequence_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 1, self.nodeaddress)
- bip112txs_vary_nSequence_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 2, self.nodeaddress)
+ bip112txs_vary_nSequence_v1 = self.create_bip112txs(bip112basicinputs[0], False, 1)
+ bip112txs_vary_nSequence_v2 = self.create_bip112txs(bip112basicinputs[0], False, 2)
# 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
- bip112txs_vary_nSequence_9_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 1, self.nodeaddress, -1)
- bip112txs_vary_nSequence_9_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 2, self.nodeaddress, -1)
+ bip112txs_vary_nSequence_9_v1 = self.create_bip112txs(bip112basicinputs[1], False, 1, -1)
+ bip112txs_vary_nSequence_9_v2 = self.create_bip112txs(bip112basicinputs[1], False, 2, -1)
# sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
- bip112txs_vary_OP_CSV_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 1, self.nodeaddress)
- bip112txs_vary_OP_CSV_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 2, self.nodeaddress)
+ bip112txs_vary_OP_CSV_v1 = self.create_bip112txs(bip112diverseinputs[0], True, 1)
+ bip112txs_vary_OP_CSV_v2 = self.create_bip112txs(bip112diverseinputs[0], True, 2)
# sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
- bip112txs_vary_OP_CSV_9_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 1, self.nodeaddress, -1)
- bip112txs_vary_OP_CSV_9_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 2, self.nodeaddress, -1)
+ bip112txs_vary_OP_CSV_9_v1 = self.create_bip112txs(bip112diverseinputs[1], True, 1, -1)
+ bip112txs_vary_OP_CSV_9_v2 = self.create_bip112txs(bip112diverseinputs[1], True, 2, -1)
# -1 OP_CSV OP_DROP input
- bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
- bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)
+ bip112tx_special_v1 = self.create_bip112special(bip112specialinput, 1)
+ bip112tx_special_v2 = self.create_bip112special(bip112specialinput, 2)
# (empty stack) OP_CSV input
- bip112tx_emptystack_v1 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 1, self.nodeaddress)
- bip112tx_emptystack_v2 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 2, self.nodeaddress)
+ bip112tx_emptystack_v1 = self.create_bip112emptystack(bip112emptystackinput, 1)
+ bip112tx_emptystack_v2 = self.create_bip112emptystack(bip112emptystackinput, 2)
self.log.info("TESTING")
@@ -292,8 +291,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
success_txs = []
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
- success_txs.append(bip113signed1)
+ self.miniwallet.sign_tx(bip113tx_v1)
+ success_txs.append(bip113tx_v1)
success_txs.append(bip112tx_special_v1)
success_txs.append(bip112tx_emptystack_v1)
# add BIP 68 txs
@@ -312,8 +311,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
success_txs = []
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
- success_txs.append(bip113signed2)
+ self.miniwallet.sign_tx(bip113tx_v2)
+ success_txs.append(bip113tx_v2)
success_txs.append(bip112tx_special_v2)
success_txs.append(bip112tx_emptystack_v2)
# add BIP 68 txs
@@ -338,17 +337,22 @@ class BIP68_112_113Test(BitcoinTestFramework):
self.log.info("BIP 113 tests")
# BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
+ self.miniwallet.sign_tx(bip113tx_v1)
+ bip113tx_v1.rehash()
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
- bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
- for bip113tx in [bip113signed1, bip113signed2]:
+ self.miniwallet.sign_tx(bip113tx_v2)
+ bip113tx_v2.rehash()
+ for bip113tx in [bip113tx_v1, bip113tx_v2]:
self.send_blocks([self.create_test_block([bip113tx])], success=False, reject_reason='bad-txns-nonfinal')
+
# BIP 113 tests should now pass if the locktime is < MTP
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
- bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
+ self.miniwallet.sign_tx(bip113tx_v1)
+ bip113tx_v1.rehash()
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
- bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
- for bip113tx in [bip113signed1, bip113signed2]:
+ self.miniwallet.sign_tx(bip113tx_v2)
+ bip113tx_v2.rehash()
+ for bip113tx in [bip113tx_v1, bip113tx_v2]:
self.send_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
@@ -471,8 +475,9 @@ class BIP68_112_113Test(BitcoinTestFramework):
time_txs = []
for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]:
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
- signtx = sign_transaction(self.nodes[0], tx)
- time_txs.append(signtx)
+ self.miniwallet.sign_tx(tx)
+ tx.rehash()
+ time_txs.append(tx)
self.send_blocks([self.create_test_block(time_txs)])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 3b430139b1..eb027c554a 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -7,7 +7,10 @@
Test that the DERSIG soft-fork activates at (regtest) height 1251.
"""
-from test_framework.blocktools import create_coinbase, create_block, create_transaction
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
from test_framework.messages import msg_block
from test_framework.p2p import P2PInterface
from test_framework.script import CScript
@@ -15,6 +18,10 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
+)
DERSIG_HEIGHT = 1251
@@ -46,8 +53,9 @@ class BIP66Test(BitcoinTestFramework):
self.setup_clean_chain = True
self.rpc_timeout = 240
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def create_tx(self, input_txid):
+ utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid, mark_as_spent=False)
+ return self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
def test_dersig_info(self, *, is_active):
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
@@ -60,17 +68,16 @@ class BIP66Test(BitcoinTestFramework):
def run_test(self):
peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
self.test_dersig_info(is_active=False)
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
- self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)]
- self.nodeaddress = self.nodes[0].getnewaddress()
+ self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.miniwallet.generate(DERSIG_HEIGHT - 2)]
self.log.info("Test that a transaction with non-DER signature can still appear in a block")
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0],
- self.nodeaddress, amount=1.0)
+ spendtx = self.create_tx(self.coinbase_txids[0])
unDERify(spendtx)
spendtx.rehash()
@@ -104,8 +111,7 @@ class BIP66Test(BitcoinTestFramework):
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1],
- self.nodeaddress, amount=1.0)
+ spendtx = self.create_tx(self.coinbase_txids[1])
unDERify(spendtx)
spendtx.rehash()
@@ -133,7 +139,7 @@ class BIP66Test(BitcoinTestFramework):
peer.sync_with_ping()
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
- block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
+ block.vtx[1] = self.create_tx(self.coinbase_txids[1])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py
index f22b7f266a..448182eded 100755
--- a/test/functional/feature_includeconf.py
+++ b/test/functional/feature_includeconf.py
@@ -42,7 +42,14 @@ class IncludeConfTest(BitcoinTestFramework):
self.log.info("-includeconf cannot be used as command-line arg")
self.stop_node(0)
- self.nodes[0].assert_start_raises_init_error(extra_args=["-includeconf=relative2.conf"], expected_msg="Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=relative2.conf")
+ self.nodes[0].assert_start_raises_init_error(
+ extra_args=['-noincludeconf=0'],
+ expected_msg='Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf=true',
+ )
+ self.nodes[0].assert_start_raises_init_error(
+ extra_args=['-includeconf=relative2.conf', '-includeconf=no_warn.conf'],
+ expected_msg='Error: Error parsing command line arguments: -includeconf cannot be used from commandline; -includeconf="relative2.conf"',
+ )
self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'")
with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f:
diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py
index 0a457ca17f..14f64d63a2 100755
--- a/test/functional/feature_loadblock.py
+++ b/test/functional/feature_loadblock.py
@@ -16,6 +16,7 @@ import sys
import tempfile
import urllib
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -28,7 +29,7 @@ class LoadblockTest(BitcoinTestFramework):
def run_test(self):
self.nodes[1].setnetworkactive(state=False)
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
# Parsing the url of our node to get settings for config file
data_dir = self.nodes[0].datadir
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 4e2de1daf4..6fc8773ee3 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -166,6 +166,8 @@ class NotificationsTest(BitcoinTestFramework):
# 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))
+ # Wait for the cached writes to hit storage
+ self.wait_until(lambda: os.path.getsize(fname) > 0, timeout=10)
with open(fname, 'rt', encoding='utf-8') as f:
text = f.read()
# Universal newline ensures '\n' on 'nt'
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index c7981d31dc..f467626801 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -14,15 +14,21 @@ Generate COINBASE_MATURITY (CB) more blocks to ensure the coinbases are mature.
"""
import time
-from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS, create_block, create_transaction, add_witness_commitment
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ NORMAL_GBT_REQUEST_PARAMS,
+ add_witness_commitment,
+ create_block,
+ create_transaction,
+)
from test_framework.messages import CTransaction
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
-COINBASE_MATURITY = 100
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+
def trueDummy(tx):
scriptSig = CScript(tx.vin[0].scriptSig)
newscript = []
@@ -35,18 +41,17 @@ def trueDummy(tx):
tx.vin[0].scriptSig = CScript(newscript)
tx.rehash()
-class NULLDUMMYTest(BitcoinTestFramework):
+class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
- # Need two nodes so GBT (getblocktemplate) doesn't complain that it's not connected.
- self.num_nodes = 2
+ self.num_nodes = 1
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
self.extra_args = [[
f'-segwitheight={COINBASE_MATURITY + 5}',
'-addresstype=legacy',
- ]] * 2
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 8bee43b8ad..162814815e 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -147,13 +147,13 @@ class ProxyTest(BitcoinTestFramework):
self.network_test(node, addr, network=NET_IPV6)
if test_onion:
- addr = "bitcoinostk4e4re.onion:8333"
+ addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
self.log.debug("Test: outgoing onion connection through node for address {}".format(addr))
node.addnode(addr, "onetry")
cmd = proxies[2].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
- assert_equal(cmd.addr, b"bitcoinostk4e4re.onion")
+ assert_equal(cmd.addr, b"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")
assert_equal(cmd.port, 8333)
if not auth:
assert_equal(cmd.username, None)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 945880cc3b..344db5f652 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -6,6 +6,7 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
@@ -27,7 +28,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""
fee = 1*COIN
while node.getbalance() < satoshi_round((amount + fee)/COIN):
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
new_addr = node.getnewaddress()
txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 183a43abd4..fc04853199 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -5,6 +5,7 @@
# Test Taproot softfork (BIPs 340-342)
from test_framework.blocktools import (
+ COINBASE_MATURITY,
create_coinbase,
create_block,
add_witness_commitment,
@@ -1440,7 +1441,7 @@ class TaprootTest(BitcoinTestFramework):
def run_test(self):
# Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
self.log.info("Post-activation tests...")
- self.nodes[1].generate(101)
+ self.nodes[1].generate(COINBASE_MATURITY + 1)
self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
# Re-connect nodes in case they have been disconnected
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 2cf0ef2251..30cd499b3f 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -5,6 +5,8 @@
"""Test bitcoin-cli"""
from decimal import Decimal
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -16,7 +18,7 @@ from test_framework.util import (
# The block reward of coinbaseoutput.nValue (50) BTC/block matures after
# COINBASE_MATURITY (100) blocks. Therefore, after mining 101 blocks we expect
# node 0 to have a balance of (BLOCKS - COINBASE_MATURITY) * 50 BTC/block.
-BLOCKS = 101
+BLOCKS = COINBASE_MATURITY + 1
BALANCE = (BLOCKS - 100) * 50
JSON_PARSING_ERROR = 'error: Error parsing JSON: foo'
@@ -151,7 +153,7 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(cli_get_info['balance'], amounts[1])
self.log.info("Test -getinfo with -rpcwallet=unloaded wallet returns no balances")
- cli_get_info = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli()
+ cli_get_info_keys = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli().keys()
assert 'balance' not in cli_get_info_keys
assert 'balances' not in cli_get_info_keys
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index c4002f524a..12aac3ab65 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -67,7 +67,8 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('Should not accept garbage to testmempoolaccept')
assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
- assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22']))
+ assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26))
+ assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[]))
assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))
self.log.info('A transaction already in the blockchain')
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index eb08765ebf..6de6778909 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -15,6 +15,7 @@ Only v0.15.2 is required by this test. The rest is used in other backwards compa
import os
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.wallet import MiniWallet
@@ -41,7 +42,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
old_node, new_node = self.nodes
new_wallet = MiniWallet(new_node)
new_wallet.generate(1)
- new_node.generate(100)
+ new_node.generate(COINBASE_MATURITY)
# Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend.
# Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced
# unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`.
diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py
index 4c46075ae9..7d1bfef333 100755
--- a/test/functional/mempool_expiry.py
+++ b/test/functional/mempool_expiry.py
@@ -12,6 +12,7 @@ definable expiry timeout via the '-mempoolexpiry=<n>' command line argument
from datetime import timedelta
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -36,7 +37,7 @@ class MempoolExpiryTest(BitcoinTestFramework):
# Add enough mature utxos to the wallet so that all txs spend confirmed coins.
self.wallet.generate(4)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
# Send a parent transaction that will expire.
parent_txid = self.wallet.send_self_transfer(from_node=node)['txid']
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 884a2fef11..1e9895e621 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -9,6 +9,7 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
@@ -42,7 +43,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
vout = utxo[0]['vout']
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 461f9237ff..606717d890 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -6,6 +6,7 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import COIN
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
@@ -59,7 +60,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
vout = utxo[0]['vout']
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 8e1f87e42c..bcc6aa7bcc 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -8,10 +8,9 @@ Test re-org scenarios with a mempool that contains transactions
that spend (directly or indirectly) coinbase transactions.
"""
-from test_framework.blocktools import create_raw_transaction
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
-
+from test_framework.wallet import MiniWallet
class MempoolCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
@@ -23,86 +22,90 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
[]
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ wallet = MiniWallet(self.nodes[0])
+
# Start with a 200 block chain
assert_equal(self.nodes[0].getblockcount(), 200)
- # Mine four blocks. After this, nodes[0] blocks
- # 101, 102, and 103 are spend-able.
- new_blocks = self.nodes[1].generate(4)
- self.sync_all()
-
- node0_address = self.nodes[0].getnewaddress()
- node1_address = self.nodes[1].getnewaddress()
+ self.log.info("Add 4 coinbase utxos to the miniwallet")
+ # Block 76 contains the first spendable coinbase txs.
+ first_block = 76
+ wallet.scan_blocks(start=first_block, num=4)
# Three scenarios for re-orging coinbase spends in the memory pool:
- # 1. Direct coinbase spend : spend_101
- # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1
- # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1
- # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase),
+ # 1. Direct coinbase spend : spend_1
+ # 2. Indirect (coinbase spend in chain, child in mempool) : spend_2 and spend_2_1
+ # 3. Indirect (coinbase and child both in chain) : spend_3 and spend_3_1
+ # Use invalidateblock to make all of the above coinbase spends invalid (immature coinbase),
# and make sure the mempool code behaves correctly.
- b = [self.nodes[0].getblockhash(n) for n in range(101, 105)]
+ b = [self.nodes[0].getblockhash(n) for n in range(first_block, first_block+4)]
coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b]
- spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[1], node1_address, amount=49.99)
- spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[2], node0_address, amount=49.99)
- spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[3], node0_address, amount=49.99)
-
- # Create a transaction which is time-locked to two blocks in the future
- timelock_tx = self.nodes[0].createrawtransaction(
- inputs=[{
- "txid": coinbase_txids[0],
- "vout": 0,
- }],
- outputs={node0_address: 49.99},
- locktime=self.nodes[0].getblockcount() + 2,
- )
- timelock_tx = self.nodes[0].signrawtransactionwithwallet(timelock_tx)["hex"]
- # This will raise an exception because the timelock transaction is too immature to spend
+ utxo_1 = wallet.get_utxo(txid=coinbase_txids[1])
+ utxo_2 = wallet.get_utxo(txid=coinbase_txids[2])
+ utxo_3 = wallet.get_utxo(txid=coinbase_txids[3])
+ self.log.info("Create three transactions spending from coinbase utxos: spend_1, spend_2, spend_3")
+ spend_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_1)
+ spend_2 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_2)
+ spend_3 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_3)
+
+ self.log.info("Create another transaction which is time-locked to two blocks in the future")
+ utxo = wallet.get_utxo(txid=coinbase_txids[0])
+ timelock_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=utxo,
+ mempool_valid=False,
+ locktime=self.nodes[0].getblockcount() + 2
+ )['hex']
+
+ self.log.info("Check that the time-locked transaction is too immature to spend")
assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
- # Broadcast and mine spend_102 and 103:
- spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw)
- spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw)
+ self.log.info("Broadcast and mine spend_2 and spend_3")
+ wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_2['hex'])
+ wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_3['hex'])
+ self.log.info("Generate a block")
self.nodes[0].generate(1)
- # Time-locked transaction is still too immature to spend
+ self.log.info("Check that time-locked transaction is still too immature to spend")
assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
- # Create 102_1 and 103_1:
- spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, node1_address, amount=49.98)
- spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, node1_address, amount=49.98)
+ self.log.info("Create spend_2_1 and spend_3_1")
+ spend_2_utxo = wallet.get_utxo(txid=spend_2['txid'])
+ spend_2_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_2_utxo)
+ spend_3_utxo = wallet.get_utxo(txid=spend_3['txid'])
+ spend_3_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_3_utxo)
- # Broadcast and mine 103_1:
- spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw)
+ self.log.info("Broadcast and mine spend_3_1")
+ spend_3_1_id = self.nodes[0].sendrawtransaction(spend_3_1['hex'])
+ self.log.info("Generate a block")
last_block = self.nodes[0].generate(1)
# Sync blocks, so that peer 1 gets the block before timelock_tx
# Otherwise, peer 1 would put the timelock_tx in recentRejects
self.sync_all()
- # Time-locked transaction can now be spent
+ self.log.info("The time-locked transaction can now be spent")
timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx)
- # ... now put spend_101 and spend_102_1 in memory pools:
- spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw)
- spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw)
+ self.log.info("Add spend_1 and spend_2_1 to the mempool")
+ spend_1_id = self.nodes[0].sendrawtransaction(spend_1['hex'])
+ spend_2_1_id = self.nodes[0].sendrawtransaction(spend_2_1['hex'])
- assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id})
+ assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, timelock_tx_id})
self.sync_all()
+ self.log.info("invalidate the last block")
for node in self.nodes:
node.invalidateblock(last_block[0])
- # Time-locked transaction is now too immature and has been removed from the mempool
- # spend_103_1 has been re-orged out of the chain and is back in the mempool
- assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id})
+ self.log.info("The time-locked transaction is now too immature and has been removed from the mempool")
+ self.log.info("spend_3_1 has been re-orged out of the chain and is back in the mempool")
+ assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, spend_3_1_id})
- # Use invalidateblock to re-org back and make all those coinbase spends
- # immature/invalid:
+ self.log.info("Use invalidateblock to re-org back and make all those coinbase spends immature/invalid")
+ b = self.nodes[0].getblockhash(first_block + 100)
for node in self.nodes:
- node.invalidateblock(new_blocks[0])
+ node.invalidateblock(b)
- # mempool should be empty.
+ self.log.info("Check that the mempool is empty")
assert_equal(set(self.nodes[0].getrawmempool()), set())
self.sync_all()
diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py
index 4aa58270b6..1b5ca7e15a 100755
--- a/test/functional/mempool_resurrect.py
+++ b/test/functional/mempool_resurrect.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test resurrection of mined transactions when the blockchain is re-organized."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.wallet import MiniWallet
@@ -20,7 +21,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
# Add enough mature utxos to the wallet so that all txs spend confirmed coins
wallet.generate(3)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
# Spend block 1/2/3's coinbase transactions
# Mine a block
diff --git a/test/functional/mempool_spend_coinbase.py b/test/functional/mempool_spend_coinbase.py
index a249a73315..b900aa0b9c 100755
--- a/test/functional/mempool_spend_coinbase.py
+++ b/test/functional/mempool_spend_coinbase.py
@@ -20,40 +20,41 @@ from test_framework.wallet import MiniWallet
class MempoolSpendCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.setup_clean_chain = True
def run_test(self):
wallet = MiniWallet(self.nodes[0])
- wallet.generate(200)
- chain_height = self.nodes[0].getblockcount()
- assert_equal(chain_height, 200)
+ # Invalidate two blocks, so that miniwallet has access to a coin that will mature in the next block
+ chain_height = 198
+ self.nodes[0].invalidateblock(self.nodes[0].getblockhash(chain_height + 1))
+ assert_equal(chain_height, self.nodes[0].getblockcount())
# Coinbase at height chain_height-100+1 ok in mempool, should
# get mined. Coinbase at height chain_height-100+2 is
# too immature to spend.
- b = [self.nodes[0].getblockhash(n) for n in range(101, 103)]
- coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b]
- utxo_101 = wallet.get_utxo(txid=coinbase_txids[0])
- utxo_102 = wallet.get_utxo(txid=coinbase_txids[1])
+ wallet.scan_blocks(start=chain_height - 100 + 1, num=1)
+ utxo_mature = wallet.get_utxo()
+ wallet.scan_blocks(start=chain_height - 100 + 2, num=1)
+ utxo_immature = wallet.get_utxo()
- spend_101_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_101)["txid"]
+ spend_mature_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_mature)["txid"]
- # coinbase at height 102 should be too immature to spend
+ # other coinbase should be too immature to spend
+ immature_tx = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_immature, mempool_valid=False)
assert_raises_rpc_error(-26,
"bad-txns-premature-spend-of-coinbase",
- lambda: wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_102))
+ lambda: self.nodes[0].sendrawtransaction(immature_tx['hex']))
- # mempool should have just spend_101:
- assert_equal(self.nodes[0].getrawmempool(), [spend_101_id])
+ # mempool should have just the mature one
+ assert_equal(self.nodes[0].getrawmempool(), [spend_mature_id])
- # mine a block, spend_101 should get confirmed
+ # mine a block, mature one should get confirmed
self.nodes[0].generate(1)
assert_equal(set(self.nodes[0].getrawmempool()), set())
- # ... and now height 102 can be spent:
- spend_102_id = wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_102)["txid"]
- assert_equal(self.nodes[0].getrawmempool(), [spend_102_id])
+ # ... and now previously immature can be spent:
+ spend_new_id = self.nodes[0].sendrawtransaction(immature_tx['hex'])
+ assert_equal(self.nodes[0].getrawmempool(), [spend_new_id])
if __name__ == '__main__':
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index cc32f78e2e..715b68e04c 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -8,6 +8,7 @@ from decimal import Decimal
import random
import threading
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import get_rpc_proxy
from test_framework.wallet import MiniWallet
@@ -62,7 +63,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
assert not thr.is_alive()
# Add enough mature utxos to the wallets, so that all txs spend confirmed coins
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
self.sync_blocks()
self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll")
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 69821763bd..87297989ba 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -11,6 +11,7 @@ from test_framework.messages import (
NODE_NETWORK,
NODE_WITNESS,
msg_addr,
+ msg_getaddr
)
from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
@@ -19,18 +20,6 @@ from test_framework.util import (
)
import time
-# Keep this with length <= 10. Addresses from larger messages are not relayed.
-ADDRS = []
-num_ipv4_addrs = 10
-
-for i in range(num_ipv4_addrs):
- addr = CAddress()
- addr.time = int(time.time()) + i
- addr.nServices = NODE_NETWORK | NODE_WITNESS
- addr.ip = "123.123.123.{}".format(i % 256)
- addr.port = 8333 + i
- ADDRS.append(addr)
-
class AddrReceiver(P2PInterface):
num_ipv4_received = 0
@@ -44,36 +33,87 @@ class AddrReceiver(P2PInterface):
self.num_ipv4_received += 1
+class GetAddrStore(P2PInterface):
+ getaddr_received = False
+ num_ipv4_received = 0
+
+ def on_getaddr(self, message):
+ self.getaddr_received = True
+
+ def on_addr(self, message):
+ for addr in message.addrs:
+ self.num_ipv4_received += 1
+
+ def addr_received(self):
+ return self.num_ipv4_received != 0
+
+
class AddrTest(BitcoinTestFramework):
+ counter = 0
+ mocktime = int(time.time())
+
def set_test_params(self):
self.num_nodes = 1
def run_test(self):
- self.log.info('Create connection that sends addr messages')
- addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+ self.oversized_addr_test()
+ self.relay_tests()
+ self.getaddr_tests()
+ self.blocksonly_mode_tests()
+
+ def setup_addr_msg(self, num):
+ addrs = []
+ for i in range(num):
+ addr = CAddress()
+ addr.time = self.mocktime + i
+ addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.ip = f"123.123.123.{self.counter % 256}"
+ addr.port = 8333 + i
+ addrs.append(addr)
+ self.counter += 1
+
msg = msg_addr()
+ msg.addrs = addrs
+ return msg
+
+ def send_addr_msg(self, source, msg, receivers):
+ source.send_and_ping(msg)
+ # pop m_next_addr_send timer
+ self.mocktime += 5 * 60
+ self.nodes[0].setmocktime(self.mocktime)
+ for peer in receivers:
+ peer.sync_send_with_ping()
- self.log.info('Send too-large addr message')
- msg.addrs = ADDRS * 101 # more than 1000 addresses in one message
+ def oversized_addr_test(self):
+ self.log.info('Send an addr message that is too large')
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+
+ msg = self.setup_addr_msg(1010)
with self.nodes[0].assert_debug_log(['addr message size = 1010']):
addr_source.send_and_ping(msg)
+ self.nodes[0].disconnect_p2ps()
+
+ def relay_tests(self):
+ self.log.info('Test address relay')
self.log.info('Check that addr message content is relayed and added to addrman')
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
num_receivers = 7
receivers = []
for _ in range(num_receivers):
receivers.append(self.nodes[0].add_p2p_connection(AddrReceiver()))
- msg.addrs = ADDRS
+
+ # Keep this with length <= 10. Addresses from larger messages are not
+ # relayed.
+ num_ipv4_addrs = 10
+ msg = self.setup_addr_msg(num_ipv4_addrs)
with self.nodes[0].assert_debug_log(
[
'Added {} addresses from 127.0.0.1: 0 tried'.format(num_ipv4_addrs),
- 'received: addr (301 bytes) peer=0',
+ 'received: addr (301 bytes) peer=1',
]
):
- addr_source.send_and_ping(msg)
- self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
- for receiver in receivers:
- receiver.sync_with_ping()
+ self.send_addr_msg(addr_source, msg, receivers)
total_ipv4_received = sum(r.num_ipv4_received for r in receivers)
@@ -82,6 +122,92 @@ class AddrTest(BitcoinTestFramework):
ipv4_branching_factor = 2
assert_equal(total_ipv4_received, num_ipv4_addrs * ipv4_branching_factor)
+ self.nodes[0].disconnect_p2ps()
+
+ self.log.info('Check relay of addresses received from outbound peers')
+ inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ msg = self.setup_addr_msg(2)
+ self.send_addr_msg(full_outbound_peer, msg, [inbound_peer])
+ self.log.info('Check that the first addr message received from an outbound peer is not relayed')
+ # Currently, there is a flag that prevents the first addr message received
+ # from a new outbound peer to be relayed to others. Originally meant to prevent
+ # large GETADDR responses from being relayed, it now typically affects the self-announcement
+ # of the outbound peer which is often sent before the GETADDR response.
+ assert_equal(inbound_peer.num_ipv4_received, 0)
+
+ self.log.info('Check that subsequent addr messages sent from an outbound peer are relayed')
+ msg2 = self.setup_addr_msg(2)
+ self.send_addr_msg(full_outbound_peer, msg2, [inbound_peer])
+ assert_equal(inbound_peer.num_ipv4_received, 2)
+
+ self.log.info('Check address relay to outbound peers')
+ block_relay_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=1, connection_type="block-relay-only")
+ msg3 = self.setup_addr_msg(2)
+ self.send_addr_msg(inbound_peer, msg3, [full_outbound_peer, block_relay_peer])
+
+ self.log.info('Check that addresses are relayed to full outbound peers')
+ assert_equal(full_outbound_peer.num_ipv4_received, 2)
+ self.log.info('Check that addresses are not relayed to block-relay-only outbound peers')
+ assert_equal(block_relay_peer.num_ipv4_received, 0)
+
+ self.nodes[0].disconnect_p2ps()
+
+ def getaddr_tests(self):
+ self.log.info('Test getaddr behavior')
+ self.log.info('Check that we send a getaddr message upon connecting to an outbound-full-relay peer')
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ full_outbound_peer.sync_with_ping()
+ assert full_outbound_peer.getaddr_received
+
+ self.log.info('Check that we do not send a getaddr message upon connecting to a block-relay-only peer')
+ block_relay_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=1, connection_type="block-relay-only")
+ block_relay_peer.sync_with_ping()
+ assert_equal(block_relay_peer.getaddr_received, False)
+
+ self.log.info('Check that we answer getaddr messages only from inbound peers')
+ inbound_peer = self.nodes[0].add_p2p_connection(GetAddrStore())
+ inbound_peer.sync_with_ping()
+
+ # Add some addresses to addrman
+ for i in range(1000):
+ first_octet = i >> 8
+ second_octet = i % 256
+ a = f"{first_octet}.{second_octet}.1.1"
+ self.nodes[0].addpeeraddress(a, 8333)
+
+ full_outbound_peer.send_and_ping(msg_getaddr())
+ block_relay_peer.send_and_ping(msg_getaddr())
+ inbound_peer.send_and_ping(msg_getaddr())
+
+ self.mocktime += 5 * 60
+ self.nodes[0].setmocktime(self.mocktime)
+ inbound_peer.wait_until(inbound_peer.addr_received)
+
+ assert_equal(full_outbound_peer.num_ipv4_received, 0)
+ assert_equal(block_relay_peer.num_ipv4_received, 0)
+ assert inbound_peer.num_ipv4_received > 100
+
+ self.nodes[0].disconnect_p2ps()
+
+ def blocksonly_mode_tests(self):
+ self.log.info('Test addr relay in -blocksonly mode')
+ self.restart_node(0, ["-blocksonly"])
+ self.mocktime = int(time.time())
+
+ self.log.info('Check that we send getaddr messages')
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ full_outbound_peer.sync_with_ping()
+ assert full_outbound_peer.getaddr_received
+
+ self.log.info('Check that we relay address messages')
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+ msg = self.setup_addr_msg(2)
+ self.send_addr_msg(addr_source, msg, [full_outbound_peer])
+ assert_equal(full_outbound_peer.num_ipv4_received, 2)
+
+ self.nodes[0].disconnect_p2ps()
+
if __name__ == '__main__':
AddrTest().main()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 6584efae79..6409d4ea82 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -6,22 +6,26 @@
import time
-from test_framework.blocktools import create_transaction
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import msg_tx
from test_framework.p2p import P2PInterface, P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+from test_framework.wallet import MiniWallet
class P2PBlocksOnly(BitcoinTestFramework):
def set_test_params(self):
+ self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [["-blocksonly"]]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ self.miniwallet = MiniWallet(self.nodes[0])
+ # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
+ self.miniwallet.generate(2)
+ self.nodes[0].generate(COINBASE_MATURITY)
+
self.blocksonly_mode_tests()
self.blocks_relay_conn_tests()
@@ -30,14 +34,14 @@ class P2PBlocksOnly(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
self.nodes[0].add_p2p_connection(P2PInterface())
- tx, txid, tx_hex = self.check_p2p_tx_violation()
+ tx, txid, wtxid, tx_hex = self.check_p2p_tx_violation()
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True)
- with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]):
+ with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(wtxid)]):
self.nodes[0].sendrawtransaction(tx_hex)
tx_relay_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
@@ -79,7 +83,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
# Ensure we disconnect if a block-relay-only connection sends us a transaction
self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only")
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False)
- _, txid, tx_hex = self.check_p2p_tx_violation(index=2)
+ _, txid, _, tx_hex = self.check_p2p_tx_violation(index=2)
self.log.info("Check that txs from RPC are not sent to blockrelay connection")
conn = self.nodes[0].add_outbound_p2p_connection(P2PTxInvStore(), p2p_idx=1, connection_type="block-relay-only")
@@ -89,29 +93,24 @@ class P2PBlocksOnly(BitcoinTestFramework):
# Bump time forward to ensure nNextInvSend timer pops
self.nodes[0].setmocktime(int(time.time()) + 60)
- # Calling sync_with_ping twice requires that the node calls
- # `ProcessMessage` twice, and thus ensures `SendMessages` must have
- # been called at least once
- conn.sync_with_ping()
- conn.sync_with_ping()
+ conn.sync_send_with_ping()
assert(int(txid, 16) not in conn.get_invs())
def check_p2p_tx_violation(self, index=1):
self.log.info('Check that txs from P2P are rejected and result in disconnect')
input_txid = self.nodes[0].getblock(self.nodes[0].getblockhash(index), 2)['tx'][0]['txid']
- tx = create_transaction(self.nodes[0], input_txid, self.nodes[0].getnewaddress(), amount=(50 - 0.001))
- txid = tx.rehash()
- tx_hex = tx.serialize().hex()
+ utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid)
+ spendtx = self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
- self.nodes[0].p2ps[0].send_message(msg_tx(tx))
+ self.nodes[0].p2ps[0].send_message(msg_tx(spendtx['tx']))
self.nodes[0].p2ps[0].wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
# Remove the disconnected peer
del self.nodes[0].p2ps[0]
- return tx, txid, tx_hex
+ return spendtx['tx'], spendtx['txid'], spendtx['wtxid'], spendtx['hex']
if __name__ == '__main__':
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 55573efc06..3e4f2f974d 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -9,7 +9,12 @@ Version 2 compact blocks are post-segwit (wtxids)
"""
import random
-from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS, add_witness_commitment
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ NORMAL_GBT_REQUEST_PARAMS,
+ add_witness_commitment,
+ create_block,
+)
from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_no_witness_block, msg_no_witness_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_block, msg_blocktxn, MSG_BLOCK, MSG_CMPCT_BLOCK, MSG_WITNESS_FLAG, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
from test_framework.p2p import p2p_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP
@@ -115,7 +120,7 @@ class CompactBlocksTest(BitcoinTestFramework):
block = self.build_block_on_tip(self.nodes[0])
self.segwit_node.send_and_ping(msg_no_witness_block(block))
assert int(self.nodes[0].getbestblockhash(), 16) == block.sha256
- self.nodes[0].generatetoaddress(100, self.nodes[0].getnewaddress(address_type="bech32"))
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY, self.nodes[0].getnewaddress(address_type="bech32"))
total_value = block.vtx[0].vout[0].nValue
out_value = total_value // 10
@@ -226,7 +231,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self):
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
block = self.build_block_on_tip(self.nodes[0])
cmpct_block = P2PHeaderAndShortIDs()
@@ -244,7 +249,7 @@ class CompactBlocksTest(BitcoinTestFramework):
version = test_node.cmpct_version
node = self.nodes[0]
# Generate a bunch of transactions.
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
num_transactions = 25
address = node.getnewaddress()
diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py
index d60aa5b383..a525996493 100755
--- a/test/functional/p2p_eviction.py
+++ b/test/functional/p2p_eviction.py
@@ -15,7 +15,11 @@ Therefore, this test is limited to the remaining protection criteria.
import time
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ create_block,
+ create_coinbase,
+)
from test_framework.messages import CTransaction, FromHex, msg_pong, msg_tx
from test_framework.p2p import P2PDataStore, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
@@ -45,7 +49,7 @@ class P2PEvict(BitcoinTestFramework):
protected_peers = set() # peers that we expect to be protected from eviction
current_peer = -1
node = self.nodes[0]
- node.generatetoaddress(101, node.get_deterministic_priv_key().address)
+ node.generatetoaddress(COINBASE_MATURITY + 1, node.get_deterministic_priv_key().address)
self.log.info("Create 4 peers and protect them from eviction by sending us a block")
for _ in range(4):
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 52dc4de3bd..0175b9f6c0 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -6,6 +6,7 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
@@ -81,7 +82,7 @@ class FeeFilterTest(BitcoinTestFramework):
miniwallet = MiniWallet(node1)
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(5)
- node1.generate(100)
+ node1.generate(COINBASE_MATURITY)
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 4bee33f825..359cfb9c34 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -174,8 +174,7 @@ class FilterTest(BitcoinTestFramework):
filter_peer.merkleblock_received = False
filter_peer.tx_received = False
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90)
- filter_peer.sync_with_ping()
- filter_peer.sync_with_ping()
+ filter_peer.sync_send_with_ping()
assert not filter_peer.merkleblock_received
assert not filter_peer.tx_received
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index c0b3c2cb12..788a81d4af 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -37,7 +37,7 @@ VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte len
class msg_unrecognized:
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
- msgtype = b'badmsg'
+ msgtype = b'badmsg\x01'
def __init__(self, *, str_data):
self.str_data = str_data.encode() if not isinstance(str_data, bytes) else str_data
@@ -104,7 +104,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_magic_bytes(self):
self.log.info("Test message with invalid magic bytes disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['HEADER ERROR - MESSAGESTART (badmsg, 2 bytes), received ffffffff']):
+ with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']):
msg = conn.build_message(msg_unrecognized(str_data="d"))
# modify magic bytes
msg = b'\xff' * 4 + msg[4:]
@@ -115,7 +115,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_checksum(self):
self.log.info("Test message with invalid checksum logs an error")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['CHECKSUM ERROR (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
+ with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']):
msg = conn.build_message(msg_unrecognized(str_data="d"))
# Checksum is after start bytes (4B), message type (12B), len (4B)
cut_len = 4 + 12 + 4
@@ -130,7 +130,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_size(self):
self.log.info("Test message with oversized payload disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE (badmsg, 4000001 bytes)']):
+ with self.nodes[0].assert_debug_log(['Header error: Size too large (badmsg, 4000001 bytes)']):
msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
msg = conn.build_message(msg)
conn.send_raw_message(msg)
@@ -140,7 +140,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_msgtype(self):
self.log.info("Test message with invalid message type logs an error")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['HEADER ERROR - COMMAND']):
+ with self.nodes[0].assert_debug_log(['Header error: Invalid message type']):
msg = msg_unrecognized(str_data="d")
msg = conn.build_message(msg)
# Modify msgtype
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 71d5ca92b3..f1538e8ac7 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -29,6 +29,8 @@ from test_framework.util import (
assert_greater_than_or_equal,
)
+PEER_TIMEOUT = 3
+
class LazyPeer(P2PInterface):
def __init__(self):
@@ -98,7 +100,7 @@ class P2PVersionStore(P2PInterface):
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-peertimeout=4']]
+ self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]
def create_old_version(self, nversion):
old_version_msg = msg_version()
@@ -134,7 +136,7 @@ class P2PLeakTest(BitcoinTestFramework):
self.nodes[0].generate(nblocks=1)
# Give the node enough time to possibly leak out a message
- time.sleep(5)
+ time.sleep(PEER_TIMEOUT + 2)
# Make sure only expected messages came in
assert not no_version_idle_peer.unexpected_msg
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
index a45f792e81..9a4ceb86ae 100755
--- a/test/functional/p2p_leak_tx.py
+++ b/test/functional/p2p_leak_tx.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test that we don't leak txs to inbound peers that we haven't yet announced to"""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import msg_getdata, CInv, MSG_TX
from test_framework.p2p import p2p_lock, P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
@@ -27,7 +28,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
miniwallet = MiniWallet(gen_node)
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(1)
- gen_node.generate(100)
+ gen_node.generate(COINBASE_MATURITY)
inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 54891b07e1..9d32c1cb86 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -164,7 +164,7 @@ class TestP2PConn(P2PInterface):
def on_wtxidrelay(self, message):
self.last_wtxidrelay.append(message)
- def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True, use_wtxid=False):
+ def announce_tx_and_wait_for_getdata(self, tx, success=True, use_wtxid=False):
if success:
# sanity check
assert (self.wtxidrelay and use_wtxid) or (not self.wtxidrelay and not use_wtxid)
@@ -178,11 +178,11 @@ class TestP2PConn(P2PInterface):
if success:
if use_wtxid:
- self.wait_for_getdata([wtxid], timeout)
+ self.wait_for_getdata([wtxid])
else:
- self.wait_for_getdata([tx.sha256], timeout)
+ self.wait_for_getdata([tx.sha256])
else:
- time.sleep(timeout)
+ time.sleep(5)
assert not self.last_message.get("getdata")
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
@@ -604,7 +604,7 @@ class SegWitTest(BitcoinTestFramework):
# Since we haven't delivered the tx yet, inv'ing the same tx from
# a witness transaction ought not result in a getdata.
- self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False)
+ self.test_node.announce_tx_and_wait_for_getdata(tx, success=False)
# Delivering this transaction with witness should fail (no matter who
# its from)
@@ -1461,7 +1461,7 @@ class SegWitTest(BitcoinTestFramework):
self.std_node.announce_tx_and_wait_for_getdata(tx3)
test_transaction_acceptance(self.nodes[1], self.std_node, tx3, with_witness=True, accepted=False, reason="bad-txns-nonstandard-inputs")
# Now the node will no longer ask for getdata of this transaction when advertised by same txid
- self.std_node.announce_tx_and_wait_for_getdata(tx3, timeout=5, success=False)
+ self.std_node.announce_tx_and_wait_for_getdata(tx3, success=False)
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
@@ -1956,22 +1956,34 @@ class SegWitTest(BitcoinTestFramework):
def test_upgrade_after_activation(self):
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
- self.restart_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)])
+ # All nodes are caught up and node 2 is a pre-segwit node that will soon upgrade.
+ for n in range(2):
+ assert_equal(self.nodes[n].getblockcount(), self.nodes[2].getblockcount())
+ assert softfork_active(self.nodes[n], "segwit")
+ assert SEGWIT_HEIGHT < self.nodes[2].getblockcount()
+ assert 'segwit' not in self.nodes[2].getblockchaininfo()['softforks']
+
+ # Restarting node 2 should result in a shutdown because the blockchain consists of
+ # insufficiently validated blocks per segwit consensus rules.
+ self.stop_node(2)
+ self.nodes[2].assert_start_raises_init_error(
+ extra_args=[f"-segwitheight={SEGWIT_HEIGHT}"],
+ expected_msg=f": Witness data for blocks after height {SEGWIT_HEIGHT} requires validation. Please restart with -reindex..\nPlease restart with -reindex or -reindex-chainstate to recover.",
+ )
+
+ # As directed, the user restarts the node with -reindex
+ self.start_node(2, extra_args=["-reindex", f"-segwitheight={SEGWIT_HEIGHT}"])
+
+ # With the segwit consensus rules, the node is able to validate only up to SEGWIT_HEIGHT - 1
+ assert_equal(self.nodes[2].getblockcount(), SEGWIT_HEIGHT - 1)
self.connect_nodes(0, 2)
# We reconnect more than 100 blocks, give it plenty of time
+ # sync_blocks() also verifies the best block hash is the same for all nodes
self.sync_blocks(timeout=240)
- # Make sure that this peer thinks segwit has activated.
- assert softfork_active(self.nodes[2], 'segwit')
-
- # Make sure this peer's blocks match those of node0.
- height = self.nodes[2].getblockcount()
- while height >= 0:
- block_hash = self.nodes[2].getblockhash(height)
- assert_equal(block_hash, self.nodes[0].getblockhash(height))
- assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash))
- height -= 1
+ # The upgraded node should now have segwit activated
+ assert softfork_active(self.nodes[2], "segwit")
@subtest # type: ignore
def test_witness_sigops(self):
diff --git a/test/functional/rpc_addresses_deprecation.py b/test/functional/rpc_addresses_deprecation.py
index bc0559f3b5..bc0559f3b5 100644..100755
--- a/test/functional/rpc_addresses_deprecation.py
+++ b/test/functional/rpc_addresses_deprecation.py
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index e090030205..00324347ed 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -149,6 +149,7 @@ class BlockchainTest(BitcoinTestFramework):
'count': 57,
'possible': True,
},
+ 'min_activation_height': 0,
},
'active': False
},
@@ -158,7 +159,8 @@ class BlockchainTest(BitcoinTestFramework):
'status': 'active',
'start_time': -1,
'timeout': 9223372036854775807,
- 'since': 0
+ 'since': 0,
+ 'min_activation_height': 0,
},
'height': 0,
'active': True
@@ -273,7 +275,7 @@ class BlockchainTest(BitcoinTestFramework):
assert 'muhash' in res6
assert(res['hash_serialized_2'] != res6['muhash'])
- # muhash should not be included in gettxoutset unless requested.
+ # muhash should not be returned unless requested.
for r in [res, res2, res3, res4, res5]:
assert 'muhash' not in r
@@ -408,6 +410,9 @@ class BlockchainTest(BitcoinTestFramework):
self.log.info("Test that getblock with verbosity 2 still works with pruned Undo data")
datadir = get_datadir_path(self.options.tmpdir, 0)
+ self.log.info("Test that getblock with invalid verbosity type returns proper error message")
+ assert_raises_rpc_error(-1, "JSON value is not an integer as expected", node.getblock, blockhash, "2")
+
def move_block_file(old, new):
old_path = os.path.join(datadir, self.chain, 'blocks', old)
new_path = os.path.join(datadir, self.chain, 'blocks', new)
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 19f0d5765a..af515f3a27 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -9,6 +9,7 @@ import itertools
import json
import os
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create, drop_origins
from test_framework.key import ECPubKey, ECKey
@@ -109,7 +110,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
def checkbalances(self):
node0, node1, node2 = self.nodes
- node0.generate(100)
+ node0.generate(COINBASE_MATURITY)
self.sync_all()
bal0 = node0.getbalance()
diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py
index e65787ce08..3efbdab013 100755
--- a/test/functional/rpc_dumptxoutset.py
+++ b/test/functional/rpc_dumptxoutset.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the generation of UTXO snapshots using `dumptxoutset`.
"""
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
@@ -21,7 +23,7 @@ class DumptxoutsetTest(BitcoinTestFramework):
node = self.nodes[0]
mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1
node.setmocktime(mocktime)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
FILENAME = 'txoutset.dat'
out = node.dumptxoutset(FILENAME)
@@ -41,7 +43,7 @@ class DumptxoutsetTest(BitcoinTestFramework):
digest = hashlib.sha256(f.read()).hexdigest()
# UTXO snapshot hash should be deterministic based on mocked time.
assert_equal(
- digest, 'be032e5f248264ba08e11099ac09dbd001f6f87ffc68bf0f87043d8146d50664')
+ digest, '7ae82c986fa5445678d2a21453bb1c86d39e47af13da137640c2b1cf8093691c')
# Specifying a path to an existing file will fail.
assert_raises_rpc_error(
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 5129ecb895..4b07a32c54 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -5,6 +5,8 @@
"""Test the fundrawtransaction RPC."""
from decimal import Decimal
+from itertools import product
+
from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -96,6 +98,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_option_subtract_fee_from_outputs()
self.test_subtract_fee_with_presets()
self.test_transaction_too_large()
+ self.test_include_unsafe()
def test_change_position(self):
"""Ensure setting changePosition in fundraw with an exact match is handled properly."""
@@ -723,17 +726,16 @@ class RawTransactionsTest(BitcoinTestFramework):
result2 = node.fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
result3 = node.fundrawtransaction(rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
result4 = node.fundrawtransaction(rawtx, {"feeRate": str(10 * self.min_relay_tx_fee)})
- # Test that funding non-standard "zero-fee" transactions is valid.
- result5 = self.nodes[3].fundrawtransaction(rawtx, {"fee_rate": 0})
- result6 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 0})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
- assert_fee_amount(result1['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
+ assert_fee_amount(result1['fee'], count_bytes(result1['hex']), 2 * result_fee_rate)
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
- assert_fee_amount(result4['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
- assert_fee_amount(result5['fee'], count_bytes(result5['hex']), 0)
- assert_fee_amount(result6['fee'], count_bytes(result6['hex']), 0)
+ assert_fee_amount(result4['fee'], count_bytes(result4['hex']), 10 * result_fee_rate)
+
+ # Test that funding non-standard "zero-fee" transactions is valid.
+ for param, zero_value in product(["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]):
+ assert_equal(self.nodes[3].fundrawtransaction(rawtx, {param: zero_value})["fee"], 0)
# With no arguments passed, expect fee of 141 satoshis.
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
@@ -767,11 +769,16 @@ class RawTransactionsTest(BitcoinTestFramework):
node.fundrawtransaction, rawtx, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
node.fundrawtransaction, rawtx, {param: {"foo": "bar"}, "add_inputs": True})
+ # Test fee rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ assert_raises_rpc_error(-3, "Invalid amount", node.fundrawtransaction, rawtx, {param: invalid_value, "add_inputs": True})
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
assert_raises_rpc_error(-3, "Invalid amount",
- node.fundrawtransaction, rawtx, {param: "", "add_inputs": True})
+ node.fundrawtransaction, rawtx, {"fee_rate": invalid_value, "add_inputs": True})
self.log.info("Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed")
- node.fundrawtransaction(rawtx, {"fee_rate": 0.99999999, "add_inputs": True})
+ node.fundrawtransaction(rawtx, {"fee_rate": 0.999, "add_inputs": True})
node.fundrawtransaction(rawtx, {"feeRate": 0.00000999, "add_inputs": True})
self.log.info("- raises RPC error if both feeRate and fee_rate are passed")
@@ -928,6 +935,40 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(10)
assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
+ def test_include_unsafe(self):
+ self.log.info("Test fundrawtxn with unsafe inputs")
+
+ self.nodes[0].createwallet("unsafe")
+ wallet = self.nodes[0].get_wallet_rpc("unsafe")
+
+ # We receive unconfirmed funds from external keys (unsafe outputs).
+ addr = wallet.getnewaddress()
+ txid1 = self.nodes[2].sendtoaddress(addr, 6)
+ txid2 = self.nodes[2].sendtoaddress(addr, 4)
+ self.sync_all()
+ vout1 = find_vout_for_address(wallet, txid1, addr)
+ vout2 = find_vout_for_address(wallet, txid2, addr)
+
+ # Unsafe inputs are ignored by default.
+ rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 5}])
+ assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, rawtx)
+
+ # But we can opt-in to use them for funding.
+ fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
+ tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
+ assert any([txin['txid'] == txid1 and txin['vout'] == vout1 for txin in tx_dec['vin']])
+ signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
+ wallet.sendrawtransaction(signedtx['hex'])
+
+ # And we can also use them once they're confirmed.
+ self.nodes[0].generate(1)
+ rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 3}])
+ fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
+ tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
+ assert any([txin['txid'] == txid2 and txin['vout'] == vout2 for txin in tx_dec['vin']])
+ signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
+ wallet.sendrawtransaction(signedtx['hex'])
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py
index 57794ae973..4af518c870 100755
--- a/test/functional/rpc_getblockstats.py
+++ b/test/functional/rpc_getblockstats.py
@@ -6,6 +6,8 @@
#
# Test getblockstats rpc call
#
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -41,7 +43,7 @@ class GetblockstatsTest(BitcoinTestFramework):
def generate_test_data(self, filename):
mocktime = 1525107225
self.nodes[0].setmocktime(mocktime)
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
address = self.nodes[0].get_deterministic_priv_key().address
self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index 1398d1237f..52c8fa883d 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -61,30 +61,30 @@ class RpcMiscTest(BitcoinTestFramework):
node.logging(include=['qt'])
assert_equal(node.logging()['qt'], True)
+ self.log.info("test echoipc (testing spawned process in multiprocess build)")
+ assert_equal(node.echoipc("hello"), "hello")
+
self.log.info("test getindexinfo")
# Without any indices running the RPC returns an empty object
assert_equal(node.getindexinfo(), {})
# Restart the node with indices and wait for them to sync
- self.restart_node(0, ["-txindex", "-blockfilterindex"])
+ self.restart_node(0, ["-txindex", "-blockfilterindex", "-coinstatsindex"])
self.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values()))
# Returns a list of all running indices by default
+ values = {"synced": True, "best_block_height": 200}
assert_equal(
node.getindexinfo(),
{
- "txindex": {"synced": True, "best_block_height": 200},
- "basic block filter index": {"synced": True, "best_block_height": 200}
+ "txindex": values,
+ "basic block filter index": values,
+ "coinstatsindex": values,
}
)
-
# Specifying an index by name returns only the status of that index
- assert_equal(
- node.getindexinfo("txindex"),
- {
- "txindex": {"synced": True, "best_block_height": 200},
- }
- )
+ for i in {"txindex", "basic block filter index", "coinstatsindex"}:
+ assert_equal(node.getindexinfo(i), {i: values})
# Specifying an unknown index name returns an empty result
assert_equal(node.getindexinfo("foo"), {})
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 9adb32c3c5..6e5ef770d1 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -11,6 +11,7 @@ from decimal import Decimal
from itertools import product
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.p2p import P2PInterface
import test_framework.messages
from test_framework.messages import (
@@ -53,7 +54,7 @@ class NetTest(BitcoinTestFramework):
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)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# By default, the test framework sets up an addnode connection from
# node 1 --> node0. By connecting node0 --> node 1, we're left with
@@ -69,6 +70,7 @@ class NetTest(BitcoinTestFramework):
self.test_getaddednodeinfo()
self.test_service_flags()
self.test_getnodeaddresses()
+ self.test_addpeeraddress()
def test_connection_count(self):
self.log.info("Test getconnectioncount")
@@ -187,42 +189,78 @@ class NetTest(BitcoinTestFramework):
def test_getnodeaddresses(self):
self.log.info("Test getnodeaddresses")
self.nodes[0].add_p2p_connection(P2PInterface())
+ services = NODE_NETWORK | NODE_WITNESS
- # Add some addresses to the Address Manager over RPC. Due to the way
- # bucket and bucket position are calculated, some of these addresses
- # will collide.
+ # Add an IPv6 address to the address manager.
+ ipv6_addr = "1233:3432:2434:2343:3234:2345:6546:4534"
+ self.nodes[0].addpeeraddress(address=ipv6_addr, port=8333)
+
+ # Add 10,000 IPv4 addresses to the address manager. Due to the way bucket
+ # and bucket positions are calculated, some of these addresses will collide.
imported_addrs = []
for i in range(10000):
first_octet = i >> 8
second_octet = i % 256
- a = "{}.{}.1.1".format(first_octet, second_octet)
+ a = f"{first_octet}.{second_octet}.1.1"
imported_addrs.append(a)
self.nodes[0].addpeeraddress(a, 8333)
- # Obtain addresses via rpc call and check they were ones sent in before.
- #
- # Maximum possible addresses in addrman is 10000, although actual
- # number will usually be less due to bucket and bucket position
- # collisions.
- node_addresses = self.nodes[0].getnodeaddresses(0)
+ # Fetch the addresses via the RPC and test the results.
+ assert_equal(len(self.nodes[0].getnodeaddresses()), 1) # default count is 1
+ assert_equal(len(self.nodes[0].getnodeaddresses(count=2)), 2)
+ assert_equal(len(self.nodes[0].getnodeaddresses(network="ipv4", count=8)), 8)
+
+ # Maximum possible addresses in AddrMan is 10000. The actual number will
+ # usually be less due to bucket and bucket position collisions.
+ node_addresses = self.nodes[0].getnodeaddresses(0, "ipv4")
assert_greater_than(len(node_addresses), 5000)
assert_greater_than(10000, len(node_addresses))
for a in node_addresses:
assert_greater_than(a["time"], 1527811200) # 1st June 2018
- assert_equal(a["services"], NODE_NETWORK | NODE_WITNESS)
+ assert_equal(a["services"], services)
assert a["address"] in imported_addrs
assert_equal(a["port"], 8333)
+ assert_equal(a["network"], "ipv4")
- node_addresses = self.nodes[0].getnodeaddresses(1)
- assert_equal(len(node_addresses), 1)
+ # Test the IPv6 address.
+ res = self.nodes[0].getnodeaddresses(0, "ipv6")
+ assert_equal(len(res), 1)
+ assert_equal(res[0]["address"], ipv6_addr)
+ assert_equal(res[0]["network"], "ipv6")
+ assert_equal(res[0]["port"], 8333)
+ assert_equal(res[0]["services"], services)
- assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
+ # Test for the absence of onion and I2P addresses.
+ for network in ["onion", "i2p"]:
+ assert_equal(self.nodes[0].getnodeaddresses(0, network), [])
- # addrman's size cannot be known reliably after insertion, as hash collisions may occur
- # so only test that requesting a large number of addresses returns less than that
- LARGE_REQUEST_COUNT = 10000
- node_addresses = self.nodes[0].getnodeaddresses(LARGE_REQUEST_COUNT)
- assert_greater_than(LARGE_REQUEST_COUNT, len(node_addresses))
+ # Test invalid arguments.
+ assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
+ assert_raises_rpc_error(-8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo")
+
+ def test_addpeeraddress(self):
+ self.log.info("Test addpeeraddress")
+ node = self.nodes[1]
+
+ self.log.debug("Test that addpeerinfo is a hidden RPC")
+ # It is hidden from general help, but its detailed help may be called directly.
+ assert "addpeerinfo" not in node.help()
+ assert "addpeerinfo" in node.help("addpeerinfo")
+
+ self.log.debug("Test that adding an empty address fails")
+ assert_equal(node.addpeeraddress(address="", port=8333), {"success": False})
+ assert_equal(node.getnodeaddresses(count=0), [])
+
+ self.log.debug("Test that adding a valid address succeeds")
+ assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": True})
+ addrs = node.getnodeaddresses(count=0)
+ assert_equal(len(addrs), 1)
+ assert_equal(addrs[0]["address"], "1.2.3.4")
+ assert_equal(addrs[0]["port"], 8333)
+
+ self.log.debug("Test that adding the same address again when already present fails")
+ assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": False})
+ assert_equal(len(node.getnodeaddresses(count=0)), 1)
if __name__ == '__main__':
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
new file mode 100755
index 0000000000..3d8d81d6b8
--- /dev/null
+++ b/test/functional/rpc_packages.py
@@ -0,0 +1,362 @@
+#!/usr/bin/env python3
+# 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.
+"""RPCs that handle raw transaction packages."""
+
+from decimal import Decimal
+from io import BytesIO
+import random
+
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
+ COIN,
+ CTransaction,
+ CTxInWitness,
+)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
+from test_framework.util import (
+ assert_equal,
+ hex_str_to_bytes,
+)
+
+class RPCPackagesTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def assert_testres_equal(self, package_hex, testres_expected):
+ """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only
+ be used to test packages where the order does not matter. The ordering of transactions in package_hex and
+ testres_expected must match.
+ """
+ shuffled_indeces = list(range(len(package_hex)))
+ random.shuffle(shuffled_indeces)
+ shuffled_package = [package_hex[i] for i in shuffled_indeces]
+ shuffled_testres = [testres_expected[i] for i in shuffled_indeces]
+ assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package))
+
+ def run_test(self):
+ self.log.info("Generate blocks to create UTXOs")
+ node = self.nodes[0]
+ self.privkeys = [node.get_deterministic_priv_key().key]
+ self.address = node.get_deterministic_priv_key().address
+ self.coins = []
+ # The last 100 coinbase transactions are premature
+ for b in node.generatetoaddress(200, self.address)[:100]:
+ coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0]
+ self.coins.append({
+ "txid": coinbase["txid"],
+ "amount": coinbase["vout"][0]["value"],
+ "scriptPubKey": coinbase["vout"][0]["scriptPubKey"],
+ })
+
+ # Create some transactions that can be reused throughout the test. Never submit these to mempool.
+ self.independent_txns_hex = []
+ self.independent_txns_testres = []
+ for _ in range(3):
+ coin = self.coins.pop()
+ rawtx = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.0001")})
+ signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys)
+ assert signedtx["complete"]
+ testres = node.testmempoolaccept([signedtx["hex"]])
+ assert testres[0]["allowed"]
+ self.independent_txns_hex.append(signedtx["hex"])
+ # testmempoolaccept returns a list of length one, avoid creating a 2D list
+ self.independent_txns_testres.append(testres[0])
+ self.independent_txns_testres_blank = [{
+ "txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres]
+
+ self.test_independent()
+ self.test_chain()
+ self.test_multiple_children()
+ self.test_multiple_parents()
+ self.test_conflicting()
+ self.test_rbf()
+
+ def chain_transaction(self, parent_txid, parent_value, n=0, parent_locking_script=None):
+ """Build a transaction that spends parent_txid.vout[n] and produces one output with
+ amount = parent_value with a fee deducted.
+ Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created).
+ """
+ node = self.nodes[0]
+ inputs = [{"txid": parent_txid, "vout": n}]
+ my_value = parent_value - Decimal("0.0001")
+ outputs = {self.address : my_value}
+ rawtx = node.createrawtransaction(inputs, outputs)
+ prevtxs = [{
+ "txid": parent_txid,
+ "vout": n,
+ "scriptPubKey": parent_locking_script,
+ "amount": parent_value,
+ }] if parent_locking_script else None
+ signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys, prevtxs=prevtxs)
+ tx = CTransaction()
+ assert signedtx["complete"]
+ tx.deserialize(BytesIO(hex_str_to_bytes(signedtx["hex"])))
+ return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex())
+
+ def test_independent(self):
+ self.log.info("Test multiple independent transactions in a package")
+ node = self.nodes[0]
+ # For independent transactions, order doesn't matter.
+ self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres)
+
+ self.log.info("Test an otherwise valid package with an extra garbage tx appended")
+ garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {self.address: 1})
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex_str_to_bytes(garbage_tx)))
+ # Only the txid and wtxids are returned because validation is incomplete for the independent txns.
+ # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package,
+ # it terminates immediately to avoid unnecessary, expensive signature verification.
+ package_bad = self.independent_txns_hex + [garbage_tx]
+ testres_bad = self.independent_txns_testres_blank + [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "allowed": False, "reject-reason": "missing-inputs"}]
+ self.assert_testres_equal(package_bad, testres_bad)
+
+ self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully")
+ coin = self.coins.pop()
+ tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.0001")})
+ tx_bad_sig = CTransaction()
+ tx_bad_sig.deserialize(BytesIO(hex_str_to_bytes(tx_bad_sig_hex)))
+ testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex])
+ # By the time the signature for the last transaction is checked, all the other transactions
+ # have been fully validated, which is why the node returns full validation results for all
+ # transactions here but empty results in other cases.
+ assert_equal(testres_bad_sig, self.independent_txns_testres + [{
+ "txid": tx_bad_sig.rehash(),
+ "wtxid": tx_bad_sig.getwtxid(), "allowed": False,
+ "reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)"
+ }])
+
+ self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate")
+ coin = self.coins.pop()
+ tx_high_fee_raw = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.999")})
+ tx_high_fee_signed = node.signrawtransactionwithkey(hexstring=tx_high_fee_raw, privkeys=self.privkeys)
+ assert tx_high_fee_signed["complete"]
+ tx_high_fee = CTransaction()
+ tx_high_fee.deserialize(BytesIO(hex_str_to_bytes(tx_high_fee_signed["hex"])))
+ testres_high_fee = node.testmempoolaccept([tx_high_fee_signed["hex"]])
+ assert_equal(testres_high_fee, [
+ {"txid": tx_high_fee.rehash(), "wtxid": tx_high_fee.getwtxid(), "allowed": False, "reject-reason": "max-fee-exceeded"}
+ ])
+ package_high_fee = [tx_high_fee_signed["hex"]] + self.independent_txns_hex
+ testres_package_high_fee = node.testmempoolaccept(package_high_fee)
+ assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank)
+
+ def test_chain(self):
+ node = self.nodes[0]
+ first_coin = self.coins.pop()
+
+ # Chain of 25 transactions
+ parent_locking_script = None
+ txid = first_coin["txid"]
+ chain_hex = []
+ chain_txns = []
+ value = first_coin["amount"]
+
+ for _ in range(25):
+ (tx, txhex, value, parent_locking_script) = self.chain_transaction(txid, value, 0, parent_locking_script)
+ txid = tx.rehash()
+ chain_hex.append(txhex)
+ chain_txns.append(tx)
+
+ self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
+ assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
+ [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])
+
+ self.log.info("Testmempoolaccept a chain of 25 transactions")
+ testres_multiple = node.testmempoolaccept(rawtxs=chain_hex)
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package test accept
+ for rawtx in chain_hex:
+ testres = node.testmempoolaccept([rawtx])
+ testres_single.append(testres[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple)
+
+ # Clean up by clearing the mempool
+ node.generate(1)
+
+ def test_multiple_children(self):
+ node = self.nodes[0]
+
+ self.log.info("Testmempoolaccept a package in which a transaction has two children within the package")
+ first_coin = self.coins.pop()
+ value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs
+ inputs = [{"txid": first_coin["txid"], "vout": 0}]
+ outputs = [{self.address : value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : value}]
+ rawtx = node.createrawtransaction(inputs, outputs)
+
+ parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys)
+ parent_tx = CTransaction()
+ assert parent_signed["complete"]
+ parent_tx.deserialize(BytesIO(hex_str_to_bytes(parent_signed["hex"])))
+ parent_txid = parent_tx.rehash()
+ assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"]
+
+ parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex()
+ child_value = value - Decimal("0.0001")
+
+ # Child A
+ (_, tx_child_a_hex, _, _) = self.chain_transaction(parent_txid, child_value, 0, parent_locking_script_a)
+ assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"]
+
+ # Child B
+ rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : child_value})
+ tx_child_b = CTransaction()
+ tx_child_b.deserialize(BytesIO(hex_str_to_bytes(rawtx_b)))
+ tx_child_b.wit.vtxinwit = [CTxInWitness()]
+ tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx_child_b_hex = tx_child_b.serialize().hex()
+ assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"]
+
+ self.log.info("Testmempoolaccept with entire package, should work with children in either order")
+ testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex])
+ testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex])
+ assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba])
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package testaccept
+ for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]:
+ testres = node.testmempoolaccept([rawtx])
+ testres_single.append(testres[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple_ab)
+
+ def create_child_with_parents(self, parents_tx, values, locking_scripts):
+ """Creates a transaction that spends the first output of each parent in parents_tx."""
+ num_parents = len(parents_tx)
+ total_value = sum(values)
+ inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx]
+ outputs = {self.address : total_value - num_parents * Decimal("0.0001")}
+ rawtx_child = self.nodes[0].createrawtransaction(inputs, outputs)
+ prevtxs = []
+ for i in range(num_parents):
+ prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]})
+ signedtx_child = self.nodes[0].signrawtransactionwithkey(hexstring=rawtx_child, privkeys=self.privkeys, prevtxs=prevtxs)
+ assert signedtx_child["complete"]
+ return signedtx_child["hex"]
+
+ def test_multiple_parents(self):
+ node = self.nodes[0]
+
+ self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package")
+ for num_parents in [2, 10, 24]:
+ # Test a package with num_parents parents and 1 child transaction.
+ package_hex = []
+ parents_tx = []
+ values = []
+ parent_locking_scripts = []
+ for _ in range(num_parents):
+ parent_coin = self.coins.pop()
+ value = parent_coin["amount"]
+ (tx, txhex, value, parent_locking_script) = self.chain_transaction(parent_coin["txid"], value)
+ package_hex.append(txhex)
+ parents_tx.append(tx)
+ values.append(value)
+ parent_locking_scripts.append(parent_locking_script)
+ child_hex = self.create_child_with_parents(parents_tx, values, parent_locking_scripts)
+ # Package accept should work with the parents in any order (as long as parents come before child)
+ for _ in range(10):
+ random.shuffle(package_hex)
+ testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_hex])
+ assert all([testres["allowed"] for testres in testres_multiple])
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package testaccept
+ for rawtx in package_hex + [child_hex]:
+ testres_single.append(node.testmempoolaccept([rawtx])[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple)
+
+ def test_conflicting(self):
+ node = self.nodes[0]
+ prevtx = self.coins.pop()
+ inputs = [{"txid": prevtx["txid"], "vout": 0}]
+ output1 = {node.get_deterministic_priv_key().address: 50 - 0.00125}
+ output2 = {ADDRESS_BCRT1_P2WSH_OP_TRUE: 50 - 0.00125}
+
+ # tx1 and tx2 share the same inputs
+ rawtx1 = node.createrawtransaction(inputs, output1)
+ rawtx2 = node.createrawtransaction(inputs, output2)
+ signedtx1 = node.signrawtransactionwithkey(hexstring=rawtx1, privkeys=self.privkeys)
+ signedtx2 = node.signrawtransactionwithkey(hexstring=rawtx2, privkeys=self.privkeys)
+ tx1 = CTransaction()
+ tx1.deserialize(BytesIO(hex_str_to_bytes(signedtx1["hex"])))
+ tx2 = CTransaction()
+ tx2.deserialize(BytesIO(hex_str_to_bytes(signedtx2["hex"])))
+ assert signedtx1["complete"]
+ assert signedtx2["complete"]
+
+ # Ensure tx1 and tx2 are valid by themselves
+ assert node.testmempoolaccept([signedtx1["hex"]])[0]["allowed"]
+ assert node.testmempoolaccept([signedtx2["hex"]])[0]["allowed"]
+
+ self.log.info("Test duplicate transactions in the same package")
+ testres = node.testmempoolaccept([signedtx1["hex"], signedtx1["hex"]])
+ assert_equal(testres, [
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ self.log.info("Test conflicting transactions in the same package")
+ testres = node.testmempoolaccept([signedtx1["hex"], signedtx2["hex"]])
+ assert_equal(testres, [
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": tx2.rehash(), "wtxid": tx2.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ def test_rbf(self):
+ node = self.nodes[0]
+ coin = self.coins.pop()
+ inputs = [{"txid": coin["txid"], "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}]
+ fee = Decimal('0.00125000')
+ output = {node.get_deterministic_priv_key().address: 50 - fee}
+ raw_replaceable_tx = node.createrawtransaction(inputs, output)
+ signed_replaceable_tx = node.signrawtransactionwithkey(hexstring=raw_replaceable_tx, privkeys=self.privkeys)
+ testres_replaceable = node.testmempoolaccept([signed_replaceable_tx["hex"]])
+ replaceable_tx = CTransaction()
+ replaceable_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"])))
+ assert_equal(testres_replaceable, [
+ {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(),
+ "allowed": True, "vsize": replaceable_tx.get_vsize(), "fees": { "base": fee }}
+ ])
+
+ # Replacement transaction is identical except has double the fee
+ replacement_tx = CTransaction()
+ replacement_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"])))
+ replacement_tx.vout[0].nValue -= int(fee * COIN) # Doubled fee
+ signed_replacement_tx = node.signrawtransactionwithkey(replacement_tx.serialize().hex(), self.privkeys)
+ replacement_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replacement_tx["hex"])))
+
+ self.log.info("Test that transactions within a package cannot replace each other")
+ testres_rbf_conflicting = node.testmempoolaccept([signed_replaceable_tx["hex"], signed_replacement_tx["hex"]])
+ assert_equal(testres_rbf_conflicting, [
+ {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ self.log.info("Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF")
+ node.sendrawtransaction(signed_replaceable_tx["hex"])
+ testres_rbf_single = node.testmempoolaccept([signed_replacement_tx["hex"]])
+ # This transaction is a valid BIP125 replace-by-fee
+ assert testres_rbf_single[0]["allowed"]
+ testres_rbf_package = self.independent_txns_testres_blank + [{
+ "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False, "reject-reason": "txn-mempool-conflict"
+ }]
+ self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package)
+
+if __name__ == "__main__":
+ RPCPackagesTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 079a3bd3ba..cf4d8a134c 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -6,6 +6,8 @@
"""
from decimal import Decimal
+from itertools import product
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_approx,
@@ -193,14 +195,14 @@ class PSBTTest(BitcoinTestFramework):
assert_approx(res2["fee"], 0.055, 0.005)
self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed")
- res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": "0.99999999", "add_inputs": True})
+ res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": "0.999", "add_inputs": True})
assert_approx(res3["fee"], 0.00000381, 0.0000001)
res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.00000999, "add_inputs": True})
assert_approx(res4["fee"], 0.00000381, 0.0000001)
self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed and that funding non-standard 'zero-fee' transactions is valid")
- for param in ["fee_rate", "feeRate"]:
- assert_equal(self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {param: 0, "add_inputs": True})["fee"], 0)
+ for param, zero_value in product(["fee_rate", "feeRate"], [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]):
+ assert_equal(0, self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {param: zero_value, "add_inputs": True})["fee"])
self.log.info("Test invalid fee rate settings")
for param, value in {("fee_rate", 100000), ("feeRate", 1)}:
@@ -210,8 +212,14 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: {"foo": "bar"}, "add_inputs": True})
+ # Test fee rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ assert_raises_rpc_error(-3, "Invalid amount",
+ self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: invalid_value, "add_inputs": True})
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
assert_raises_rpc_error(-3, "Invalid amount",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: "", "add_inputs": True})
+ self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": invalid_value, "add_inputs": True})
self.log.info("- raises RPC error if both feeRate and fee_rate are passed")
assert_raises_rpc_error(-8, "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)",
@@ -394,6 +402,14 @@ class PSBTTest(BitcoinTestFramework):
# We don't care about the decode result, but decoding must succeed.
self.nodes[0].decodepsbt(double_processed_psbt["psbt"])
+ # Make sure unsafe inputs are included if specified
+ self.nodes[2].createwallet(wallet_name="unsafe")
+ wunsafe = self.nodes[2].get_wallet_rpc("unsafe")
+ self.nodes[0].sendtoaddress(wunsafe.getnewaddress(), 2)
+ self.sync_mempools()
+ assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{self.nodes[0].getnewaddress(): 1}])
+ wunsafe.walletcreatefundedpsbt([], [{self.nodes[0].getnewaddress(): 1}], 0, {"include_unsafe": True})
+
# BIP 174 Test Vectors
# Check that unknown values are just passed through
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 86c7b3fbcc..53ddf24e47 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -15,6 +15,8 @@ Test the following RPCs:
from collections import OrderedDict
from decimal import Decimal
from io import BytesIO
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import CTransaction, ToHex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -66,7 +68,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info('prepare some coins for multiple *rawtransaction commands')
self.nodes[2].generate(1)
self.sync_all()
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.sync_all()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0)
diff --git a/test/functional/rpc_signer.py b/test/functional/rpc_signer.py
new file mode 100755
index 0000000000..3188763f49
--- /dev/null
+++ b/test/functional/rpc_signer.py
@@ -0,0 +1,79 @@
+#!/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.
+See also wallet_signer.py for tests that require wallet context.
+"""
+import os
+import platform
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+
+class RPCSignerTest(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_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(-1, '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(-1, 'fingerprint not found',
+ self.nodes[1].enumeratesigners
+ )
+ self.clear_mock_result(self.nodes[1])
+
+ result = self.nodes[1].enumeratesigners()
+ assert_equal(len(result['signers']), 2)
+ assert_equal(result['signers'][0]["fingerprint"], "00000001")
+ assert_equal(result['signers'][0]["name"], "trezor_t")
+
+if __name__ == '__main__':
+ RPCSignerTest().main()
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 2fbbdbbdf0..16b0019866 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -4,16 +4,18 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction signing using the signrawtransaction* RPCs."""
-from test_framework.address import check_script, script_to_p2sh
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.address import check_script, script_to_p2sh, script_to_p2wsh
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
-from test_framework.messages import sha256
-from test_framework.script import CScript, OP_0, OP_CHECKSIG
+from test_framework.messages import sha256, CTransaction, CTxInWitness
+from test_framework.script import CScript, OP_0, OP_CHECKSIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE
from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_p2wsh_script, script_to_p2wsh_script
from test_framework.wallet_util import bytes_to_wif
-from decimal import Decimal
+from decimal import Decimal, getcontext
+from io import BytesIO
class SignRawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -154,7 +156,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
def test_fully_signed_tx(self):
self.log.info("Test signing a fully signed transaction does nothing")
self.nodes[0].walletpassphrase("password", 9999)
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
rawtx = self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 10}])
fundedtx = self.nodes[0].fundrawtransaction(rawtx)
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"])
@@ -173,7 +175,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
embedded_pubkey = eckey.get_pubkey().get_bytes().hex()
p2sh_p2wsh_address = self.nodes[1].createmultisig(1, [embedded_pubkey], "p2sh-segwit")
# send transaction to P2SH-P2WSH 1-of-1 multisig address
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999)
self.nodes[0].generate(1)
self.sync_all()
@@ -238,6 +240,78 @@ class SignRawTransactionsTest(BitcoinTestFramework):
txn = self.nodes[0].signrawtransactionwithwallet(hex_str, prev_txs)
assert txn["complete"]
+ def test_signing_with_csv(self):
+ self.log.info("Test signing a transaction containing a fully signed CSV input")
+ self.nodes[0].walletpassphrase("password", 9999)
+ getcontext().prec = 8
+
+ # Make sure CSV is active
+ self.nodes[0].generate(500)
+
+ # Create a P2WSH script with CSV
+ script = CScript([1, OP_CHECKSEQUENCEVERIFY, OP_DROP])
+ address = script_to_p2wsh(script)
+
+ # Fund that address and make the spend
+ txid = self.nodes[0].sendtoaddress(address, 1)
+ vout = find_vout_for_address(self.nodes[0], txid, address)
+ self.nodes[0].generate(1)
+ utxo = self.nodes[0].listunspent()[0]
+ amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
+ tx = self.nodes[0].createrawtransaction(
+ [{"txid": txid, "vout": vout, "sequence": 1},{"txid": utxo["txid"], "vout": utxo["vout"]}],
+ [{self.nodes[0].getnewaddress(): amt}],
+ self.nodes[0].getblockcount()
+ )
+
+ # Set the witness script
+ ctx = CTransaction()
+ ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
+ ctx.wit.vtxinwit.append(CTxInWitness())
+ ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
+ tx = ctx.serialize_with_witness().hex()
+
+ # Sign and send the transaction
+ signed = self.nodes[0].signrawtransactionwithwallet(tx)
+ assert_equal(signed["complete"], True)
+ self.nodes[0].sendrawtransaction(signed["hex"])
+
+ def test_signing_with_cltv(self):
+ self.log.info("Test signing a transaction containing a fully signed CLTV input")
+ self.nodes[0].walletpassphrase("password", 9999)
+ getcontext().prec = 8
+
+ # Make sure CSV is active
+ self.nodes[0].generate(1500)
+
+ # Create a P2WSH script with CLTV
+ script = CScript([1000, OP_CHECKLOCKTIMEVERIFY, OP_DROP])
+ address = script_to_p2wsh(script)
+
+ # Fund that address and make the spend
+ txid = self.nodes[0].sendtoaddress(address, 1)
+ vout = find_vout_for_address(self.nodes[0], txid, address)
+ self.nodes[0].generate(1)
+ utxo = self.nodes[0].listunspent()[0]
+ amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
+ tx = self.nodes[0].createrawtransaction(
+ [{"txid": txid, "vout": vout},{"txid": utxo["txid"], "vout": utxo["vout"]}],
+ [{self.nodes[0].getnewaddress(): amt}],
+ self.nodes[0].getblockcount()
+ )
+
+ # Set the witness script
+ ctx = CTransaction()
+ ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
+ ctx.wit.vtxinwit.append(CTxInWitness())
+ ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
+ tx = ctx.serialize_with_witness().hex()
+
+ # Sign and send the transaction
+ signed = self.nodes[0].signrawtransactionwithwallet(tx)
+ assert_equal(signed["complete"], True)
+ self.nodes[0].sendrawtransaction(signed["hex"])
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
@@ -245,6 +319,8 @@ class SignRawTransactionsTest(BitcoinTestFramework):
self.OP_1NEGATE_test()
self.test_with_lock_outputs()
self.test_fully_signed_tx()
+ self.test_signing_with_csv()
+ self.test_signing_with_cltv()
if __name__ == '__main__':
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index 528da0cbfc..bf96b6353c 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test gettxoutproof and verifytxoutproof RPCs."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import CMerkleBlock, FromHex, ToHex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
@@ -23,7 +24,7 @@ class MerkleBlockTest(BitcoinTestFramework):
miniwallet = MiniWallet(self.nodes[0])
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(5)
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
self.sync_all()
chain_height = self.nodes[1].getblockcount()
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index e691b63df6..f35ea6c122 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -52,6 +52,9 @@ MAX_BLOCK_SIGOPS_WEIGHT = MAX_BLOCK_SIGOPS * WITNESS_SCALE_FACTOR
# Genesis block time (regtest)
TIME_GENESIS_BLOCK = 1296688602
+# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
+COINBASE_MATURITY = 100
+
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
@@ -115,7 +118,7 @@ def script_BIP34_coinbase_height(height):
return CScript([CScriptNum(height)])
-def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
+def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValue=50):
"""Create a coinbase transaction.
If pubkey is passed in, the coinbase output will be a P2PK output;
@@ -126,10 +129,11 @@ def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
coinbase = CTransaction()
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), 0xffffffff))
coinbaseoutput = CTxOut()
- coinbaseoutput.nValue = 50 * COIN
- halvings = int(height / 150) # regtest
- coinbaseoutput.nValue >>= halvings
- coinbaseoutput.nValue += fees
+ coinbaseoutput.nValue = nValue * COIN
+ if nValue == 50:
+ halvings = int(height / 150) # regtest
+ coinbaseoutput.nValue >>= halvings
+ coinbaseoutput.nValue += fees
if pubkey is not None:
coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG])
else:
@@ -177,6 +181,11 @@ def create_raw_transaction(node, txid, to_address, *, amount):
signed_psbt = wrpc.walletprocesspsbt(psbt)
psbt = signed_psbt['psbt']
final_psbt = node.finalizepsbt(psbt)
+ if not final_psbt["complete"]:
+ node.log.info(f'final_psbt={final_psbt}')
+ for w in node.listwallets():
+ wrpc = node.get_wallet_rpc(w)
+ node.log.info(f'listunspent={wrpc.listunspent()}')
assert_equal(final_psbt["complete"], True)
return final_psbt['hex']
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index e047e7fa14..5dc723c1d5 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -151,7 +151,7 @@ def test_ipv6_local():
have_ipv6 = True
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
- s.connect(('::1', 0))
+ s.connect(('::1', 1))
except socket.error:
have_ipv6 = False
return have_ipv6
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index 05099f3339..cc80b543cd 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -539,8 +539,16 @@ class P2PInterface(P2PConnection):
self.send_message(message)
self.sync_with_ping(timeout=timeout)
- # Sync up with the node
+ def sync_send_with_ping(self, timeout=60):
+ """Ensure SendMessages is called on this connection"""
+ # Calling sync_with_ping twice requires that the node calls
+ # `ProcessMessage` twice, and thus ensures `SendMessages` must have
+ # been called at least once
+ self.sync_with_ping()
+ self.sync_with_ping()
+
def sync_with_ping(self, timeout=60):
+ """Ensure ProcessMessages is called on this connection"""
self.send_message(msg_ping(nonce=self.ping_counter))
def test_function():
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 02eb10b5a4..a89a26caea 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -739,11 +739,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# 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]
+ gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
+ assert_equal(len(gen_addresses), 4)
for i in range(8):
cache_node.generatetoaddress(
nblocks=25 if i != 7 else 24,
- address=gen_addresses[i % 4],
+ address=gen_addresses[i % len(gen_addresses)],
)
assert_equal(cache_node.getblockchaininfo()["blocks"], 199)
@@ -757,7 +758,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
os.rmdir(cache_path('wallets')) # Remove empty wallets dir
for entry in os.listdir(cache_path()):
- if entry not in ['chainstate', 'blocks']: # Only keep chainstate and blocks folder
+ if entry not in ['chainstate', 'blocks', 'indexes']: # Only indexes, chainstate and blocks folders
os.remove(cache_path(entry))
for i in range(self.num_nodes):
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index ce9c1bc024..ba52abc7dd 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -400,14 +400,14 @@ class TestNode():
self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
- def profile_with_perf(self, profile_name):
+ def profile_with_perf(self, profile_name: str):
"""
Context manager that allows easy profiling of node activity using `perf`.
See `test/functional/README.md` for details on perf usage.
Args:
- profile_name (str): This string will be appended to the
+ profile_name: This string will be appended to the
profile data filename generated by perf.
"""
subp = self._start_perf(profile_name)
@@ -491,6 +491,7 @@ class TestNode():
self.start(extra_args, stdout=log_stdout, stderr=log_stderr, *args, **kwargs)
ret = self.process.wait(timeout=self.rpc_timeout)
self.log.debug(self._node_msg(f'bitcoind exited with status {ret} during initialization'))
+ assert ret != 0 # Exit code must indicate failure
self.running = False
self.process = None
# Check stderr for expected message
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 5c774934be..462019566c 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -20,6 +20,7 @@ import unittest
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
from io import BytesIO
+from typing import Callable, Optional
logger = logging.getLogger("TestFramework.utils")
@@ -80,7 +81,7 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_process_error(returncode, output, fun, *args, **kwds):
+def assert_raises_process_error(returncode: int, output: str, fun: Callable, *args, **kwds):
"""Execute a process and asserts the process return code and output.
Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError
@@ -88,9 +89,9 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
no CalledProcessError was raised or if the return code and output are not as expected.
Args:
- returncode (int): the process return code.
- output (string): [a substring of] the process output.
- fun (function): the function to call. This should execute a process.
+ returncode: the process return code.
+ output: [a substring of] the process output.
+ fun: the function to call. This should execute a process.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -105,7 +106,7 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_rpc_error(code, message, fun, *args, **kwds):
+def assert_raises_rpc_error(code: Optional[int], message: Optional[str], fun: Callable, *args, **kwds):
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
@@ -113,11 +114,11 @@ def assert_raises_rpc_error(code, message, fun, *args, **kwds):
no JSONRPCException was raised or if the error code/message are not as expected.
Args:
- code (int), optional: the error code returned by the RPC call (defined
- in src/rpc/protocol.h). Set to None if checking the error code is not required.
- message (string), optional: [a substring of] the error string returned by the
- RPC call. Set to None if checking the error string is not required.
- fun (function): the function to call. This should be the name of an RPC.
+ code: the error code returned by the RPC call (defined in src/rpc/protocol.h).
+ Set to None if checking the error code is not required.
+ message: [a substring of] the error string returned by the RPC call.
+ Set to None if checking the error string is not required.
+ fun: the function to call. This should be the name of an RPC.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -374,6 +375,8 @@ def write_config(config_path, *, n, chain, extra_config=""):
f.write("upnp=0\n")
f.write("natpmp=0\n")
f.write("shrinkdebugfile=0\n")
+ # To improve SQLite wallet performance so that the tests don't timeout, use -unsafesqlitesync
+ f.write("unsafesqlitesync=1\n")
f.write(extra_config)
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index a906a21dd0..0962a5cb54 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -5,7 +5,10 @@
"""A limited-functionality wallet, which may replace a real wallet in tests"""
from decimal import Decimal
+from enum import Enum
+from typing import Optional
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.key import ECKey
from test_framework.messages import (
COIN,
COutPoint,
@@ -16,7 +19,11 @@ from test_framework.messages import (
)
from test_framework.script import (
CScript,
+ LegacySignatureHash,
+ OP_CHECKSIG,
OP_TRUE,
+ OP_NOP,
+ SIGHASH_ALL,
)
from test_framework.util import (
assert_equal,
@@ -25,25 +32,72 @@ from test_framework.util import (
)
+class MiniWalletMode(Enum):
+ """Determines the transaction type the MiniWallet is creating and spending.
+
+ For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient;
+ it simply uses a fixed bech32 P2WSH address whose coins are spent with a
+ witness stack of OP_TRUE, i.e. following an anyone-can-spend policy.
+ However, if the transactions need to be modified by the user (e.g. prepending
+ scriptSig for testing opcodes that are activated by a soft-fork), or the txs
+ should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK
+ can be useful. Summary of modes:
+
+ | output | | tx is | can modify | needs
+ mode | description | address | standard | scriptSig | signing
+ ----------------+-------------------+-----------+----------+------------+----------
+ ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no
+ RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no
+ RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes
+ """
+ ADDRESS_OP_TRUE = 1
+ RAW_OP_TRUE = 2
+ RAW_P2PK = 3
+
+
class MiniWallet:
- def __init__(self, test_node):
+ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
self._test_node = test_node
self._utxos = []
- self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
- self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ self._priv_key = None
+ self._address = None
+
+ assert isinstance(mode, MiniWalletMode)
+ if mode == MiniWalletMode.RAW_OP_TRUE:
+ self._scriptPubKey = bytes(CScript([OP_TRUE]))
+ elif mode == MiniWalletMode.RAW_P2PK:
+ # use simple deterministic private key (k=1)
+ self._priv_key = ECKey()
+ self._priv_key.set((1).to_bytes(32, 'big'), True)
+ pub_key = self._priv_key.get_pubkey()
+ self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG]))
+ elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
+ 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']})
+ self.scan_tx(tx)
+
+ def scan_tx(self, tx):
+ """Scan the tx for self._scriptPubKey outputs and add them to self._utxos"""
+ 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 sign_tx(self, tx):
+ """Sign tx that has been created by MiniWallet in P2PK mode"""
+ assert self._priv_key is not None
+ (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
+ assert err is None
+ tx.vin[0].scriptSig = CScript([self._priv_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
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)
+ blocks = self._test_node.generatetodescriptor(num_blocks, f'raw({self._scriptPubKey.hex()})')
for b in blocks:
cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
@@ -52,12 +106,12 @@ class MiniWallet:
def get_address(self):
return self._address
- def get_utxo(self, *, txid=''):
+ def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True):
"""
Returns a utxo and marks it as spent (pops it from the internal list)
Args:
- txid (string), optional: get the first utxo we find from a specific transaction
+ txid: get the first utxo we find from a specific transaction
Note: Can be used to get the change output immediately after a send_self_transfer
"""
@@ -65,10 +119,19 @@ class MiniWallet:
if txid:
utxo = next(filter(lambda utxo: txid == utxo['txid'], self._utxos))
index = self._utxos.index(utxo)
- return self._utxos.pop(index)
+ if mark_as_spent:
+ return self._utxos.pop(index)
+ else:
+ return self._utxos[index]
- def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
+ def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, locktime=0):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
+ tx = self.create_self_transfer(fee_rate=fee_rate, from_node=from_node, utxo_to_spend=utxo_to_spend)
+ self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])
+ return tx
+
+ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0):
+ """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
self._utxos = sorted(self._utxos, key=lambda k: k['value'])
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
vsize = Decimal(96)
@@ -79,13 +142,30 @@ class MiniWallet:
tx = CTransaction()
tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']))]
tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
- tx.wit.vtxinwit = [CTxInWitness()]
- tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx.nLockTime = locktime
+ if not self._address:
+ # raw script
+ if self._priv_key is not None:
+ # P2PK, need to sign
+ self.sign_tx(tx)
+ else:
+ # anyone-can-spend
+ tx.vin[0].scriptSig = CScript([OP_NOP] * 35) # pad to identical size
+ else:
+ tx.wit.vtxinwit = [CTxInWitness()]
+ tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
tx_hex = tx.serialize().hex()
tx_info = from_node.testmempoolaccept([tx_hex])[0]
- self._utxos.append({'txid': tx_info['txid'], 'vout': 0, 'value': send_value})
+ assert_equal(mempool_valid, tx_info['allowed'])
+ if mempool_valid:
+ # TODO: for P2PK, vsize is not constant due to varying scriptSig length,
+ # so only check this for anyone-can-spend outputs right now
+ if self._priv_key is None:
+ assert_equal(tx_info['vsize'], vsize)
+ assert_equal(tx_info['fees']['base'], fee)
+ return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
+
+ def sendrawtransaction(self, *, from_node, tx_hex):
from_node.sendrawtransaction(tx_hex)
- assert_equal(tx_info['vsize'], vsize)
- assert_equal(tx_info['fees']['base'], fee)
- return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex}
+ self.scan_tx(from_node.decoderawtransaction(tx_hex))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 001d161612..c9a8cc5611 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -111,6 +111,7 @@ BASE_SCRIPTS = [
'wallet_listtransactions.py --legacy-wallet',
'wallet_listtransactions.py --descriptors',
'feature_taproot.py',
+ 'rpc_signer.py',
'wallet_signer.py --descriptors',
# vv Tests less than 60s vv
'p2p_sendheaders.py',
@@ -210,6 +211,7 @@ BASE_SCRIPTS = [
'mempool_package_onemore.py',
'rpc_createmultisig.py --legacy-wallet',
'rpc_createmultisig.py --descriptors',
+ 'rpc_packages.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py --legacy-wallet',
@@ -259,6 +261,7 @@ BASE_SCRIPTS = [
'wallet_send.py --legacy-wallet',
'wallet_send.py --descriptors',
'wallet_create_tx.py --descriptors',
+ 'wallet_taproot.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
'wallet_coinbase_category.py --legacy-wallet',
@@ -280,6 +283,8 @@ BASE_SCRIPTS = [
'rpc_scantxoutset.py',
'feature_logging.py',
'feature_anchors.py',
+ 'feature_coinstatsindex.py',
+ 'wallet_orphanedreward.py',
'p2p_node_network_limited.py',
'p2p_permissions.py',
'feature_blocksdir.py',
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 2e0edcfa38..d24cc802a4 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -12,6 +12,7 @@
"""
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -28,7 +29,7 @@ class AbandonConflictTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.sync_blocks()
balance = self.nodes[0].getbalance()
txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index b3bee1876d..6d93cf412f 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -53,6 +53,7 @@ Test that the nodes generate the correct change address type:
from decimal import Decimal
import itertools
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import (
descsum_create,
@@ -220,7 +221,7 @@ class AddressTypeTest(BitcoinTestFramework):
def run_test(self):
# Mine 101 blocks on node5 to bring nodes out of IBD and make sure that
# no coinbases are maturing for the nodes-under-test during the test
- self.nodes[5].generate(101)
+ self.nodes[5].generate(COINBASE_MATURITY + 1)
self.sync_blocks()
uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
@@ -258,7 +259,7 @@ class AddressTypeTest(BitcoinTestFramework):
self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))
old_balances = self.get_balances()
self.log.debug("Old balances are {}".format(old_balances))
- to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
+ to_send = (old_balances[from_node] / (COINBASE_MATURITY + 1)).quantize(Decimal("0.00000001"))
sends = {}
addresses = {}
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 1d3736d9b1..c13d8de4b5 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -42,25 +42,25 @@ def count_unspent(node):
r["reused"]["supported"] = supports_reused
return r
-def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None):
+def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None, margin=0.001):
'''Make assertions about a node's unspent output statistics'''
stats = count_unspent(node)
if total_count is not None:
assert_equal(stats["total"]["count"], total_count)
if total_sum is not None:
- assert_approx(stats["total"]["sum"], total_sum, 0.001)
+ assert_approx(stats["total"]["sum"], total_sum, margin)
if reused_supported is not None:
assert_equal(stats["reused"]["supported"], reused_supported)
if reused_count is not None:
assert_equal(stats["reused"]["count"], reused_count)
if reused_sum is not None:
- assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
+ assert_approx(stats["reused"]["sum"], reused_sum, margin)
-def assert_balances(node, mine):
+def assert_balances(node, mine, margin=0.001):
'''Make assertions about a node's getbalances output'''
got = node.getbalances()["mine"]
for k,v in mine.items():
- assert_approx(got[k], v, 0.001)
+ assert_approx(got[k], v, margin)
class AvoidReuseTest(BitcoinTestFramework):
@@ -299,7 +299,7 @@ class AvoidReuseTest(BitcoinTestFramework):
ret_addr = self.nodes[0].getnewaddress()
# send multiple transactions, reusing one address
- for _ in range(11):
+ for _ in range(101):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
@@ -311,14 +311,14 @@ class AvoidReuseTest(BitcoinTestFramework):
# getbalances and listunspent should show the remaining outputs
# in the reused address as used/reused
- assert_unspent(self.nodes[1], total_count=2, total_sum=6, reused_count=1, reused_sum=1)
- assert_balances(self.nodes[1], mine={"used": 1, "trusted": 5})
+ assert_unspent(self.nodes[1], total_count=2, total_sum=96, reused_count=1, reused_sum=1, margin=0.01)
+ assert_balances(self.nodes[1], mine={"used": 1, "trusted": 95}, margin=0.01)
def test_full_destination_group_is_preferred(self):
'''
- Test the case where [1] only has 11 outputs of 1 BTC in the same reused
+ Test the case where [1] only has 101 outputs of 1 BTC in the same reused
address and tries to send a small payment of 0.5 BTC. The wallet
- should use 10 outputs from the reused address as inputs and not a
+ should use 100 outputs from the reused address as inputs and not a
single 1 BTC input, in order to join several outputs from the reused
address.
'''
@@ -330,8 +330,8 @@ class AvoidReuseTest(BitcoinTestFramework):
new_addr = self.nodes[1].getnewaddress()
ret_addr = self.nodes[0].getnewaddress()
- # Send 11 outputs of 1 BTC to the same, reused address in the wallet
- for _ in range(11):
+ # Send 101 outputs of 1 BTC to the same, reused address in the wallet
+ for _ in range(101):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
@@ -342,14 +342,14 @@ class AvoidReuseTest(BitcoinTestFramework):
txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=0.5)
inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"]
- # The transaction should use 10 inputs exactly
- assert_equal(len(inputs), 10)
+ # The transaction should use 100 inputs exactly
+ assert_equal(len(inputs), 100)
def test_all_destination_groups_are_used(self):
'''
- Test the case where [1] only has 22 outputs of 1 BTC in the same reused
- address and tries to send a payment of 20.5 BTC. The wallet
- should use all 22 outputs from the reused address as inputs.
+ Test the case where [1] only has 202 outputs of 1 BTC in the same reused
+ address and tries to send a payment of 200.5 BTC. The wallet
+ should use all 202 outputs from the reused address as inputs.
'''
self.log.info("Test that all destination groups are used")
@@ -359,20 +359,20 @@ class AvoidReuseTest(BitcoinTestFramework):
new_addr = self.nodes[1].getnewaddress()
ret_addr = self.nodes[0].getnewaddress()
- # Send 22 outputs of 1 BTC to the same, reused address in the wallet
- for _ in range(22):
+ # Send 202 outputs of 1 BTC to the same, reused address in the wallet
+ for _ in range(202):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
self.sync_all()
# Sending a transaction that needs to use the full groups
- # of 10 inputs but also the incomplete group of 2 inputs.
- txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=20.5)
+ # of 100 inputs but also the incomplete group of 2 inputs.
+ txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=200.5)
inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"]
- # The transaction should use 22 inputs exactly
- assert_equal(len(inputs), 22)
+ # The transaction should use 202 inputs exactly
+ assert_equal(len(inputs), 202)
if __name__ == '__main__':
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index f34c1345e0..05a0ef0ea1 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -35,6 +35,7 @@ import os
from random import randint
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -123,7 +124,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.sync_blocks()
self.nodes[2].generate(1)
self.sync_blocks()
- self.nodes[3].generate(100)
+ self.nodes[3].generate(COINBASE_MATURITY)
self.sync_blocks()
assert_equal(self.nodes[0].getbalance(), 50)
@@ -152,7 +153,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.do_one_round()
# Generate 101 more blocks, so any fees paid mature
- self.nodes[3].generate(101)
+ self.nodes[3].generate(COINBASE_MATURITY + 1)
self.sync_all()
balance0 = self.nodes[0].getbalance()
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 433b40faee..204a866c55 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -7,6 +7,7 @@ from decimal import Decimal
import struct
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE as ADDRESS_WATCHONLY
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -72,7 +73,7 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
self.nodes[1].generate(1)
- self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY)
+ self.nodes[1].generatetoaddress(COINBASE_MATURITY + 1, ADDRESS_WATCHONLY)
self.sync_all()
if not self.options.descriptors:
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index dc6f8ed9c4..a052ec7477 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -6,6 +6,7 @@
from decimal import Decimal
from itertools import product
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
@@ -15,6 +16,7 @@ from test_framework.util import (
)
from test_framework.wallet_util import test_address
+NOT_A_NUMBER_OR_STRING = "Amount is not a number or string"
OUT_OF_RANGE = "Amount out of range"
@@ -64,7 +66,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(walletinfo['balance'], 0)
self.sync_all(self.nodes[0:3])
- self.nodes[1].generate(101)
+ self.nodes[1].generate(COINBASE_MATURITY + 1)
self.sync_all(self.nodes[0:3])
assert_equal(self.nodes[0].getbalance(), 50)
@@ -95,6 +97,8 @@ class WalletTest(BitcoinTestFramework):
# but invisible if you include mempool
txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
assert_equal(txout['value'], 50)
+ txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index) # by default include_mempool=True
+ assert txout is None
txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True)
assert txout is None
# new utxo from mempool should be invisible if you exclude mempool
@@ -155,7 +159,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(len(self.nodes[1].listlockunspent()), 0)
# Have node1 generate 100 blocks (so node0 can recover the fee)
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.sync_all(self.nodes[0:3])
# node0 should end up with 100 btc in block rewards plus fees, but
@@ -262,12 +266,25 @@ class WalletTest(BitcoinTestFramework):
# Test setting explicit fee rate just below the minimum.
self.log.info("Test sendmany raises 'fee rate too low' if fee_rate of 0.99999999 is passed")
assert_raises_rpc_error(-6, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",
- self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0.99999999)
-
- self.log.info("Test sendmany raises if fee_rate of 0 or -1 is passed")
- assert_raises_rpc_error(-6, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",
- self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0)
+ self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0.999)
+
+ self.log.info("Test sendmany raises if an invalid fee_rate is passed")
+ # Test fee_rate with zero values.
+ msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
+ for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
+ assert_raises_rpc_error(-6, msg, self.nodes[2].sendmany, amounts={address: 1}, fee_rate=zero_value)
+ msg = "Invalid amount"
+ # Test fee_rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 1.0}, fee_rate=invalid_value)
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=invalid_value)
+ # Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=-1)
+ # Test type error.
+ for invalid_value in [True, {"foo": "bar"}]:
+ assert_raises_rpc_error(-3, NOT_A_NUMBER_OR_STRING, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=invalid_value)
self.log.info("Test sendmany raises if an invalid conf_target or estimate_mode is passed")
for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):
@@ -445,12 +462,25 @@ class WalletTest(BitcoinTestFramework):
# Test setting explicit fee rate just below the minimum.
self.log.info("Test sendtoaddress raises 'fee rate too low' if fee_rate of 0.99999999 is passed")
assert_raises_rpc_error(-6, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",
- self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=0.99999999)
-
- self.log.info("Test sendtoaddress raises if fee_rate of 0 or -1 is passed")
- assert_raises_rpc_error(-6, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",
- self.nodes[2].sendtoaddress, address=address, amount=10, fee_rate=0)
+ self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=0.999)
+
+ self.log.info("Test sendtoaddress raises if an invalid fee_rate is passed")
+ # Test fee_rate with zero values.
+ msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
+ for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
+ assert_raises_rpc_error(-6, msg, self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=zero_value)
+ msg = "Invalid amount"
+ # Test fee_rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ assert_raises_rpc_error(-3, msg, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=invalid_value)
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ assert_raises_rpc_error(-3, msg, self.nodes[2].sendtoaddress, address=address, amount=10, fee_rate=invalid_value)
+ # Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=-1)
+ # Test type error.
+ for invalid_value in [True, {"foo": "bar"}]:
+ assert_raises_rpc_error(-3, NOT_A_NUMBER_OR_STRING, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=invalid_value)
self.log.info("Test sendtoaddress raises if an invalid conf_target or estimate_mode is passed")
for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 0d1b6c54ce..b21461ee7b 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -16,6 +16,7 @@ make assumptions about execution order.
from decimal import Decimal
import io
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.blocktools import add_witness_commitment, create_block, create_coinbase, send_to_witness
from test_framework.messages import BIP125_SEQUENCE_NUMBER, CTransaction
from test_framework.test_framework import BitcoinTestFramework
@@ -110,13 +111,24 @@ class BumpFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Insufficient total fee 0.00000141", rbf_node.bumpfee, rbfid, {"fee_rate": INSUFFICIENT})
self.log.info("Test invalid fee rate settings")
- assert_raises_rpc_error(-8, "Insufficient total fee 0.00", rbf_node.bumpfee, rbfid, {"fee_rate": 0})
assert_raises_rpc_error(-4, "Specified or calculated fee 0.141 is too high (cannot be higher than -maxtxfee 0.10",
rbf_node.bumpfee, rbfid, {"fee_rate": TOO_HIGH})
+ # Test fee_rate with zero values.
+ msg = "Insufficient total fee 0.00"
+ for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
+ assert_raises_rpc_error(-8, msg, rbf_node.bumpfee, rbfid, {"fee_rate": zero_value})
+ msg = "Invalid amount"
+ # Test fee_rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, {"fee_rate": invalid_value})
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, {"fee_rate": invalid_value})
+ # Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, "Amount out of range", rbf_node.bumpfee, rbfid, {"fee_rate": -1})
+ # Test type error.
for value in [{"foo": "bar"}, True]:
assert_raises_rpc_error(-3, "Amount is not a number or string", rbf_node.bumpfee, rbfid, {"fee_rate": value})
- assert_raises_rpc_error(-3, "Invalid amount", rbf_node.bumpfee, rbfid, {"fee_rate": ""})
self.log.info("Test explicit fee rate raises RPC error if both fee_rate and conf_target are passed")
assert_raises_rpc_error(-8, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation "
@@ -254,7 +266,7 @@ def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
self.log.info('Testing small output with feerate bump succeeds')
# Make sure additional inputs exist
- rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbf_node.generatetoaddress(COINBASE_MATURITY + 1, rbf_node.getnewaddress())
rbfid = spend_one_input(rbf_node, dest_address)
input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
assert_equal(len(input_list), 1)
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 1e032bdd6c..c6f5d334f8 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test descriptor wallet function."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -83,7 +84,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
send_wrpc = self.nodes[0].get_wallet_rpc("desc1")
# Generate some coins
- send_wrpc.generatetoaddress(101, send_wrpc.getnewaddress())
+ send_wrpc.generatetoaddress(COINBASE_MATURITY + 1, send_wrpc.getnewaddress())
# Make transactions
self.log.info("Test sending and receiving")
diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py
index 78eef4b790..b28f3ecebc 100755
--- a/test/functional/wallet_fallbackfee.py
+++ b/test/functional/wallet_fallbackfee.py
@@ -3,6 +3,8 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet replace-by-fee capabilities in conjunction with the fallbackfee."""
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_raises_rpc_error
@@ -15,7 +17,7 @@ class WalletRBFTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# sending a transaction without fee estimations must be possible by default on regtest
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index c0b76d960f..f32acb8e15 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet group functionality."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import CTransaction, FromHex, ToHex
from test_framework.util import (
@@ -31,7 +32,7 @@ class WalletGroupTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Setting up")
# Mine some coins
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# Get some addresses from the two nodes
addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 23d132df41..d41a389197 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -7,6 +7,7 @@
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -48,7 +49,7 @@ class WalletHDTest(BitcoinTestFramework):
# Derive some HD addresses and remember the last
# Also send funds to each add
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
hd_add = None
NUM_HD_ADDS = 10
for i in range(1, NUM_HD_ADDS + 1):
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index ed62ef0e9d..a2da16e5a3 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -16,6 +16,7 @@ variants.
and test the values returned."""
from test_framework.address import key_to_p2pkh
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
from test_framework.util import (
@@ -73,7 +74,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
self.log.info('Mining coins')
- w0.generatetoaddress(101, w0.getnewaddress())
+ w0.generatetoaddress(COINBASE_MATURITY + 1, w0.getnewaddress())
# RPC importdescriptors -----------------------------------------------
@@ -459,6 +460,77 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(tx_signed_2['complete'], True)
self.nodes[1].sendrawtransaction(tx_signed_2['hex'])
+ self.log.info("We can create and use a huge multisig under P2WSH")
+ self.nodes[1].createwallet(wallet_name='wmulti_priv_big', blank=True, descriptors=True)
+ wmulti_priv_big = self.nodes[1].get_wallet_rpc('wmulti_priv_big')
+ xkey = "tprv8ZgxMBicQKsPeZSeYx7VXDDTs3XrTcmZQpRLbAeSQFCQGgKwR4gKpcxHaKdoTNHniv4EPDJNdzA3KxRrrBHcAgth8fU5X4oCndkkxk39iAt/*"
+ xkey_int = "tprv8ZgxMBicQKsPeZSeYx7VXDDTs3XrTcmZQpRLbAeSQFCQGgKwR4gKpcxHaKdoTNHniv4EPDJNdzA3KxRrrBHcAgth8fU5X4oCndkkxk39iAt/1/*"
+ res = wmulti_priv_big.importdescriptors([
+ {
+ "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey + ',') * 19}{xkey}))"),
+ "active": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ },
+ {
+ "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey_int + ',') * 19}{xkey_int}))"),
+ "active": True,
+ "internal": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ }])
+ assert_equal(res[0]['success'], True)
+ assert_equal(res[1]['success'], True)
+
+ addr = wmulti_priv_big.getnewaddress()
+ w0.sendtoaddress(addr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+ # It is standard and would relay.
+ txid = wmulti_priv_big.sendtoaddress(w0.getnewaddress(), 9.999)
+ decoded = wmulti_priv_big.decoderawtransaction(wmulti_priv_big.gettransaction(txid)['hex'])
+ # 20 sigs + dummy + witness script
+ assert_equal(len(decoded['vin'][0]['txinwitness']), 22)
+
+
+ self.log.info("Under P2SH, multisig are standard with up to 15 "
+ "compressed keys")
+ self.nodes[1].createwallet(wallet_name='multi_priv_big_legacy',
+ blank=True, descriptors=True)
+ multi_priv_big = self.nodes[1].get_wallet_rpc('multi_priv_big_legacy')
+ res = multi_priv_big.importdescriptors([
+ {
+ "desc": descsum_create(f"sh(multi(15,{(xkey + ',') * 14}{xkey}))"),
+ "active": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ },
+ {
+ "desc": descsum_create(f"sh(multi(15,{(xkey_int + ',') * 14}{xkey_int}))"),
+ "active": True,
+ "internal": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ }])
+ assert_equal(res[0]['success'], True)
+ assert_equal(res[1]['success'], True)
+
+ addr = multi_priv_big.getnewaddress("", "legacy")
+ w0.sendtoaddress(addr, 10)
+ self.nodes[0].generate(6)
+ self.sync_all()
+ # It is standard and would relay.
+ txid = multi_priv_big.sendtoaddress(w0.getnewaddress(), 10, "", "",
+ True)
+ decoded = multi_priv_big.decoderawtransaction(
+ multi_priv_big.gettransaction(txid)['hex']
+ )
+
+
self.log.info("Combo descriptors cannot be active")
self.test_importdesc({"desc": descsum_create("combo(tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*)"),
"active": True,
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 13186b9e1d..0a00c5eed9 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -15,6 +15,7 @@ variants.
- `test_address()` is called to call getaddressinfo for an address on node1
and test the values returned."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.script import (
CScript,
OP_NOP,
@@ -255,7 +256,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH address
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -276,7 +277,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -297,7 +298,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script + Private Keys + !Watchonly
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -323,7 +324,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script + Private Keys + Watchonly
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 7635ce2139..ded0e64b1d 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -5,6 +5,7 @@
"""Test the importprunedfunds and removeprunedfunds RPCs."""
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.address import key_to_p2wpkh
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
@@ -24,7 +25,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Mining blocks...")
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.sync_all()
@@ -46,7 +47,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.sync_all()
# Node 1 sync test
- assert_equal(self.nodes[1].getblockcount(), 101)
+ assert_equal(self.nodes[1].getblockcount(), COINBASE_MATURITY + 1)
# Address Test - before import
address_info = self.nodes[1].getaddressinfo(address1)
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index 5619d57947..1ecf08b9ac 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -13,6 +13,7 @@ Two nodes. Node1 is under test. Node0 is providing transactions and generating b
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -31,7 +32,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
def run_test(self):
wallet_path = os.path.join(self.nodes[1].datadir, self.chain, "wallets", self.default_wallet_name, self.wallet_data_filename)
wallet_backup_path = os.path.join(self.nodes[1].datadir, "wallet.bak")
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.log.info("Make backup of wallet")
self.stop_node(1)
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index 551eb72720..2d792bac52 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -11,6 +11,7 @@ RPCs tested are:
"""
from collections import defaultdict
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
from test_framework.wallet_util import test_address
@@ -32,7 +33,7 @@ class WalletLabelsTest(BitcoinTestFramework):
# Note each time we call generate, all generated coins go into
# the same address, so we call twice to get two addresses w/50 each
node.generatetoaddress(nblocks=1, address=node.getnewaddress(label='coinbase'))
- node.generatetoaddress(nblocks=101, address=node.getnewaddress(label='coinbase'))
+ node.generatetoaddress(nblocks=COINBASE_MATURITY + 1, address=node.getnewaddress(label='coinbase'))
assert_equal(node.getbalance(), 100)
# there should be 2 address groups
@@ -104,7 +105,7 @@ class WalletLabelsTest(BitcoinTestFramework):
label.verify(node)
assert_equal(node.getreceivedbylabel(label.name), 2)
label.verify(node)
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
# Check that setlabel can assign a label to a new unused address.
for label in labels:
@@ -124,7 +125,7 @@ class WalletLabelsTest(BitcoinTestFramework):
label.add_address(multisig_address)
label.purpose[multisig_address] = "send"
label.verify(node)
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
# Check that setlabel can change the label of an address from a
# different label.
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 448720530c..3899971bd7 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -5,6 +5,7 @@
"""Test the listsinceblock RPC."""
from test_framework.address import key_to_p2wpkh
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import BIP125_SEQUENCE_NUMBER
@@ -29,7 +30,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
# All nodes are in IBD from genesis, so they'll need the miner (node2) to be an outbound connection, or have
# only one connection. (See fPreferredDownload in net_processing)
self.connect_nodes(1, 2)
- self.nodes[2].generate(101)
+ self.nodes[2].generate(COINBASE_MATURITY + 1)
self.sync_all()
self.test_no_blockhash()
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 71d1b96a95..00d2c9ffe4 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -14,6 +14,7 @@ import stat
import time
from test_framework.authproxy import JSONRPCException
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
from test_framework.util import (
@@ -229,7 +230,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo)
w1, w2, w3, w4, *_ = wallets
- node.generatetoaddress(nblocks=101, address=w1.getnewaddress())
+ node.generatetoaddress(nblocks=COINBASE_MATURITY + 1, address=w1.getnewaddress())
assert_equal(w1.getbalance(), 100)
assert_equal(w2.getbalance(), 0)
assert_equal(w3.getbalance(), 0)
diff --git a/test/functional/wallet_orphanedreward.py b/test/functional/wallet_orphanedreward.py
new file mode 100755
index 0000000000..e1544cbb48
--- /dev/null
+++ b/test/functional/wallet_orphanedreward.py
@@ -0,0 +1,61 @@
+#!/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 orphaned block rewards in the wallet."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class OrphanedBlockRewardTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Generate some blocks and obtain some coins on node 0. We send
+ # some balance to node 1, which will hold it as a single coin.
+ self.nodes[0].generate(150)
+ self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
+ self.nodes[0].generate(1)
+
+ # Get a block reward with node 1 and remember the block so we can orphan
+ # it later.
+ self.sync_blocks()
+ blk = self.nodes[1].generate(1)[0]
+ self.sync_blocks()
+
+ # Let the block reward mature and send coins including both
+ # the existing balance and the block reward.
+ self.nodes[0].generate(150)
+ assert_equal(self.nodes[1].getbalance(), 10 + 25)
+ txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 30)
+
+ # Orphan the block reward and make sure that the original coins
+ # from the wallet can still be spent.
+ self.nodes[0].invalidateblock(blk)
+ self.nodes[0].generate(152)
+ self.sync_blocks()
+ # Without the following abandontransaction call, the coins are
+ # not considered available yet.
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 0,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ # The following abandontransaction is necessary to make the later
+ # lines succeed, and probably should not be needed; see
+ # https://github.com/bitcoin/bitcoin/issues/14148.
+ self.nodes[1].abandontransaction(txid)
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 10,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 9)
+
+if __name__ == '__main__':
+ OrphanedBlockRewardTest().main()
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 53553dcd80..d24d1693af 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -33,12 +33,15 @@ class WalletSendTest(BitcoinTestFramework):
def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
arg_conf_target=None, arg_estimate_mode=None, arg_fee_rate=None,
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
- inputs=None, add_inputs=None, change_address=None, change_position=None, change_type=None,
+ inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
expect_error=None):
assert (amount is None) != (data is None)
- from_balance_before = from_wallet.getbalance()
+ from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
+ if include_unsafe:
+ from_balance_before += from_wallet.getbalances()["mine"]["untrusted_pending"]
+
if to_wallet is None:
assert amount is None
else:
@@ -71,6 +74,8 @@ class WalletSendTest(BitcoinTestFramework):
options["inputs"] = inputs
if add_inputs is not None:
options["add_inputs"] = add_inputs
+ if include_unsafe is not None:
+ options["include_unsafe"] = include_unsafe
if change_address is not None:
options["change_address"] = change_address
if change_position is not None:
@@ -133,6 +138,10 @@ class WalletSendTest(BitcoinTestFramework):
assert not "txid" in res
assert "psbt" in res
+ from_balance = from_wallet.getbalances()["mine"]["trusted"]
+ if include_unsafe:
+ from_balance += from_wallet.getbalances()["mine"]["untrusted_pending"]
+
if add_to_wallet and not include_watching:
# Ensure transaction exists in the wallet:
tx = from_wallet.gettransaction(res["txid"])
@@ -143,13 +152,13 @@ class WalletSendTest(BitcoinTestFramework):
assert tx
if amount:
if subtract_fee_from_outputs:
- assert_equal(from_balance_before - from_wallet.getbalance(), amount)
+ assert_equal(from_balance_before - from_balance, amount)
else:
- assert_greater_than(from_balance_before - from_wallet.getbalance(), amount)
+ assert_greater_than(from_balance_before - from_balance, amount)
else:
assert next((out for out in tx["vout"] if out["scriptPubKey"]["asm"] == "OP_RETURN 35"), None)
else:
- assert_equal(from_balance_before, from_wallet.getbalance())
+ assert_equal(from_balance_before, from_balance)
if to_wallet:
self.sync_mempools()
@@ -343,22 +352,41 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=0.1, arg_estimate_mode=mode, expect_error=(-8, msg))
assert_raises_rpc_error(-8, msg, w0.send, {w1.getnewaddress(): 1}, 0.1, mode)
- for mode in ["economical", "conservative", "btc/kb", "sat/b"]:
- self.log.debug("{}".format(mode))
- for k, v in {"string": "true", "object": {"foo": "bar"}}.items():
+ for mode in ["economical", "conservative"]:
+ for k, v in {"string": "true", "bool": True, "object": {"foo": "bar"}}.items():
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode,
- expect_error=(-3, "Expected type number for conf_target, got {}".format(k)))
+ expect_error=(-3, f"Expected type number for conf_target, got {k}"))
- # Test setting explicit fee rate just below the minimum and at zero.
+ # Test setting explicit fee rate just below the minimum of 1 sat/vB.
self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed")
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.99999999,
- expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.99999999,
- expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0,
- expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0,
- expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
+ msg = "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.999, expect_error=(-4, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.999, expect_error=(-4, msg))
+
+ self.log.info("Explicit fee rate raises if invalid fee_rate is passed")
+ # Test fee_rate with zero values.
+ msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
+ for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=zero_value, expect_error=(-4, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=zero_value, expect_error=(-4, msg))
+ msg = "Invalid amount"
+ # Test fee_rate values that don't pass fixed-point parsing checks.
+ for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
+ # Test fee_rate values that cannot be represented in sat/vB.
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
+ # Test fee_rate out of range (negative number).
+ msg = "Amount out of range"
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=-1, expect_error=(-3, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=-1, expect_error=(-3, msg))
+ # Test type error.
+ msg = "Amount is not a number or string"
+ for invalid_value in [True, {"foo": "bar"}]:
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
# TODO: Return hex if fee rate is below -maxmempool
# res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)
@@ -440,6 +468,14 @@ class WalletSendTest(BitcoinTestFramework):
self.log.info("Subtract fee from output")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
+ self.log.info("Include unsafe inputs")
+ self.nodes[1].createwallet(wallet_name="w5")
+ w5 = self.nodes[1].get_wallet_rpc("w5")
+ self.test_send(from_wallet=w0, to_wallet=w5, amount=2)
+ self.test_send(from_wallet=w5, to_wallet=w0, amount=1, expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
+ assert res["complete"]
+
if __name__ == '__main__':
WalletSendTest().main()
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index 9dd080dca9..afd4fd3691 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -5,6 +5,7 @@
"""Test external signer.
Verify that a bitcoind node can use an external signer command
+See also rpc_signer.py for tests without wallet context.
"""
import os
import platform
@@ -16,7 +17,7 @@ from test_framework.util import (
)
-class SignerTest(BitcoinTestFramework):
+class WalletSignerTest(BitcoinTestFramework):
def mock_signer_path(self):
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py')
if platform.system() == "Windows":
@@ -25,18 +26,16 @@ class SignerTest(BitcoinTestFramework):
return path
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 2
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()
+ self.skip_if_no_wallet()
def set_mock_result(self, node, res):
with open(os.path.join(node.cwd, "mock_result"), "w", encoding="utf8") as f:
@@ -48,28 +47,6 @@ class SignerTest(BitcoinTestFramework):
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)
@@ -81,11 +58,6 @@ class SignerTest(BitcoinTestFramework):
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')
@@ -123,14 +95,14 @@ class SignerTest(BitcoinTestFramework):
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)
+ self.log.info('Test walletdisplayaddress')
+ result = hww.walletdisplayaddress(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
+ hww.walletdisplayaddress, address1
)
self.clear_mock_result(self.nodes[1])
@@ -214,4 +186,4 @@ class SignerTest(BitcoinTestFramework):
# self.clear_mock_result(self.nodes[4])
if __name__ == '__main__':
- SignerTest().main()
+ WalletSignerTest().main()
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
new file mode 100755
index 0000000000..65ca7bdef7
--- /dev/null
+++ b/test/functional/wallet_taproot.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+# 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.
+"""Test generation and spending of P2TR addresses."""
+
+import random
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+from test_framework.descriptors import descsum_create
+from test_framework.script import (CScript, OP_CHECKSIG, taproot_construct)
+from test_framework.segwit_addr import encode_segwit_address
+
+# xprvs/xpubs, and m/* derived x-only pubkeys (created using independent implementation)
+KEYS = [
+ {
+ "xprv": "tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV",
+ "xpub": "tpubD6NzVbkrYhZ4XqNGAWGWSzmxGWFwVjVTjZxh2fioKbVYi7Jx8fdbprVWsdW7mHwqjchBVas8TLZG4Xwuz4RKU4iaCqiCvoSkFCzQptqk5Y1",
+ "pubs": [
+ "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18",
+ "a30253b018ea6fca966135bf7dd8026915427f24ccf10d4e03f7870f4128569b",
+ "a61e5749f2f3db9dc871d7b187e30bfd3297eea2557e9be99897ea8ff7a29a21",
+ "8110cf482f66dc37125e619d73075af932521724ffc7108309e88f361efe8c8a",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe98QUPieXy5KFPVjuZNpcC9JY7K7buJEm8nWvJogK4kTda7eLjK9U4PnMNbSjEkpjDJazeBZ4rhYNYD7N6GEdaysj1AYSb5",
+ "xpub": "tpubD6NzVbkrYhZ4XcACN3PEwNjRpR1g4tZjBVk5pdMR2B6dbd3HYhdGVZNKofAiFZd9okBserZvv58A6tBX4pE64UpXGNTSesfUW7PpW36HuKz",
+ "pubs": [
+ "f95886b02a84928c5c15bdca32784993105f73de27fa6ad8c1a60389b999267c",
+ "71522134160685eb779857033bfc84c7626f13556154653a51dd42619064e679",
+ "48957b4158b2c5c3f4c000f51fd2cf0fd5ff8868ebfb194256f5e9131fc74bd8",
+ "086dda8139b3a84944010648d2b674b70447be3ae59322c09a4907bc80be62c1",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe3ZJmcj9aJ2EPZJYYCh6Lp3v82p75wspgaXmtDZ2RBtkAtWcGnW2VQDzMHQPBkCKMoYTqh1RfJKjv4PcmWVR7KqTpjsdboN",
+ "xpub": "tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy",
+ "pubs": [
+ "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "8a104c54cd34acba60c97dd8f1f7abc89ba9587afd88dc928e91aca7b1c50d20",
+ "13ba6b252a4eb5ef31d39cb521724cdab19a698323f5c17093f28fb1821d052f",
+ "f6c2b4863fd5ba1ba09e3a890caed8b75ffbe013ebab31a06ab87cd6f72506af",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdKziibn63Rm6aNzp7dSjDnufZMStXr71Huz7iihCRpbZZZ6Voy5HyuHCWx6foHMipzMzUq4tZrtkZ24DJwz5EeNWdsuwX5h",
+ "xpub": "tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR",
+ "pubs": [
+ "03a669ea926f381582ec4a000b9472ba8a17347f5fb159eddd4a07036a6718eb",
+ "bbf56b14b119bccafb686adec2e3d2a6b51b1626213590c3afa815d1fd36f85d",
+ "2994519e31bbc238a07d82f85c9832b831705d2ee4a2dbb477ecec8a3f570fe5",
+ "68991b5c139a4c479f8c89d6254d288c533aefc0c5b91fac6c89019c4de64988",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPen4PGtDwURYnCtVMDejyE8vVwMGhQWfVqB2FBPdekhTacDW4vmsKTsgC1wsncVqXiZdX2YFGAnKoLXYf42M78fQJFzuDYFN",
+ "xpub": "tpubD6NzVbkrYhZ4YF6BAXtXsqCtmv1HNyvsoSXHDsJzpnTtffH1onTEwC5SnLzCHPKPebh2i7Gxvi9kJNADcpuSmH8oM3rCYcHVtdXHjpYoKnX",
+ "pubs": [
+ "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
+ "c8558b7caf198e892032d91f1a48ee9bdc25462b83b4d0ac62bb7fb2a0df630e",
+ "8a4bcaba0e970685858d133a4d0079c8b55bbc755599e212285691eb779ce3dc",
+ "b0d68ada13e0d954b3921b88160d4453e9c151131c2b7c724e08f538a666ceb3",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPd91vCgRmbzA13wyip2RimYeVEkAyZvsEN5pUSB3T43SEBxPsytkxb42d64W2EiRE9CewpJQkzR8HKHLV8Uhk4dMF5yRPaTv",
+ "xpub": "tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6",
+ "pubs": [
+ "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744",
+ "8e971b781b7ce7ab742d80278f2dfe7dd330f3efd6d00047f4a2071f2e7553cb",
+ "b811d66739b9f07435ccda907ec5cd225355321c35e0a7c7791232f24cf10632",
+ "4cd27a5552c272bc80ba544e9cc6340bb906969f5e7a1510b6cef9592683fbc9",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdEhLRxxwzTv2t18j7ruoffPeqAwVA2qXJ2P66RaMZLUWQ85SjoA7xPxdSgCB9UZ72m65qbnaLPtFTfHVP3MEmkpZk1Bv8RT",
+ "xpub": "tpubD6NzVbkrYhZ4Whj8KcdYPsa9T2efHC6iExzS7gynaJdv8WdripPwjq6NaH5gQJGrLmvUwHY1smhiakUosXNDTEa6qfKUQdLKV6DJBre6XvQ",
+ "pubs": [
+ "d0c19def28bb1b39451c1a814737615983967780d223b79969ba692182c6006b",
+ "cb1d1b1dc62fec1894d4c3d9a1b6738e5ff9c273a64f74e9ab363095f45e9c47",
+ "245be588f41acfaeb9481aa132717db56ee1e23eb289729fe2b8bde8f9a00830",
+ "5bc4ad6d6187fa82728c85a073b428483295288f8aef5722e47305b5872f7169",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPcxbqxzcMAwQpiCD8x6qaZEJTxdKxw4w9GuMzDACTD9yhEsHGfqQcfYX4LivosLDDngTykYEp9JnTdcqY7cHqU8PpeFFKyV3",
+ "xpub": "tpubD6NzVbkrYhZ4WRddreGwaM4wHDj57S2V8XuFF9NGMLjY7PckqZ23PebZR1wGA4w84uX2vZphdZVsnREjij1ibYjEBTaTVQCEZCLs4xUDapx",
+ "pubs": [
+ "065cc1b92bd99e5a3e626e8296a366b2d132688eb43aea19bc14fd8f43bf07fb",
+ "5b95633a7dda34578b6985e6bfd85d83ec38b7ded892a9b74a3d899c85890562",
+ "dc86d434b9a34495c8e845b969d51f80d19a8df03b400353ffe8036a0c22eb60",
+ "06c8ffde238745b29ae8a97ae533e1f3edf214bba6ec58b5e7b9451d1d61ec19",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe6zLoU8MTTXgsdJVNBErrYGpoGwHf5VGvwUzdNc7NHeCSzkJkniCxBhZWujXjmD4HZmBBrnr3URgJjM6GxRgMmEhLdqNTWG",
+ "xpub": "tpubD6NzVbkrYhZ4Xa28h7nwrsBoSepRXWRmRqsc5nyb5MHfmRjmFmRhYnG4d9dC7uxixN5AfsEv1Lz3mCAuWvERyvPgKozHUVjfo8EG6foJGy7",
+ "pubs": [
+ "d826a0a53abb6ffc60df25b9c152870578faef4b2eb5a09bdd672bbe32cdd79b",
+ "939365e0359ff6bc6f6404ee220714c5d4a0d1e36838b9e2081ede217674e2ba",
+ "4e8767edcf7d3d90258cfbbea01b784f4d2de813c4277b51279cf808bac410a2",
+ "d42a2c280940bfc6ede971ae72cde2e1df96c6da7dab06a132900c6751ade208",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPeB5o5oCsN2dVxM2mtJiYERQEBRc4JNwC1DFGYaEdNkmh8jJYVPU76YhkFoRoWTdh1p3yQGykG8TfDW34dKgrgSx28gswUyL",
+ "xpub": "tpubD6NzVbkrYhZ4Xe7aySsTmSHcXNYi3duSoj11TweMiejaqhW3Ay4DZFPZJses4sfpk4b9VHRhn8v4cKTMjugMM3hqXcqSSmRdiW8QvASXjfY",
+ "pubs": [
+ "e360564b2e0e8d06681b6336a29d0750210e8f34afd9afb5e6fd5fe6dba26c81",
+ "76b4900f00a1dcce463b6d8e02b768518fce4f9ecd6679a13ad78ea1e4815ad3",
+ "5575556e263c8ed52e99ab02147cc05a738869afe0039911b5a60a780f4e43d2",
+ "593b00e2c8d4bd6dda0fd9e238888acf427bb4e128887fd5a40e0e9da78cbc01",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPfEH6jHemkGDjZRnAaKFJVGH8pQU638E6SdbX9hxit1tK2sfFPfL6KS7v8FfUKxstbfEpzSymbdfBM9Y5UkrxErF9fJaKLK3",
+ "xpub": "tpubD6NzVbkrYhZ4YhJtcwKN9fsr8TJ6jeSD4Zsv6vWPTQ2VH7rHn6nK4WWBCzKK7FkdVVwm3iztCU1UmStY4hX6gRbBmp9UzK9C59dQEzeXS12",
+ "pubs": [
+ "7631cacec3343052d87ef4d0065f61dde82d7d2db0c1cc02ef61ef3c982ea763",
+ "c05e44a9e735d1b1bef62e2c0d886e6fb4923b2649b67828290f5cacc51c71b7",
+ "b33198b20701afe933226c92fd0e3d51d3f266f1113d864dbd026ae3166ef7f2",
+ "f99643ac3f4072ee4a949301e86963a9ca0ad57f2ef29f6b84fda037d7cac85b",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdNWU38dT6aGxtqJR4oYS5kPpLVBcuKiiu7gqTYqMMqhUG6DP7pPahzPQu36sWSmeLCP1C4AwqcR5FX2RyRoZfd4B8pAnSdX",
+ "xpub": "tpubD6NzVbkrYhZ4WqYFvnJ3Vyw5TrpME8jLf3zbd1DvKbX7jbwc5wewYLKLSFRzZWV6hZj7XhsXAy7fhE5jB25DiWyNM3ztXbsXHRVCrp5BiPY",
+ "pubs": [
+ "2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7",
+ "83df59d0a5c951cdd62b7ab225a62079f48d2a333a86e66c35420d101446e92e",
+ "2a654bf234d819055312f9ca03fad5836f9163b09cdd24d29678f694842b874a",
+ "aa0334ab910047387c912a21ec0dab806a47ffa38365060dbc5d47c18c6e66e7",
+ ]
+ },
+ {
+ "xprv": "tprv8mGPkMVz5mZuJDnC2NjjAv7E9Zqa5LCgX4zawbZu5nzTtLb5kGhPwycX4H1gtW1f5ZdTKTNtQJ61hk71F2TdcQ93EFDTpUcPBr98QRji615",
+ "xpub": "tpubDHxRtmYEE9FaBgoyv2QKaKmLibMWEfPb6NbNE7cCW4nripqrNfWz8UEPEPbHCrakwLvwFfsqoaf4pjX4gWStp4nECRf1QwBKPkLqnY8pHbj",
+ "pubs": [
+ "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d3",
+ "b2749b74d51a78f5fe3ebb3a7c0ff266a468cade143dfa265c57e325177edf00",
+ "6b8747a6bbe4440d7386658476da51f6e49a220508a7ec77fe7bccc3e7baa916",
+ "4674bf4d9ebbe01bf0aceaca2472f63198655ecf2df810f8d69b38421972318e",
+ ]
+ }
+]
+
+CHANGE_XPRV = "tprv8ZgxMBicQKsPcyDrWwiecVnTtFmfRwbfFqEfR4ZGWvq5aTTwLBWmAm5zrbMcYtb9gQNFfhRfqhhrBG37U3nhmXxEgeEPBJGHAPrHCrAd1WX"
+CHANGE_XPUB = "tpubD6NzVbkrYhZ4WSFeQbPF1uSaTHHbbGnZq8qShabZwCdUQwihxaLMMFhs2kidGF2qrRKiQVqw8VoyuTHj1bZqmMXMeciaU1gBjWA1sim2zUB"
+
+# Point with no known discrete log.
+H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
+
+
+def key(hex_key):
+ """Construct an x-only pubkey from its hex representation."""
+ return bytes.fromhex(hex_key)
+
+def pk(hex_key):
+ """Construct a script expression for taproot_construct for pk(hex_key)."""
+ return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
+
+def compute_taproot_address(pubkey, scripts):
+ """Compute the address for a taproot output with given inner key and scripts."""
+ tap = taproot_construct(pubkey, scripts)
+ assert tap.scriptPubKey[0] == 0x51
+ assert tap.scriptPubKey[1] == 0x20
+ return encode_segwit_address("bcrt", 1, tap.scriptPubKey[2:])
+
+class WalletTaprootTest(BitcoinTestFramework):
+ """Test generation and spending of P2TR address outputs."""
+
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [['-keypool=100'], ['-keypool=100']]
+ self.supports_cli = False
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_sqlite()
+
+ def setup_network(self):
+ self.setup_nodes()
+
+ def init_wallet(self, i):
+ pass
+
+ @staticmethod
+ def rand_keys(n):
+ ret = []
+ idxes = set()
+ for _ in range(n):
+ while True:
+ i = random.randrange(len(KEYS))
+ if not i in idxes:
+ break
+ idxes.add(i)
+ ret.append(KEYS[i])
+ return ret
+
+ @staticmethod
+ def make_desc(pattern, privmap, keys, pub_only = False):
+ pat = pattern.replace("$H", H_POINT)
+ for i in range(len(privmap)):
+ if privmap[i] and not pub_only:
+ pat = pat.replace("$%i" % (i + 1), keys[i]['xprv'])
+ else:
+ pat = pat.replace("$%i" % (i + 1), keys[i]['xpub'])
+ return descsum_create(pat)
+
+ @staticmethod
+ def make_addr(treefn, keys, i):
+ args = []
+ for j in range(len(keys)):
+ args.append(keys[j]['pubs'][i])
+ return compute_taproot_address(*treefn(*args))
+
+ def do_test_addr(self, comment, pattern, privmap, treefn, keys):
+ self.log.info("Testing %s address derivation" % comment)
+ desc = self.make_desc(pattern, privmap, keys, False)
+ desc_pub = self.make_desc(pattern, privmap, keys, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc)['descriptor'], desc_pub)
+ result = self.addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.addr_gen.getnewaddress(address_type='bech32')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys, i)
+ assert_equal(addr_g, addr_r)
+
+ def do_test(self, comment, pattern, privmap, treefn, nkeys):
+ keys = self.rand_keys(nkeys)
+ self.do_test_addr(comment, pattern, privmap, treefn, keys)
+
+ def run_test(self):
+ self.log.info("Creating wallets...")
+ self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
+ self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")
+
+ self.do_test(
+ "tr(XPRV)",
+ "tr($1/*)",
+ [True],
+ lambda k1: (key(k1), []),
+ 1
+ )
+ self.do_test(
+ "tr(H,XPRV)",
+ "tr($H,pk($1/*))",
+ [True],
+ lambda k1: (key(H_POINT), [pk(k1)]),
+ 1
+ )
+ self.do_test(
+ "tr(XPRV,{H,{H,XPUB}})",
+ "tr($1/*,{pk($H),{pk($H),pk($2/*)}})",
+ [True, False],
+ lambda k1, k2: (key(k1), [pk(H_POINT), [pk(H_POINT), pk(k2)]]),
+ 2
+ )
+ self.do_test(
+ "tr(XPUB,{{H,{H,XPUB}},{H,{H,{H,XPRV}}}})",
+ "tr($1/*,{{pk($H),{pk($H),pk($2/*)}},{pk($H),{pk($H),{pk($H),pk($3/*)}}}})",
+ [False, False, True],
+ lambda k1, k2, k3: (key(k1), [[pk(H_POINT), [pk(H_POINT), pk(k2)]], [pk(H_POINT), [pk(H_POINT), [pk(H_POINT), pk(k3)]]]]),
+ 3
+ )
+
+if __name__ == '__main__':
+ WalletTaprootTest().main()
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index fbc0f995d2..e7dd7592b6 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -17,6 +17,7 @@ import struct
from io import BytesIO
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.bdb import dump_bdb_kv
from test_framework.messages import deser_compact_size, deser_string
from test_framework.test_framework import BitcoinTestFramework
@@ -118,11 +119,11 @@ class UpgradeWalletTest(BitcoinTestFramework):
assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
def run_test(self):
- self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
self.dumb_sync_blocks()
# # Sanity check the test framework:
res = self.nodes[0].getblockchaininfo()
- assert_equal(res['blocks'], 101)
+ assert_equal(res['blocks'], COINBASE_MATURITY + 1)
node_master = self.nodes[0]
v16_3_node = self.nodes[1]
v15_2_node = self.nodes[2]
@@ -130,7 +131,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
# Send coins to old wallets for later conversion checks.
v16_3_wallet = v16_3_node.get_wallet_rpc('wallet.dat')
v16_3_address = v16_3_wallet.getnewaddress()
- node_master.generatetoaddress(101, v16_3_address)
+ node_master.generatetoaddress(COINBASE_MATURITY + 1, v16_3_address)
self.dumb_sync_blocks()
v16_3_balance = v16_3_wallet.getbalance()
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
index c345c382d0..6743c4a49b 100755
--- a/test/functional/wallet_watchonly.py
+++ b/test/functional/wallet_watchonly.py
@@ -5,6 +5,7 @@
"""Test createwallet watchonly arguments.
"""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -36,7 +37,7 @@ class CreateWalletWatchonlyTest(BitcoinTestFramework):
wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_change)['pubkey'])
# generate some btc for testing
- node.generatetoaddress(101, a1)
+ node.generatetoaddress(COINBASE_MATURITY + 1, a1)
# send 1 btc to our watch-only address
txid = def_wallet.sendtoaddress(wo_addr, 1)
diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh
index e66adfc3ce..9fca1129b6 100755
--- a/test/lint/commit-script-check.sh
+++ b/test/lint/commit-script-check.sh
@@ -12,7 +12,7 @@
# one. Any remaining diff signals an error.
export LC_ALL=C
-if test "x$1" = "x"; then
+if test -z $1; then
echo "Usage: $0 <commit>..."
exit 1
fi
@@ -24,7 +24,7 @@ for commit in $(git rev-list --reverse $1); do
if git rev-list -n 1 --pretty="%s" $commit | grep -q "^scripted-diff:"; then
git checkout --quiet $commit^ || exit
SCRIPT="$(git rev-list --format=%b -n1 $commit | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d')"
- if test "x$SCRIPT" = "x"; then
+ if test -z "$SCRIPT"; then
echo "Error: missing script for: $commit"
echo "Failed"
RET=1
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index 0b15f99448..f8f24bb1ff 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -11,10 +11,12 @@ export LC_ALL=C
EXPECTED_CIRCULAR_DEPENDENCIES=(
"chainparamsbase -> util/system -> chainparamsbase"
"index/txindex -> validation -> index/txindex"
- "index/blockfilterindex -> validation -> index/blockfilterindex"
+ "node/blockstorage -> validation -> node/blockstorage"
+ "index/blockfilterindex -> node/blockstorage -> validation -> index/blockfilterindex"
+ "index/base -> validation -> index/blockfilterindex -> index/base"
+ "index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
"policy/fees -> txmempool -> policy/fees"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
- "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
"qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog"
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
@@ -22,6 +24,10 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
"node/coinstats -> validation -> node/coinstats"
+ # Temporary circular dependencies that allow wallet.h/wallet.cpp to be
+ # split up in a MOVEONLY commit. These are removed in #21206.
+ "wallet/receive -> wallet/wallet -> wallet/receive"
+ "wallet/spend -> wallet/wallet -> wallet/spend"
)
EXIT_CODE=0
diff --git a/test/lint/lint-filenames.sh b/test/lint/lint-filenames.sh
deleted file mode 100755
index 3f7491cd2b..0000000000
--- a/test/lint/lint-filenames.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-#
-# 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.
-#
-# Make sure only lowercase alphanumerics (a-z0-9), underscores (_),
-# hyphens (-) and dots (.) are used in source code filenames.
-
-export LC_ALL=C
-
-EXIT_CODE=0
-OUTPUT=$(git ls-files --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]" | \
- grep -vE '^[a-z0-9_./-]+$' | \
- grep -vE '^src/(secp256k1/|univalue/|test/fuzz/FuzzedDataProvider.h)')
-
-if [[ ${OUTPUT} != "" ]]; then
- echo "Use only lowercase alphanumerics (a-z0-9), underscores (_), hyphens (-) and dots (.)"
- echo "in source code filenames:"
- echo
- echo "${OUTPUT}"
- EXIT_CODE=1
-fi
-exit ${EXIT_CODE}
diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py
new file mode 100755
index 0000000000..400921e5f3
--- /dev/null
+++ b/test/lint/lint-files.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+# 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.
+
+"""
+This checks that all files in the repository have correct filenames and permissions
+"""
+
+import os
+import re
+import sys
+from subprocess import check_output
+from typing import Optional, NoReturn
+
+CMD_ALL_FILES = "git ls-files -z --full-name"
+CMD_SOURCE_FILES = 'git ls-files -z --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]"'
+CMD_SHEBANG_FILES = "git grep --full-name --line-number -I '^#!'"
+ALLOWED_FILENAME_REGEXP = "^[a-zA-Z0-9/_.@][a-zA-Z0-9/_.@-]*$"
+ALLOWED_SOURCE_FILENAME_REGEXP = "^[a-z0-9_./-]+$"
+ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXP = (
+ "^src/(secp256k1/|univalue/|test/fuzz/FuzzedDataProvider.h)"
+)
+ALLOWED_PERMISSION_NON_EXECUTABLES = 644
+ALLOWED_PERMISSION_EXECUTABLES = 755
+ALLOWED_EXECUTABLE_SHEBANG = {
+ "py": [b"#!/usr/bin/env python3"],
+ "sh": [b"#!/usr/bin/env bash", b"#!/bin/sh"],
+}
+
+
+class FileMeta(object):
+ def __init__(self, file_path: str):
+ self.file_path = file_path
+
+ @property
+ def extension(self) -> Optional[str]:
+ """
+ Returns the file extension for a given filename string.
+ eg:
+ 'ci/lint_run_all.sh' -> 'sh'
+ 'ci/retry/retry' -> None
+ 'contrib/devtools/split-debug.sh.in' -> 'in'
+ """
+ return str(os.path.splitext(self.file_path)[1].strip(".") or None)
+
+ @property
+ def full_extension(self) -> Optional[str]:
+ """
+ Returns the full file extension for a given filename string.
+ eg:
+ 'ci/lint_run_all.sh' -> 'sh'
+ 'ci/retry/retry' -> None
+ 'contrib/devtools/split-debug.sh.in' -> 'sh.in'
+ """
+ filename_parts = self.file_path.split(os.extsep, 1)
+ try:
+ return filename_parts[1]
+ except IndexError:
+ return None
+
+ @property
+ def permissions(self) -> int:
+ """
+ Returns the octal file permission of the file
+ """
+ return int(oct(os.stat(self.file_path).st_mode)[-3:])
+
+
+def check_all_filenames() -> int:
+ """
+ Checks every file in the repository against an allowed regexp to make sure only lowercase or uppercase
+ alphanumerics (a-zA-Z0-9), underscores (_), hyphens (-), at (@) and dots (.) are used in repository filenames.
+ """
+ filenames = check_output(CMD_ALL_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
+ filename_regex = re.compile(ALLOWED_FILENAME_REGEXP)
+ failed_tests = 0
+ for filename in filenames:
+ if not filename_regex.match(filename):
+ print(
+ f"""File {repr(filename)} does not not match the allowed filename regexp ('{ALLOWED_FILENAME_REGEXP}')."""
+ )
+ failed_tests += 1
+ return failed_tests
+
+
+def check_source_filenames() -> int:
+ """
+ Checks only source files (*.cpp, *.h, *.py, *.sh) against a stricter allowed regexp to make sure only lowercase
+ alphanumerics (a-z0-9), underscores (_), hyphens (-) and dots (.) are used in source code filenames.
+
+ Additionally there is an exception regexp for directories or files which are excepted from matching this regexp.
+ """
+ filenames = check_output(CMD_SOURCE_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
+ filename_regex = re.compile(ALLOWED_SOURCE_FILENAME_REGEXP)
+ filename_exception_regex = re.compile(ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXP)
+ failed_tests = 0
+ for filename in filenames:
+ if not filename_regex.match(filename) and not filename_exception_regex.match(filename):
+ print(
+ f"""File {repr(filename)} does not not match the allowed source filename regexp ('{ALLOWED_SOURCE_FILENAME_REGEXP}'), or the exception regexp ({ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXP})."""
+ )
+ failed_tests += 1
+ return failed_tests
+
+
+def check_all_file_permissions() -> int:
+ """
+ Checks all files in the repository match an allowed executable or non-executable file permission octal.
+
+ Additionally checks that for executable files, the file contains a shebang line
+ """
+ filenames = check_output(CMD_ALL_FILES, shell=True).decode("utf8").rstrip("\0").split("\0")
+ failed_tests = 0
+ for filename in filenames:
+ file_meta = FileMeta(filename)
+ if file_meta.permissions == ALLOWED_PERMISSION_EXECUTABLES:
+ with open(filename, "rb") as f:
+ shebang = f.readline().rstrip(b"\n")
+
+ # For any file with executable permissions the first line must contain a shebang
+ if not shebang.startswith(b"#!"):
+ print(
+ f"""File "{filename}" has permission {ALLOWED_PERMISSION_EXECUTABLES} (executable) and is thus expected to contain a shebang '#!'. Add shebang or do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES} {filename}" to make it non-executable."""
+ )
+ failed_tests += 1
+
+ # For certain file extensions that have been defined, we also check that the shebang conforms to a specific
+ # allowable set of shebangs
+ if file_meta.extension in ALLOWED_EXECUTABLE_SHEBANG.keys():
+ if shebang not in ALLOWED_EXECUTABLE_SHEBANG[file_meta.extension]:
+ print(
+ f"""File "{filename}" is missing expected shebang """
+ + " or ".join(
+ [
+ x.decode("utf-8")
+ for x in ALLOWED_EXECUTABLE_SHEBANG[file_meta.extension]
+ ]
+ )
+ )
+ failed_tests += 1
+
+ elif file_meta.permissions == ALLOWED_PERMISSION_NON_EXECUTABLES:
+ continue
+ else:
+ print(
+ f"""File "{filename}" has unexpected permission {file_meta.permissions}. Do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES} {filename}" (if non-executable) or "chmod {ALLOWED_PERMISSION_EXECUTABLES} {filename}" (if executable)."""
+ )
+ failed_tests += 1
+
+ return failed_tests
+
+
+def check_shebang_file_permissions() -> int:
+ """
+ Checks every file that contains a shebang line to ensure it has an executable permission
+ """
+ filenames = check_output(CMD_SHEBANG_FILES, shell=True).decode("utf8").strip().split("\n")
+
+ # The git grep command we use returns files which contain a shebang on any line within the file
+ # so we need to filter the list to only files with the shebang on the first line
+ filenames = [filename.split(":1:")[0] for filename in filenames if ":1:" in filename]
+
+ failed_tests = 0
+ for filename in filenames:
+ file_meta = FileMeta(filename)
+ if file_meta.permissions != ALLOWED_PERMISSION_EXECUTABLES:
+ # These file types are typically expected to be sourced and not executed directly
+ if file_meta.full_extension in ["bash", "init", "openrc", "sh.in"]:
+ continue
+
+ # *.py files which don't contain an `if __name__ == '__main__'` are not expected to be executed directly
+ if file_meta.extension == "py":
+ with open(filename, "r", encoding="utf8") as f:
+ file_data = f.read()
+ if not re.search("""if __name__ == ['"]__main__['"]:""", file_data):
+ continue
+
+ print(
+ f"""File "{filename}" contains a shebang line, but has the file permission {file_meta.permissions} instead of the expected executable permission {ALLOWED_PERMISSION_EXECUTABLES}. Do "chmod {ALLOWED_PERMISSION_EXECUTABLES} {filename}" (or remove the shebang line)."""
+ )
+ failed_tests += 1
+ return failed_tests
+
+
+def main() -> NoReturn:
+ failed_tests = 0
+ failed_tests += check_all_filenames()
+ failed_tests += check_source_filenames()
+ failed_tests += check_all_file_permissions()
+ failed_tests += check_shebang_file_permissions()
+
+ if failed_tests:
+ print(
+ f"ERROR: There were {failed_tests} failed tests in the lint-files.py lint test. Please resolve the above errors."
+ )
+ sys.exit(1)
+ else:
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-files.sh b/test/lint/lint-files.sh
new file mode 100755
index 0000000000..1e115778bd
--- /dev/null
+++ b/test/lint/lint-files.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+export LC_ALL=C
+
+set -e
+cd "$(dirname $0)/../.."
+test/lint/lint-files.py
diff --git a/test/lint/lint-include-guards.sh b/test/lint/lint-include-guards.sh
index 5cfa41537f..c23b903bce 100755
--- a/test/lint/lint-include-guards.sh
+++ b/test/lint/lint-include-guards.sh
@@ -15,7 +15,7 @@ REGEXP_EXCLUDE_FILES_WITH_PREFIX="src/(crypto/ctaes/|leveldb/|crc32c/|secp256k1/
EXIT_CODE=0
for HEADER_FILE in $(git ls-files -- "*.h" | grep -vE "^${REGEXP_EXCLUDE_FILES_WITH_PREFIX}")
do
- HEADER_ID_BASE=$(cut -f2- -d/ <<< "${HEADER_FILE}" | sed "s/\.h$//g" | tr / _ | tr "[:lower:]" "[:upper:]")
+ HEADER_ID_BASE=$(cut -f2- -d/ <<< "${HEADER_FILE}" | sed "s/\.h$//g" | tr / _ | tr - _ | tr "[:lower:]" "[:upper:]")
HEADER_ID="${HEADER_ID_PREFIX}${HEADER_ID_BASE}${HEADER_ID_SUFFIX}"
if [[ $(grep -cE "^#(ifndef|define) ${HEADER_ID}" "${HEADER_FILE}") != 2 ]]; then
echo "${HEADER_FILE} seems to be missing the expected include guard:"
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index e5657f7555..737d35a397 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -43,7 +43,7 @@ KNOWN_VIOLATIONS=(
"src/dbwrapper.cpp.*stoul"
"src/dbwrapper.cpp:.*vsnprintf"
"src/httprpc.cpp.*trim"
- "src/init.cpp:.*atoi"
+ "src/node/blockstorage.cpp:.*atoi"
"src/qt/rpcconsole.cpp:.*atoi"
"src/rest.cpp:.*strtol"
"src/test/dbwrapper_tests.cpp:.*snprintf"
diff --git a/test/lint/lint-shebang.sh b/test/lint/lint-shebang.sh
deleted file mode 100755
index 13ebdfd78a..0000000000
--- a/test/lint/lint-shebang.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2018-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.
-
-# Assert expected shebang lines
-
-export LC_ALL=C
-EXIT_CODE=0
-for PYTHON_FILE in $(git ls-files -- "*.py"); do
- if [[ $(head -c 2 "${PYTHON_FILE}") == "#!" &&
- $(head -n 1 "${PYTHON_FILE}") != "#!/usr/bin/env python3" ]]; then
- echo "Missing shebang \"#!/usr/bin/env python3\" in ${PYTHON_FILE} (do not use python or python2)"
- EXIT_CODE=1
- fi
-done
-for SHELL_FILE in $(git ls-files -- "*.sh"); do
- if [[ $(head -n 1 "${SHELL_FILE}") != "#!/usr/bin/env bash" &&
- $(head -n 1 "${SHELL_FILE}") != "#!/bin/sh" ]]; then
- echo "Missing expected shebang \"#!/usr/bin/env bash\" or \"#!/bin/sh\" in ${SHELL_FILE}"
- EXIT_CODE=1
- fi
-done
-exit ${EXIT_CODE}
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index 78ffe4def3..267c06ea20 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -6,6 +6,7 @@ fpr
hights
hist
inout
+invokable
mor
nin
ser
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 3fc9fac25c..7db051ca37 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -3,32 +3,12 @@
#
# https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
-# double locks (TODO fix)
-mutex:g_genesis_wait_mutex
-mutex:Interrupt
-mutex:CThreadInterrupt
-mutex:CConnman::Interrupt
-mutex:CConnman::WakeMessageHandler
-mutex:CConnman::ThreadOpenConnections
-mutex:CConnman::ThreadOpenAddedConnections
-mutex:CConnman::SocketHandler
-mutex:UpdateTip
-mutex:PeerManagerImpl::UpdatedBlockTip
-mutex:g_best_block_mutex
-
# race (TODO fix)
-race:CConnman::WakeMessageHandler
-race:CConnman::ThreadMessageHandler
-race:fHaveGenesis
-race:ProcessNewBlock
-race:ThreadImport
race:LoadWallet
race:WalletBatch::WriteHDChain
race:BerkeleyBatch
race:BerkeleyDatabase
race:DatabaseBatch
-race:leveldb::DBImpl::DeleteObsoleteFiles
-race:validation_chainstatemanager_tests
race:zmq::*
race:bitcoin-qt
@@ -36,12 +16,16 @@ race:bitcoin-qt
deadlock:CChainState::ConnectTip
# Intentional deadlock in tests
-deadlock:TestPotentialDeadLockDetected
+deadlock:sync_tests::potential_deadlock_detected
# Wildcard for all gui tests, should be replaced with non-wildcard suppressions
race:src/qt/test/*
deadlock:src/qt/test/*
+# Race in src/test/main.cpp
+# Can be removed once upgraded to boost test 1.74 in depends
+race:validation_chainstatemanager_tests
+
# External libraries
deadlock:libdb
race:libzmq
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 97f0f45e7f..2850cfcea5 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,6 +1,12 @@
# -fsanitize=undefined suppressions
# =================================
-# No suppressions at the moment. Hooray!
+# This would be `signed-integer-overflow:CTxMemPool::PrioritiseTransaction`,
+# however due to a bug in clang the symbolizer is disabled and thus no symbol
+# names can be used.
+# See https://github.com/google/sanitizers/issues/1364
+signed-integer-overflow:txmempool.cpp
+# https://github.com/bitcoin/bitcoin/pull/21798#issuecomment-829180719
+signed-integer-overflow:policy/feerate.cpp
# -fsanitize=integer suppressions
# ===============================
@@ -56,7 +62,6 @@ implicit-integer-sign-change:key.cpp
implicit-integer-sign-change:noui.cpp
implicit-integer-sign-change:policy/fees.cpp
implicit-integer-sign-change:prevector.h
-implicit-integer-sign-change:protocol.cpp
implicit-integer-sign-change:script/bitcoinconsensus.cpp
implicit-integer-sign-change:script/interpreter.cpp
implicit-integer-sign-change:serialize.h
@@ -81,6 +86,7 @@ implicit-signed-integer-truncation:chain.h
implicit-signed-integer-truncation:crypto/
implicit-signed-integer-truncation:cuckoocache.h
implicit-signed-integer-truncation:leveldb/
+implicit-signed-integer-truncation:miner.cpp
implicit-signed-integer-truncation:net.cpp
implicit-signed-integer-truncation:net_processing.cpp
implicit-signed-integer-truncation:streams.h