aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml10
-rw-r--r--.cirrus.yml14
-rw-r--r--.github/ISSUE_TEMPLATE/good_first_issue.md4
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml8
-rw-r--r--.tx/config2
-rw-r--r--CODEOWNERS136
-rw-r--r--Makefile.am15
-rw-r--r--build-aux/m4/ax_boost_thread.m454
-rw-r--r--build-aux/m4/ax_pthread.m4228
-rw-r--r--build-aux/m4/bitcoin_qt.m490
-rw-r--r--build_msvc/.gitignore1
-rw-r--r--build_msvc/README.md28
-rw-r--r--build_msvc/common.init.vcxproj11
-rw-r--r--build_msvc/libsecp256k1/libsecp256k1.vcxproj2
-rw-r--r--build_msvc/vcpkg-packages.txt1
-rw-r--r--build_msvc/vcpkg.json20
-rwxr-xr-xci/lint/06_script.sh6
-rwxr-xr-xci/test/00_setup_env.sh4
-rw-r--r--ci/test/00_setup_env_arm.sh2
-rw-r--r--ci/test/00_setup_env_native_asan.sh3
-rw-r--r--ci/test/00_setup_env_native_msan.sh2
-rw-r--r--ci/test/00_setup_env_native_qt5.sh2
-rw-r--r--ci/test/00_setup_env_native_tsan.sh2
-rw-r--r--ci/test/00_setup_env_native_valgrind.sh4
-rwxr-xr-xci/test/04_install.sh5
-rwxr-xr-xci/test/05_before_script.sh2
-rwxr-xr-xci/test/06_script_b.sh6
-rw-r--r--configure.ac110
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml2
-rw-r--r--contrib/linearize/example-linearize.cfg8
-rwxr-xr-xcontrib/testgen/gen_key_io_test_vectors.py9
-rwxr-xr-x[-rw-r--r--]contrib/zmq/zmq_sub.py19
-rw-r--r--depends/Makefile5
-rw-r--r--depends/README.md4
-rw-r--r--depends/funcs.mk15
-rw-r--r--depends/packages/bdb.mk4
-rw-r--r--depends/packages/boost.mk4
-rw-r--r--depends/packages/fontconfig.mk12
-rw-r--r--depends/packages/miniupnpc.mk3
-rw-r--r--depends/packages/native_cctools.mk4
-rw-r--r--depends/packages/native_cdrkit.mk2
-rw-r--r--depends/packages/native_libdmg-hfsplus.mk2
-rw-r--r--depends/packages/packages.mk3
-rw-r--r--depends/packages/qt.mk13
-rw-r--r--depends/packages/sqlite.mk26
-rw-r--r--depends/packages/zeromq.mk8
-rw-r--r--depends/patches/bdb/clang_cxx_11.patch147
-rw-r--r--depends/patches/boost/unused_var_in_process.patch22
-rw-r--r--depends/patches/fontconfig/gperf_header_regen.patch24
-rw-r--r--depends/patches/fontconfig/remove_char_width_usage.patch62
-rw-r--r--depends/patches/miniupnpc/dont_use_wingen.patch26
-rw-r--r--depends/patches/native_cctools/ld64_disable_threading.patch26
-rw-r--r--depends/patches/qt/dont_hardcode_pwd.patch27
-rw-r--r--depends/patches/qt/drop_lrelease_dependency.patch20
-rw-r--r--depends/patches/qt/fix_powerpc_libpng.patch23
-rw-r--r--depends/patches/qt/freetype_back_compat.patch28
-rw-r--r--depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch30
-rw-r--r--depends/patches/zeromq/0002-disable-pthread_set_name_np.patch35
-rw-r--r--depends/patches/zeromq/remove_libstd_link.patch25
-rw-r--r--doc/bips.md5
-rw-r--r--doc/build-openbsd.md13
-rw-r--r--doc/build-osx.md4
-rw-r--r--doc/build-unix.md11
-rw-r--r--doc/dependencies.md2
-rw-r--r--doc/developer-notes.md47
-rw-r--r--doc/files.md60
-rw-r--r--doc/release-notes-16525.md9
-rw-r--r--doc/release-notes.md149
-rw-r--r--doc/tor.md9
-rw-r--r--doc/zmq.md52
-rw-r--r--src/Makefile.am34
-rw-r--r--src/Makefile.bench.include4
-rw-r--r--src/Makefile.qt.include8
-rw-r--r--src/Makefile.qt_locale.include5
-rw-r--r--src/Makefile.qttest.include4
-rw-r--r--src/Makefile.test.include346
-rw-r--r--src/addrdb.cpp20
-rw-r--r--src/addrdb.h20
-rw-r--r--src/addrman.cpp12
-rw-r--r--src/addrman.h72
-rw-r--r--src/banman.cpp2
-rw-r--r--src/base58.cpp27
-rw-r--r--src/base58.h28
-rw-r--r--src/bench/addrman.cpp2
-rw-r--r--src/bench/base58.cpp6
-rw-r--r--src/bench/bench.cpp5
-rw-r--r--src/bench/block_assemble.cpp2
-rw-r--r--src/bench/checkblock.cpp3
-rw-r--r--src/bench/checkqueue.cpp9
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/crypto_hash.cpp11
-rw-r--r--src/bench/wallet_balance.cpp2
-rw-r--r--src/bitcoin-cli.cpp196
-rw-r--r--src/bitcoin-tx.cpp17
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/bitcoind.cpp49
-rw-r--r--src/chain.h6
-rw-r--r--src/chainparams.cpp157
-rw-r--r--src/chainparams.h7
-rw-r--r--src/chainparamsbase.cpp28
-rw-r--r--src/chainparamsbase.h10
-rw-r--r--src/compat.h3
-rw-r--r--src/consensus/params.h10
-rw-r--r--src/consensus/validation.h27
-rw-r--r--src/core_read.cpp29
-rw-r--r--src/core_write.cpp5
-rw-r--r--src/crypto/common.h7
-rw-r--r--src/crypto/sha3.cpp161
-rw-r--r--src/crypto/sha3.h41
-rw-r--r--src/crypto/siphash.cpp2
-rw-r--r--src/crypto/siphash.h2
-rw-r--r--src/dummywallet.cpp36
-rw-r--r--src/hash.cpp17
-rw-r--r--src/hash.h40
-rw-r--r--src/index/base.cpp9
-rw-r--r--src/index/base.h16
-rw-r--r--src/index/disktxpos.h35
-rw-r--r--src/index/txindex.cpp32
-rw-r--r--src/init.cpp485
-rw-r--r--src/init.h19
-rw-r--r--src/interfaces/chain.cpp59
-rw-r--r--src/interfaces/chain.h24
-rw-r--r--src/interfaces/node.cpp73
-rw-r--r--src/interfaces/node.h75
-rw-r--r--src/interfaces/wallet.cpp64
-rw-r--r--src/interfaces/wallet.h34
-rw-r--r--src/key.cpp4
-rw-r--r--src/limitedmap.h100
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp530
-rw-r--r--src/net.h312
-rw-r--r--src/net_permissions.cpp2
-rw-r--r--src/net_permissions.h1
-rw-r--r--src/net_processing.cpp1467
-rw-r--r--src/net_processing.h82
-rw-r--r--src/netaddress.cpp781
-rw-r--r--src/netaddress.h357
-rw-r--r--src/netbase.cpp5
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h11
-rw-r--r--src/node/transaction.cpp41
-rw-r--r--src/policy/fees.cpp112
-rw-r--r--src/policy/fees.h4
-rw-r--r--src/policy/policy.cpp34
-rw-r--r--src/policy/policy.h8
-rw-r--r--src/policy/rbf.cpp6
-rw-r--r--src/policy/rbf.h22
-rw-r--r--src/primitives/transaction.h10
-rw-r--r--src/protocol.cpp44
-rw-r--r--src/protocol.h62
-rw-r--r--src/pubkey.cpp24
-rw-r--r--src/pubkey.h24
-rw-r--r--src/qt/bitcoin.cpp100
-rw-r--r--src/qt/bitcoin.h20
-rw-r--r--src/qt/bitcoin_locale.qrc3
-rw-r--r--src/qt/bitcoingui.cpp28
-rw-r--r--src/qt/bitcoingui.h4
-rw-r--r--src/qt/bitcoinstrings.cpp34
-rw-r--r--src/qt/clientmodel.cpp4
-rw-r--r--src/qt/forms/debugwindow.ui2
-rw-r--r--src/qt/forms/receivecoinsdialog.ui6
-rw-r--r--src/qt/guiconstants.h1
-rw-r--r--src/qt/guiutil.cpp7
-rw-r--r--src/qt/intro.cpp9
-rw-r--r--src/qt/intro.h2
-rw-r--r--src/qt/locale/bitcoin_af.ts2678
-rw-r--r--src/qt/locale/bitcoin_ar.ts46
-rw-r--r--src/qt/locale/bitcoin_bn.ts3746
-rw-r--r--src/qt/locale/bitcoin_ca.ts435
-rw-r--r--src/qt/locale/bitcoin_cs.ts266
-rw-r--r--src/qt/locale/bitcoin_da.ts8
-rw-r--r--src/qt/locale/bitcoin_de.ts24
-rw-r--r--src/qt/locale/bitcoin_el.ts8
-rw-r--r--src/qt/locale/bitcoin_en.ts825
-rw-r--r--src/qt/locale/bitcoin_en_GB.ts166
-rw-r--r--src/qt/locale/bitcoin_es.ts144
-rw-r--r--src/qt/locale/bitcoin_es_CL.ts2
-rw-r--r--src/qt/locale/bitcoin_es_CO.ts10
-rw-r--r--src/qt/locale/bitcoin_es_MX.ts712
-rw-r--r--src/qt/locale/bitcoin_et.ts54
-rw-r--r--src/qt/locale/bitcoin_eu.ts38
-rw-r--r--src/qt/locale/bitcoin_fa.ts222
-rw-r--r--src/qt/locale/bitcoin_fi.ts204
-rw-r--r--src/qt/locale/bitcoin_fr.ts8
-rw-r--r--src/qt/locale/bitcoin_gl_ES.ts (renamed from src/qt/locale/bitcoin_tr.ts)1651
-rw-r--r--src/qt/locale/bitcoin_he.ts211
-rw-r--r--src/qt/locale/bitcoin_hi.ts164
-rw-r--r--src/qt/locale/bitcoin_hu.ts106
-rw-r--r--src/qt/locale/bitcoin_id.ts496
-rw-r--r--src/qt/locale/bitcoin_is.ts4
-rw-r--r--src/qt/locale/bitcoin_it.ts8
-rw-r--r--src/qt/locale/bitcoin_ja.ts112
-rw-r--r--src/qt/locale/bitcoin_km.ts345
-rw-r--r--src/qt/locale/bitcoin_ko.ts206
-rw-r--r--src/qt/locale/bitcoin_lt.ts104
-rw-r--r--src/qt/locale/bitcoin_lv.ts12
-rw-r--r--src/qt/locale/bitcoin_ml.ts276
-rw-r--r--src/qt/locale/bitcoin_mr_IN.ts76
-rw-r--r--src/qt/locale/bitcoin_my.ts20
-rw-r--r--src/qt/locale/bitcoin_nb.ts340
-rw-r--r--src/qt/locale/bitcoin_ne.ts12
-rw-r--r--src/qt/locale/bitcoin_nl.ts8
-rw-r--r--src/qt/locale/bitcoin_pl.ts126
-rw-r--r--src/qt/locale/bitcoin_pt.ts8
-rw-r--r--src/qt/locale/bitcoin_pt_BR.ts12
-rw-r--r--src/qt/locale/bitcoin_ro.ts136
-rw-r--r--src/qt/locale/bitcoin_ru.ts188
-rw-r--r--src/qt/locale/bitcoin_si.ts180
-rw-r--r--src/qt/locale/bitcoin_sk.ts126
-rw-r--r--src/qt/locale/bitcoin_sl.ts22
-rw-r--r--src/qt/locale/bitcoin_sq.ts12
-rw-r--r--src/qt/locale/bitcoin_sr.ts2687
-rw-r--r--src/qt/locale/bitcoin_sr@latin.ts40
-rw-r--r--src/qt/locale/bitcoin_sv.ts172
-rw-r--r--src/qt/locale/bitcoin_te.ts52
-rw-r--r--src/qt/locale/bitcoin_th.ts4
-rw-r--r--src/qt/locale/bitcoin_uk.ts14
-rw-r--r--src/qt/locale/bitcoin_ur.ts4
-rw-r--r--src/qt/locale/bitcoin_vi.ts260
-rw-r--r--src/qt/locale/bitcoin_zh.ts108
-rw-r--r--src/qt/locale/bitcoin_zh_CN.ts12
-rw-r--r--src/qt/locale/bitcoin_zh_TW.ts54
-rw-r--r--src/qt/locale/bitcoin_zu.ts291
-rw-r--r--src/qt/networkstyle.cpp3
-rw-r--r--src/qt/optionsmodel.cpp26
-rw-r--r--src/qt/optionsmodel.h9
-rw-r--r--src/qt/paymentserver.cpp10
-rw-r--r--src/qt/paymentserver.h2
-rw-r--r--src/qt/qrimagewidget.cpp31
-rw-r--r--src/qt/qrimagewidget.h4
-rw-r--r--src/qt/receivecoinsdialog.cpp16
-rw-r--r--src/qt/receivecoinsdialog.h3
-rw-r--r--src/qt/rpcconsole.cpp10
-rw-r--r--src/qt/rpcconsole.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp10
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/splashscreen.cpp47
-rw-r--r--src/qt/splashscreen.h13
-rw-r--r--src/qt/test/addressbooktests.cpp6
-rw-r--r--src/qt/test/apptests.cpp8
-rw-r--r--src/qt/test/test_main.cpp12
-rw-r--r--src/qt/test/wallettests.cpp6
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/utilitydialog.h6
-rw-r--r--src/qt/walletcontroller.cpp17
-rw-r--r--src/qt/walletframe.cpp25
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/randomenv.cpp5
-rw-r--r--src/rest.cpp8
-rw-r--r--src/rpc/blockchain.cpp364
-rw-r--r--src/rpc/blockchain.h2
-rw-r--r--src/rpc/client.cpp11
-rw-r--r--src/rpc/mining.cpp157
-rw-r--r--src/rpc/misc.cpp202
-rw-r--r--src/rpc/net.cpp267
-rw-r--r--src/rpc/rawtransaction.cpp249
-rw-r--r--src/rpc/rawtransaction_util.cpp17
-rw-r--r--src/rpc/server.cpp15
-rw-r--r--src/rpc/server.h4
-rw-r--r--src/rpc/util.cpp4
-rw-r--r--src/script/interpreter.cpp490
-rw-r--r--src/script/interpreter.h98
-rw-r--r--src/script/script.cpp11
-rw-r--r--src/script/script.h17
-rw-r--r--src/script/script_error.cpp22
-rw-r--r--src/script/script_error.h12
-rw-r--r--src/script/sigcache.cpp43
-rw-r--r--src/script/sigcache.h4
-rw-r--r--src/script/sign.cpp11
-rw-r--r--src/script/sign.h1
-rw-r--r--src/script/standard.cpp20
-rw-r--r--src/script/standard.h14
-rw-r--r--src/secp256k1/.gitignore4
-rw-r--r--src/secp256k1/.travis.yml34
-rw-r--r--src/secp256k1/Makefile.am12
-rw-r--r--src/secp256k1/README.md2
-rw-r--r--src/secp256k1/TODO3
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m45
-rw-r--r--src/secp256k1/configure.ac164
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c1
-rwxr-xr-xsrc/secp256k1/contrib/travis.sh25
-rw-r--r--src/secp256k1/include/secp256k1.h2
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h236
-rw-r--r--src/secp256k1/include/secp256k1_schnorrsig.h111
-rw-r--r--src/secp256k1/sage/gen_exhaustive_groups.sage129
-rw-r--r--src/secp256k1/src/assumptions.h80
-rw-r--r--src/secp256k1/src/basic-config.h10
-rw-r--r--src/secp256k1/src/bench_internal.c178
-rw-r--r--src/secp256k1/src/bench_schnorrsig.c102
-rw-r--r--src/secp256k1/src/ecmult.h2
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h36
-rw-r--r--src/secp256k1/src/ecmult_impl.h157
-rw-r--r--src/secp256k1/src/field.h12
-rw-r--r--src/secp256k1/src/field_5x52.h6
-rw-r--r--src/secp256k1/src/field_impl.h8
-rw-r--r--src/secp256k1/src/gen_context.c1
-rw-r--r--src/secp256k1/src/group.h18
-rw-r--r--src/secp256k1/src/group_impl.h131
-rw-r--r--src/secp256k1/src/hash_impl.h18
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h4
-rw-r--r--src/secp256k1/src/modules/extrakeys/Makefile.am.include4
-rw-r--r--src/secp256k1/src/modules/extrakeys/main_impl.h251
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h68
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_impl.h524
-rw-r--r--src/secp256k1/src/modules/recovery/Makefile.am.include1
-rw-r--r--src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h149
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h10
-rw-r--r--src/secp256k1/src/modules/schnorrsig/Makefile.am.include9
-rw-r--r--src/secp256k1/src/modules/schnorrsig/main_impl.h239
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h206
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_impl.h806
-rw-r--r--src/secp256k1/src/scalar.h18
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h32
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h40
-rw-r--r--src/secp256k1/src/scalar_impl.h256
-rw-r--r--src/secp256k1/src/scalar_low_impl.h11
-rw-r--r--src/secp256k1/src/scratch_impl.h25
-rw-r--r--src/secp256k1/src/secp256k1.c81
-rw-r--r--src/secp256k1/src/selftest.h32
-rw-r--r--src/secp256k1/src/testrand.h23
-rw-r--r--src/secp256k1/src/testrand_impl.h72
-rw-r--r--src/secp256k1/src/tests.c668
-rw-r--r--src/secp256k1/src/tests_exhaustive.c379
-rw-r--r--src/secp256k1/src/util.h76
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c50
-rw-r--r--src/serialize.h22
-rw-r--r--src/signet.cpp149
-rw-r--r--src/signet.h37
-rw-r--r--src/streams.h1
-rw-r--r--src/support/lockedpool.cpp1
-rw-r--r--src/sync.cpp5
-rw-r--r--src/sync.h24
-rw-r--r--src/test/addrman_tests.cpp12
-rw-r--r--src/test/base32_tests.cpp3
-rw-r--r--src/test/base58_tests.cpp2
-rw-r--r--src/test/crypto_tests.cpp107
-rw-r--r--src/test/denialofservice_tests.cpp75
-rw-r--r--src/test/fuzz/asmap.cpp14
-rw-r--r--src/test/fuzz/crypto.cpp18
-rw-r--r--src/test/fuzz/deserialize.cpp19
-rw-r--r--src/test/fuzz/fuzz.cpp9
-rw-r--r--src/test/fuzz/locale.cpp3
-rw-r--r--src/test/fuzz/net.cpp153
-rw-r--r--src/test/fuzz/netaddress.cpp5
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp20
-rw-r--r--src/test/fuzz/process_message.cpp24
-rw-r--r--src/test/fuzz/process_messages.cpp9
-rw-r--r--src/test/fuzz/script.cpp2
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp200
-rw-r--r--src/test/fuzz/script_sigcache.cpp18
-rw-r--r--src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp38
-rw-r--r--src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp33
-rw-r--r--src/test/fuzz/signature_checker.cpp7
-rw-r--r--src/test/fuzz/signet.cpp32
-rw-r--r--src/test/fuzz/txrequest.cpp374
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/key_tests.cpp28
-rw-r--r--src/test/limitedmap_tests.cpp101
-rw-r--r--src/test/miner_tests.cpp13
-rw-r--r--src/test/net_tests.cpp468
-rw-r--r--src/test/netbase_tests.cpp122
-rw-r--r--src/test/pow_tests.cpp67
-rw-r--r--src/test/script_standard_tests.cpp17
-rw-r--r--src/test/script_tests.cpp140
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/sigopcount_tests.cpp10
-rw-r--r--src/test/transaction_tests.cpp47
-rw-r--r--src/test/txrequest_tests.cpp738
-rw-r--r--src/test/txvalidation_tests.cpp3
-rw-r--r--src/test/txvalidationcache_tests.cpp4
-rw-r--r--src/test/uint256_tests.cpp6
-rw-r--r--src/test/util/setup_common.cpp54
-rw-r--r--src/test/util/setup_common.h29
-rw-r--r--src/test/util_tests.cpp129
-rw-r--r--src/test/validation_block_tests.cpp3
-rw-r--r--src/test/validation_chainstate_tests.cpp3
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp11
-rw-r--r--src/test/validation_flush_tests.cpp3
-rw-r--r--src/test/validation_tests.cpp79
-rw-r--r--src/test/versionbits_tests.cpp4
-rw-r--r--src/threadsafety.h6
-rw-r--r--src/timedata.cpp10
-rw-r--r--src/torcontrol.cpp75
-rw-r--r--src/torcontrol.h7
-rw-r--r--src/txmempool.cpp197
-rw-r--r--src/txmempool.h94
-rw-r--r--src/txrequest.cpp756
-rw-r--r--src/txrequest.h211
-rw-r--r--src/uint256.cpp6
-rw-r--r--src/uint256.h18
-rw-r--r--src/util/error.cpp2
-rw-r--r--src/util/message.cpp2
-rw-r--r--src/util/strencodings.cpp45
-rw-r--r--src/util/strencodings.h26
-rw-r--r--src/util/string.h13
-rw-r--r--src/util/system.cpp30
-rw-r--r--src/validation.cpp258
-rw-r--r--src/validation.h69
-rw-r--r--src/validationinterface.cpp12
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/version.h3
-rw-r--r--src/versionbitsinfo.cpp4
-rw-r--r--src/wallet/bdb.cpp92
-rw-r--r--src/wallet/bdb.h26
-rw-r--r--src/wallet/coinselection.cpp43
-rw-r--r--src/wallet/coinselection.h8
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/db.h54
-rw-r--r--src/wallet/feebumper.cpp3
-rw-r--r--src/wallet/init.cpp30
-rw-r--r--src/wallet/load.cpp49
-rw-r--r--src/wallet/load.h4
-rw-r--r--src/wallet/rpcdump.cpp116
-rw-r--r--src/wallet/rpcwallet.cpp1019
-rw-r--r--src/wallet/rpcwallet.h4
-rw-r--r--src/wallet/salvage.cpp45
-rw-r--r--src/wallet/salvage.h4
-rw-r--r--src/wallet/scriptpubkeyman.cpp5
-rw-r--r--src/wallet/scriptpubkeyman.h4
-rw-r--r--src/wallet/sqlite.cpp629
-rw-r--r--src/wallet/sqlite.h122
-rw-r--r--src/wallet/test/coinselector_tests.cpp4
-rw-r--r--src/wallet/test/init_test_fixture.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.h3
-rw-r--r--src/wallet/test/init_tests.cpp14
-rw-r--r--src/wallet/test/ismine_tests.cpp40
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp4
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp52
-rw-r--r--src/wallet/wallet.cpp442
-rw-r--r--src/wallet/wallet.h91
-rw-r--r--src/wallet/walletdb.cpp93
-rw-r--r--src/wallet/walletdb.h20
-rw-r--r--src/wallet/wallettool.cpp80
-rw-r--r--src/wallet/wallettool.h2
-rw-r--r--src/wallet/walletutil.cpp49
-rw-r--r--src/wallet/walletutil.h20
-rw-r--r--src/zmq/zmqabstractnotifier.cpp22
-rw-r--r--src/zmq/zmqabstractnotifier.h22
-rw-r--r--src/zmq/zmqconfig.h22
-rw-r--r--src/zmq/zmqnotificationinterface.cpp153
-rw-r--r--src/zmq/zmqnotificationinterface.h6
-rw-r--r--src/zmq/zmqpublishnotifier.cpp91
-rw-r--r--src/zmq/zmqpublishnotifier.h11
-rw-r--r--src/zmq/zmqrpc.cpp10
-rw-r--r--src/zmq/zmqutil.cpp14
-rw-r--r--src/zmq/zmqutil.h10
-rw-r--r--test/functional/README.md28
-rwxr-xr-xtest/functional/example_test.py29
-rwxr-xr-xtest/functional/feature_abortnode.py6
-rwxr-xr-xtest/functional/feature_assumevalid.py2
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py10
-rwxr-xr-xtest/functional/feature_bip68_sequence.py17
-rwxr-xr-xtest/functional/feature_block.py10
-rwxr-xr-xtest/functional/feature_cltv.py16
-rwxr-xr-xtest/functional/feature_config_args.py8
-rwxr-xr-xtest/functional/feature_csv_activation.py6
-rwxr-xr-xtest/functional/feature_dersig.py16
-rwxr-xr-xtest/functional/feature_fee_estimation.py7
-rwxr-xr-xtest/functional/feature_filelock.py5
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py10
-rwxr-xr-xtest/functional/feature_minchainwork.py4
-rwxr-xr-xtest/functional/feature_notifications.py23
-rwxr-xr-xtest/functional/feature_nulldummy.py15
-rwxr-xr-xtest/functional/feature_proxy.py41
-rwxr-xr-xtest/functional/feature_pruning.py38
-rwxr-xr-xtest/functional/feature_segwit.py5
-rwxr-xr-xtest/functional/feature_settings.py13
-rwxr-xr-xtest/functional/feature_shutdown.py4
-rwxr-xr-xtest/functional/feature_signet.py74
-rwxr-xr-xtest/functional/feature_taproot.py1458
-rwxr-xr-xtest/functional/feature_versionbits_warning.py13
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py11
-rwxr-xr-xtest/functional/interface_rpc.py2
-rwxr-xr-xtest/functional/interface_zmq.py437
-rwxr-xr-xtest/functional/mempool_accept.py17
-rwxr-xr-xtest/functional/mempool_compatibility.py3
-rwxr-xr-xtest/functional/mempool_packages.py11
-rwxr-xr-xtest/functional/mempool_persist.py11
-rwxr-xr-xtest/functional/mempool_unbroadcast.py10
-rwxr-xr-xtest/functional/mining_basic.py11
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py33
-rwxr-xr-xtest/functional/p2p_addr_relay.py4
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py79
-rwxr-xr-xtest/functional/p2p_blockfilters.py26
-rwxr-xr-xtest/functional/p2p_blocksonly.py36
-rwxr-xr-xtest/functional/p2p_compactblocks.py112
-rwxr-xr-xtest/functional/p2p_disconnect_ban.py18
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py18
-rwxr-xr-xtest/functional/p2p_eviction.py8
-rwxr-xr-xtest/functional/p2p_feefilter.py77
-rwxr-xr-xtest/functional/p2p_filter.py12
-rwxr-xr-xtest/functional/p2p_fingerprint.py12
-rwxr-xr-xtest/functional/p2p_getaddr_caching.py53
-rwxr-xr-xtest/functional/p2p_getdata.py4
-rwxr-xr-xtest/functional/p2p_invalid_block.py16
-rwxr-xr-xtest/functional/p2p_invalid_locator.py16
-rwxr-xr-xtest/functional/p2p_invalid_messages.py114
-rwxr-xr-xtest/functional/p2p_invalid_tx.py17
-rwxr-xr-xtest/functional/p2p_leak.py77
-rwxr-xr-xtest/functional/p2p_leak_tx.py19
-rwxr-xr-xtest/functional/p2p_nobloomfilter_messages.py2
-rwxr-xr-xtest/functional/p2p_node_network_limited.py21
-rwxr-xr-xtest/functional/p2p_permissions.py32
-rwxr-xr-xtest/functional/p2p_ping.py2
-rwxr-xr-xtest/functional/p2p_segwit.py61
-rwxr-xr-xtest/functional/p2p_sendheaders.py31
-rwxr-xr-xtest/functional/p2p_timeouts.py2
-rwxr-xr-xtest/functional/p2p_tx_download.py168
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py9
-rwxr-xr-xtest/functional/rpc_blockchain.py48
-rwxr-xr-xtest/functional/rpc_createmultisig.py3
-rwxr-xr-xtest/functional/rpc_deprecated.py37
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py13
-rwxr-xr-xtest/functional/rpc_generate.py36
-rwxr-xr-xtest/functional/rpc_getblockfilter.py5
-rwxr-xr-xtest/functional/rpc_getdescriptorinfo.py1
-rwxr-xr-xtest/functional/rpc_getpeerinfo_deprecation.py (renamed from test/functional/rpc_getpeerinfo_banscore_deprecation.py)20
-rwxr-xr-xtest/functional/rpc_invalidateblock.py12
-rwxr-xr-xtest/functional/rpc_misc.py32
-rwxr-xr-xtest/functional/rpc_net.py153
-rwxr-xr-xtest/functional/rpc_preciousblock.py7
-rwxr-xr-xtest/functional/rpc_psbt.py51
-rwxr-xr-xtest/functional/rpc_rawtransaction.py13
-rwxr-xr-xtest/functional/rpc_setban.py7
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py20
-rwxr-xr-xtest/functional/rpc_txoutproof.py86
-rw-r--r--test/functional/test_framework/address.py72
-rw-r--r--test/functional/test_framework/bip340_test_vectors.csv16
-rw-r--r--test/functional/test_framework/blocktools.py51
-rw-r--r--test/functional/test_framework/key.py212
-rwxr-xr-xtest/functional/test_framework/messages.py131
-rw-r--r--test/functional/test_framework/muhash.py110
-rwxr-xr-xtest/functional/test_framework/p2p.py (renamed from test/functional/test_framework/mininode.py)43
-rw-r--r--test/functional/test_framework/script.py133
-rw-r--r--test/functional/test_framework/segwit_addr.py22
-rwxr-xr-xtest/functional/test_framework/test_framework.py91
-rwxr-xr-xtest/functional/test_framework/test_node.py23
-rw-r--r--test/functional/test_framework/util.py139
-rw-r--r--test/functional/test_framework/wallet.py68
-rwxr-xr-xtest/functional/test_runner.py21
-rwxr-xr-xtest/functional/tool_wallet.py27
-rwxr-xr-xtest/functional/wallet_abandonconflict.py6
-rwxr-xr-xtest/functional/wallet_address_types.py16
-rwxr-xr-xtest/functional/wallet_avoidreuse.py3
-rwxr-xr-xtest/functional/wallet_backup.py78
-rwxr-xr-xtest/functional/wallet_balance.py7
-rwxr-xr-xtest/functional/wallet_basic.py55
-rwxr-xr-xtest/functional/wallet_bumpfee.py88
-rwxr-xr-xtest/functional/wallet_create_tx.py8
-rwxr-xr-xtest/functional/wallet_descriptor.py6
-rwxr-xr-xtest/functional/wallet_disable.py1
-rwxr-xr-xtest/functional/wallet_dump.py2
-rwxr-xr-xtest/functional/wallet_groups.py90
-rwxr-xr-xtest/functional/wallet_hd.py11
-rwxr-xr-xtest/functional/wallet_import_rescan.py7
-rwxr-xr-xtest/functional/wallet_importmulti.py2
-rwxr-xr-xtest/functional/wallet_keypool.py2
-rwxr-xr-xtest/functional/wallet_keypool_topup.py11
-rwxr-xr-xtest/functional/wallet_listsinceblock.py25
-rwxr-xr-xtest/functional/wallet_multiwallet.py73
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py14
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py32
-rwxr-xr-xtest/functional/wallet_send.py345
-rwxr-xr-xtest/functional/wallet_startup.py58
-rwxr-xr-xtest/functional/wallet_txn_clone.py8
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py6
-rwxr-xr-xtest/functional/wallet_upgradewallet.py4
-rwxr-xr-xtest/functional/wallet_zapwallettxes.py79
-rwxr-xr-xtest/fuzz/test_runner.py72
-rwxr-xr-xtest/get_previous_releases.py (renamed from contrib/devtools/previous_release.py)61
-rwxr-xr-xtest/lint/check-doc.py2
-rwxr-xr-xtest/lint/commit-script-check.sh2
-rwxr-xr-xtest/lint/lint-cpp.sh21
-rwxr-xr-xtest/lint/lint-git-commit-check.sh8
-rwxr-xr-xtest/lint/lint-locale-dependence.sh33
-rwxr-xr-xtest/lint/lint-whitespace.sh23
-rw-r--r--test/sanitizer_suppressions/tsan3
-rw-r--r--test/sanitizer_suppressions/ubsan1
583 files changed, 39709 insertions, 10152 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 7dcf9388b9..eace0b7121 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -11,25 +11,19 @@ environment:
QT_DOWNLOAD_HASH: '9a8c6eb20967873785057fdcd329a657c7f922b0af08c5fde105cc597dd37e21'
QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019'
VCPKG_INSTALL_PATH: 'C:\tools\vcpkg\installed'
- VCPKG_COMMIT_ID: 'ed0df8ecc4ed7e755ea03e18aaf285fd9b4b4a74'
+ VCPKG_COMMIT_ID: '40230b8e3f6368dcb398d649331be878ca1e9007'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
# - cmd: pip install zmq
# Powershell block below is to install the c++ dependencies via vcpkg. The pseudo code is:
# a. Checkout the vcpkg source (including port files) for the specific checkout and build the vcpkg binary,
-# b. Install the missing packages.
+# b. Install the missing packages using the vcpkg manifest.
- ps: |
- $env:PACKAGES = Get-Content -Path build_msvc\vcpkg-packages.txt
- Write-Host "vcpkg installing packages: $env:PACKAGES"
cd c:\tools\vcpkg
$env:GIT_REDIRECT_STDERR = '2>&1' # git is writing non-errors to STDERR when doing git pull. Send to STDOUT instead.
git pull origin master > $null
git -c advice.detachedHead=false checkout $env:VCPKG_COMMIT_ID
.\bootstrap-vcpkg.bat > $null
- Add-Content "C:\tools\vcpkg\triplets\$env:PLATFORM-windows-static.cmake" "set(VCPKG_BUILD_TYPE release)"
- .\vcpkg install --triplet $env:PLATFORM-windows-static $env:PACKAGES.split() > $null
- Write-Host "vcpkg packages installed successfully."
- .\vcpkg integrate install
cd "$env:APPVEYOR_BUILD_FOLDER"
before_build:
# Powershell block below is to download and extract the Qt static libraries. The pseudo code is:
diff --git a/.cirrus.yml b/.cirrus.yml
index 446d3e35a9..5dc7e7995a 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -5,7 +5,8 @@ container:
# https://cirrus-ci.org/faq/#are-there-any-limits
# Each project has 16 CPU in total, assign 2 to each container, so that 8 tasks run in parallel
cpu: 2
- memory: 6G # https://cirrus-ci.org/guide/linux/#linux-containers
+ 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
env:
PACKAGE_MANAGER_INSTALL : "apt-get update && apt-get install -y"
MAKEJOBS: "-j4"
@@ -56,7 +57,10 @@ task:
<< : *GLOBAL_TASK_TEMPLATE
container:
image: ubuntu:focal
+ cpu: 4 # Double CPU and Memory to avoid timeout
+ memory: 16G
env:
+ MAKEJOBS: "-j8"
FILE_ENV: "./ci/test/00_setup_env_native_tsan.sh"
task:
@@ -66,3 +70,11 @@ task:
image: ubuntu:focal
env:
FILE_ENV: "./ci/test/00_setup_env_native_asan.sh"
+
+task:
+ name: 'x86_64 Linux [GOAL: install] [focal] [no depends, only system libs, fuzzers under valgrind]'
+ << : *GLOBAL_TASK_TEMPLATE
+ container:
+ image: ubuntu:focal
+ env:
+ FILE_ENV: "./ci/test/00_setup_env_native_fuzz_with_valgrind.sh"
diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.md b/.github/ISSUE_TEMPLATE/good_first_issue.md
index 8be78a1f6e..d32e22d360 100644
--- a/.github/ISSUE_TEMPLATE/good_first_issue.md
+++ b/.github/ISSUE_TEMPLATE/good_first_issue.md
@@ -2,11 +2,13 @@
name: Good first issue
about: '(Regular devs only): Suggest a new good first issue'
title: ''
-labels: good first issue
+labels: ''
assignees: ''
---
+<!-- Needs the label "good first issue" assigned manually before or after opening -->
+
<!-- A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution -->
<!-- Motivate the issue and explain the solution briefly -->
diff --git a/.gitignore b/.gitignore
index 3e5d284aa3..5726b18928 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,7 +119,10 @@ releases
/*.info
test_bitcoin.coverage/
total.coverage/
+fuzz.coverage/
coverage_percent.txt
+/cov_tool_wrapper.sh
+qa-assets/
#build tests
linux-coverage-build
diff --git a/.travis.yml b/.travis.yml
index f1cee7133f..42fa653d8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -66,7 +66,7 @@ jobs:
- set -o errexit; source ./ci/lint/06_script.sh
- stage: test
- name: 'ARM [GOAL: install] [buster] [unit tests, functional tests]'
+ name: 'ARM [GOAL: install] [buster] [unit tests, no functional tests]'
arch: arm64 # Can disable QEMU_USER_CMD and run the tests natively without qemu
env: >-
FILE_ENV="./ci/test/00_setup_env_arm.sh"
@@ -110,11 +110,6 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_native_multiprocess.sh"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [focal] [no depends, only system libs, fuzzers under valgrind]'
- env: >-
- FILE_ENV="./ci/test/00_setup_env_native_fuzz_with_valgrind.sh"
-
- - stage: test
name: 'x86_64 Linux [GOAL: install] [xenial] [no wallet]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_nowallet.sh"
@@ -137,6 +132,7 @@ jobs:
- berkeley-db4
- miniupnpc
- qrencode
+ - sqlite
- ccache
- zeromq
env: >-
diff --git a/.tx/config b/.tx/config
index cd9e237158..86b517a612 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[bitcoin.qt-translation-020x]
+[bitcoin.qt-translation-021x]
file_filter = src/qt/locale/bitcoin_<lang>.ts
source_file = src/qt/locale/bitcoin_en.ts
source_lang = en
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000000..24a80fb35d
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,136 @@
+# ==============================================================================
+# Bitcoin Core CODEOWNERS
+# ==============================================================================
+
+# Configuration of code ownership and review approvals for the bitcoin/bitcoin
+# repo.
+
+# Order is important; the last matching pattern takes the most precedence.
+# More info on how this file works can be found at:
+# https://help.github.com/articles/about-codeowners/
+
+# This file is called CODEOWNERS because it is a magic file for GitHub to
+# automatically suggest reviewers. In this project's case, the names below
+# should be thought of as code reviewers rather than owners. Regular
+# contributors are free to add their names to specific directories or files
+# provided that they are willing to provide a review when automatically
+# assigned.
+
+# Absence from this list should not be interpreted as a discouragement to
+# review a pull request. Peer review is always welcome and is a critical
+# component of the progress of the codebase. Information on peer review
+# guidelines can be found in the CONTRIBUTING.md doc.
+
+
+# Maintainers
+# @laanwj
+# @sipa
+# @fanquake
+# @jonasschnelli
+# @marcofalke
+# @meshcollider
+
+# Docs
+/doc/*[a-zA-Z-].md @harding
+/doc/Doxyfile.in @fanquake
+/doc/REST-interface.md @jonasschnelli
+/doc/benchmarking.md @ariard
+/doc/bitcoin-conf.md @hebasto
+/doc/build-freebsd.md @fanquake
+/doc/build-netbsd.md @fanquake
+/doc/build-openbsd.md @laanwj
+/doc/build-osx.md @fanquake
+/doc/build-unix.md @laanwj
+/doc/build-windows.md @sipsorcery
+/doc/dependencies.md @fanquake
+/doc/developer-notes.md @laanwj
+/doc/files.md @hebasto
+/doc/gitian-building.md @laanwj
+/doc/reduce-memory.md @fanquake
+/doc/reduce-traffic.md @jonasschnelli
+/doc/release-process.md @laanwj
+/doc/translation_strings_policy.md @laanwj
+
+# Build aux
+/build-aux/m4/bitcoin_qt.m4 @hebasto
+
+# MSVC build system
+/build_msvc/ @sipsorcery
+
+# Settings
+/src/util/settings.* @ryanofsky
+
+# Fuzzing
+/src/test/fuzz/ @practicalswift
+/doc/fuzzing.md @practicalswift
+
+# Test framework
+/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
+
+# Translations
+/src/util/translation.h @hebasto
+
+# Dev Tools
+/contrib/devtools/security-check.py @fanquake
+/contrib/devtools/test-security-check.py @fanquake
+/contrib/devtools/symbol-check.py @fanquake
+
+# Gitian/Guix
+/contrib/gitian-build.py @hebasto
+/contrib/guix/ @dongcarl
+
+# Compatibility
+/src/compat/glibc_* @fanquake
+
+# GUI
+/src/qt/forms/ @hebasto
+
+# Wallet
+/src/wallet/ @achow101
+
+# CLI
+/src/bitcoin-cli.cpp @jonatack
+
+# Coinstats
+/src/node/coinstats.* @fjahr
+
+# Index
+/src/index/ @fjahr
+
+# Descriptors
+*descriptor* @achow101 @sipa
+
+# Interfaces
+/src/interfaces/ @ryanofsky
+
+# DB
+/src/txdb.* @jamesob
+/src/dbwrapper.* @jamesob
+
+# Scripts/Linter
+*.sh @practicalswift
+/test/lint/ @practicalswift
+/test/lint/lint-shell.sh @hebasto
+
+# Bech32
+/src/bech32.* @sipa
+/src/bench/bech32.* @sipa
+
+# PSBT
+/src/psbt* @achow101
+/src/node/psbt* @achow101
+/doc/psbt.md @achow101
+
+# P2P
+/src/net_processing.* @sipa
+/src/protocol.* @sipa
+
+# Consensus
+/src/coins.* @sipa @jamesob
+/src/script/script.* @sipa
+/src/script/interpreter.* @sipa
+/src/validation.* @sipa
+/src/consensus/ @sipa
diff --git a/Makefile.am b/Makefile.am
index 75a164f49e..c8af4228f3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,10 +65,10 @@ OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \
$(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \
$(top_srcdir)/contrib/macdeploy/detached-sig-create.sh
-COVERAGE_INFO = baseline.info \
+COVERAGE_INFO = $(COV_TOOL_WRAPPER) baseline.info \
test_bitcoin_filtered.info total_coverage.info \
baseline_filtered.info functional_test.info functional_test_filtered.info \
- test_bitcoin_coverage.info test_bitcoin.info fuzz.info fuzz_coverage.info
+ test_bitcoin_coverage.info test_bitcoin.info fuzz.info fuzz_filtered.info fuzz_coverage.info
dist-hook:
-$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf -
@@ -181,6 +181,7 @@ $(BITCOIN_WALLET_BIN): FORCE
if USE_LCOV
LCOV_FILTER_PATTERN = \
+ -p "/usr/local/" \
-p "/usr/include/" \
-p "/usr/lib/" \
-p "/usr/lib64/" \
@@ -192,7 +193,13 @@ LCOV_FILTER_PATTERN = \
-p "src/secp256k1" \
-p "depends"
-baseline.info:
+DIR_FUZZ_SEED_CORPUS ?= qa-assets/fuzz_seed_corpus
+
+$(COV_TOOL_WRAPPER):
+ @echo 'exec $(COV_TOOL) "$$@"' > $(COV_TOOL_WRAPPER)
+ @chmod +x $(COV_TOOL_WRAPPER)
+
+baseline.info: $(COV_TOOL_WRAPPER)
$(LCOV) -c -i -d $(abs_builddir)/src -o $@
baseline_filtered.info: baseline.info
@@ -200,7 +207,7 @@ baseline_filtered.info: baseline.info
$(LCOV) -a $@ $(LCOV_OPTS) -o $@
fuzz.info: baseline_filtered.info
- @TIMEOUT=15 test/fuzz/test_runner.py qa-assets/fuzz_seed_corpus -l DEBUG
+ @TIMEOUT=15 test/fuzz/test_runner.py $(DIR_FUZZ_SEED_CORPUS) -l DEBUG
$(LCOV) -c $(LCOV_OPTS) -d $(abs_builddir)/src --t fuzz-tests -o $@
$(LCOV) -z $(LCOV_OPTS) -d $(abs_builddir)/src
diff --git a/build-aux/m4/ax_boost_thread.m4 b/build-aux/m4/ax_boost_thread.m4
index e9dea43535..75e80e6e75 100644
--- a/build-aux/m4/ax_boost_thread.m4
+++ b/build-aux/m4/ax_boost_thread.m4
@@ -30,7 +30,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 32
+#serial 33
AC_DEFUN([AX_BOOST_THREAD],
[
@@ -67,13 +67,24 @@ AC_DEFUN([AX_BOOST_THREAD],
[AC_LANG_PUSH([C++])
CXXFLAGS_SAVE=$CXXFLAGS
- if test "x$host_os" = "xsolaris" ; then
- CXXFLAGS="-pthreads $CXXFLAGS"
- elif test "x$host_os" = "xmingw32" ; then
- CXXFLAGS="-mthreads $CXXFLAGS"
- else
- CXXFLAGS="-pthread $CXXFLAGS"
- fi
+ case "x$host_os" in
+ xsolaris )
+ CXXFLAGS="-pthreads $CXXFLAGS"
+ break;
+ ;;
+ xmingw32 )
+ CXXFLAGS="-mthreads $CXXFLAGS"
+ break;
+ ;;
+ *android* )
+ break;
+ ;;
+ * )
+ CXXFLAGS="-pthread $CXXFLAGS"
+ break;
+ ;;
+ esac
+
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM(
[[@%:@include <boost/thread/thread.hpp>]],
@@ -84,13 +95,23 @@ AC_DEFUN([AX_BOOST_THREAD],
AC_LANG_POP([C++])
])
if test "x$ax_cv_boost_thread" = "xyes"; then
- if test "x$host_os" = "xsolaris" ; then
- BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
- elif test "x$host_os" = "xmingw32" ; then
- BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
- else
- BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
- fi
+ case "x$host_os" in
+ xsolaris )
+ BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
+ break;
+ ;;
+ xmingw32 )
+ BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
+ break;
+ ;;
+ *android* )
+ break;
+ ;;
+ * )
+ BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
+ break;
+ ;;
+ esac
AC_SUBST(BOOST_CPPFLAGS)
@@ -148,6 +169,9 @@ AC_DEFUN([AX_BOOST_THREAD],
xmingw32 )
break;
;;
+ *android* )
+ break;
+ ;;
* )
BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread"
break;
diff --git a/build-aux/m4/ax_pthread.m4 b/build-aux/m4/ax_pthread.m4
index 4c4051ea37..1598d077ff 100644
--- a/build-aux/m4/ax_pthread.m4
+++ b/build-aux/m4/ax_pthread.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
@@ -55,6 +55,7 @@
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
@@ -67,7 +68,7 @@
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
+# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
@@ -82,7 +83,7 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
-#serial 23
+#serial 27
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
@@ -123,10 +124,12 @@ fi
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
-# Create a list of thread flags to try. Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
+# Create a list of thread flags to try. Items with a "," contain both
+# C compiler flags (before ",") and linker flags (after ","). Other items
+# starting with a "-" are C compiler flags, and remaining items are
+# library names, except for "none" which indicates that we try without
+# any flags at all, and "pthread-config" which is a program returning
+# the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
@@ -194,14 +197,47 @@ case $host_os in
# that too in a future libc.) So we'll check first for the
# standard Solaris way of linking pthreads (-mt -lpthread).
- ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
+ ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
;;
esac
+# Are we compiling with Clang?
+
+AC_CACHE_CHECK([whether $CC is Clang],
+ [ax_cv_PTHREAD_CLANG],
+ [ax_cv_PTHREAD_CLANG=no
+ # Note that Autoconf sets GCC=yes for Clang as well as GCC
+ if test "x$GCC" = "xyes"; then
+ AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
+ [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+# if defined(__clang__) && defined(__llvm__)
+ AX_PTHREAD_CC_IS_CLANG
+# endif
+ ],
+ [ax_cv_PTHREAD_CLANG=yes])
+ fi
+ ])
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
+# Note that for GCC and Clang -pthread generally implies -lpthread,
+# except when -nostdlib is passed.
+# This is problematic using libtool to build C++ shared libraries with pthread:
+# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
+# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
+# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
+# To solve this, first try -pthread together with -lpthread for GCC
+
AS_IF([test "x$GCC" = "xyes"],
- [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
+ [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
+
+# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
+
+AS_IF([test "x$ax_pthread_clang" = "xyes"],
+ [ax_pthread_flags="-pthread,-lpthread -pthread"])
+
# The presence of a feature test macro requesting re-entrant function
# definitions is, on some systems, a strong hint that pthreads support is
@@ -224,25 +260,86 @@ AS_IF([test "x$ax_pthread_check_macro" = "x--"],
[ax_pthread_check_cond=0],
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
-# Are we compiling with Clang?
-AC_CACHE_CHECK([whether $CC is Clang],
- [ax_cv_PTHREAD_CLANG],
- [ax_cv_PTHREAD_CLANG=no
- # Note that Autoconf sets GCC=yes for Clang as well as GCC
- if test "x$GCC" = "xyes"; then
- AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
- [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
-# if defined(__clang__) && defined(__llvm__)
- AX_PTHREAD_CC_IS_CLANG
-# endif
- ],
- [ax_cv_PTHREAD_CLANG=yes])
- fi
- ])
-ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
+
+ case $ax_pthread_try_flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ *,*)
+ PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
+ PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
+ AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
+ PTHREAD_CFLAGS="$ax_pthread_try_flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+ AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
+ PTHREAD_LIBS="-l$ax_pthread_try_flag"
+ ;;
+ esac
+
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+# if $ax_pthread_check_cond
+# error "$ax_pthread_check_macro must be defined"
+# endif
+ static void *some_global = NULL;
+ static void routine(void *a)
+ {
+ /* To avoid any unused-parameter or
+ unused-but-set-parameter warning. */
+ some_global = a;
+ }
+ static void *start_routine(void *a) { return a; }],
+ [pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+ pthread_join(th, 0);
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */])],
+ [ax_pthread_ok=yes],
+ [])
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ AC_MSG_RESULT([$ax_pthread_ok])
+ AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
-ax_pthread_clang_warning=no
# Clang needs special handling, because older versions handle the -pthread
# option in a rather... idiosyncratic way
@@ -261,11 +358,6 @@ if test "x$ax_pthread_clang" = "xyes"; then
# -pthread does define _REENTRANT, and while the Darwin headers
# ignore this macro, third-party headers might not.)
- PTHREAD_CFLAGS="-pthread"
- PTHREAD_LIBS=
-
- ax_pthread_ok=yes
-
# However, older versions of Clang make a point of warning the user
# that, in an invocation where only linking and no compilation is
# taking place, the -pthread option has no effect ("argument unused
@@ -320,78 +412,7 @@ if test "x$ax_pthread_clang" = "xyes"; then
fi # $ax_pthread_clang = yes
-if test "x$ax_pthread_ok" = "xno"; then
-for ax_pthread_try_flag in $ax_pthread_flags; do
-
- case $ax_pthread_try_flag in
- none)
- AC_MSG_CHECKING([whether pthreads work without any flags])
- ;;
-
- -mt,pthread)
- AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
- PTHREAD_CFLAGS="-mt"
- PTHREAD_LIBS="-lpthread"
- ;;
-
- -*)
- AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
- PTHREAD_CFLAGS="$ax_pthread_try_flag"
- ;;
-
- pthread-config)
- AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
- AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
- PTHREAD_CFLAGS="`pthread-config --cflags`"
- PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
- ;;
- *)
- AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
- PTHREAD_LIBS="-l$ax_pthread_try_flag"
- ;;
- esac
-
- ax_pthread_save_CFLAGS="$CFLAGS"
- ax_pthread_save_LIBS="$LIBS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- LIBS="$PTHREAD_LIBS $LIBS"
-
- # Check for various functions. We must include pthread.h,
- # since some functions may be macros. (On the Sequent, we
- # need a special flag -Kthread to make this header compile.)
- # We check for pthread_join because it is in -lpthread on IRIX
- # while pthread_create is in libc. We check for pthread_attr_init
- # due to DEC craziness with -lpthreads. We check for
- # pthread_cleanup_push because it is one of the few pthread
- # functions on Solaris that doesn't have a non-functional libc stub.
- # We try pthread_create on general principles.
-
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
-# if $ax_pthread_check_cond
-# error "$ax_pthread_check_macro must be defined"
-# endif
- static void routine(void *a) { a = 0; }
- static void *start_routine(void *a) { return a; }],
- [pthread_t th; pthread_attr_t attr;
- pthread_create(&th, 0, start_routine, 0);
- pthread_join(th, 0);
- pthread_attr_init(&attr);
- pthread_cleanup_push(routine, 0);
- pthread_cleanup_pop(0) /* ; */])],
- [ax_pthread_ok=yes],
- [])
-
- CFLAGS="$ax_pthread_save_CFLAGS"
- LIBS="$ax_pthread_save_LIBS"
-
- AC_MSG_RESULT([$ax_pthread_ok])
- AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
-
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
-done
-fi
# Various other checks:
if test "x$ax_pthread_ok" = "xyes"; then
@@ -438,7 +459,8 @@ if test "x$ax_pthread_ok" = "xyes"; then
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
[ax_cv_PTHREAD_PRIO_INHERIT],
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
- [[int i = PTHREAD_PRIO_INHERIT;]])],
+ [[int i = PTHREAD_PRIO_INHERIT;
+ return i;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no])
])
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index e171238cbc..6c7665830b 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -72,18 +72,32 @@ AC_DEFUN([BITCOIN_QT_INIT],[
AC_ARG_WITH([qtdbus],
[AS_HELP_STRING([--with-qtdbus],
- [enable DBus support (default is yes if qt is enabled and QtDBus is found)])],
+ [enable DBus support (default is yes if qt is enabled and QtDBus is found, except on Android)])],
[use_dbus=$withval],
[use_dbus=auto])
+ dnl Android doesn't support D-Bus and certainly doesn't use it for notifications
+ case $host in
+ *android*)
+ if test "x$use_dbus" != xyes; then
+ use_dbus=no
+ fi
+ ;;
+ esac
+
AC_SUBST(QT_TRANSLATION_DIR,$qt_translation_path)
])
dnl Find Qt libraries and includes.
+dnl
+dnl BITCOIN_QT_CONFIGURE([MINIMUM-VERSION])
+dnl
dnl Outputs: See _BITCOIN_QT_FIND_LIBS
dnl Outputs: Sets variables for all qt-related tools.
dnl Outputs: bitcoin_enable_qt, bitcoin_enable_qt_dbus, bitcoin_enable_qt_test
AC_DEFUN([BITCOIN_QT_CONFIGURE],[
+ qt_version=">= $1"
+ qt_lib_prefix="Qt5"
BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS])
dnl This is ugly and complicated. Yuck. Works as follows:
@@ -221,7 +235,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
bitcoin_enable_qt=no
])
if test x$bitcoin_enable_qt = xyes; then
- AC_MSG_RESULT([$bitcoin_enable_qt ($QT_LIB_PREFIX)])
+ AC_MSG_RESULT([$bitcoin_enable_qt ($qt_lib_prefix)])
else
AC_MSG_RESULT([$bitcoin_enable_qt])
fi
@@ -295,25 +309,19 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
if test -d "$qt_plugin_path/platforms/android"; then
QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL"
fi
- m4_ifdef([PKG_CHECK_MODULES],[
- if test x$bitcoin_cv_qt58 = xno; then
- PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"])
- else
- PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
- fi
- if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
- elif test "x$TARGET_OS" = xdarwin; then
- PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
- PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"])
- fi
- ])
+ PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport], [QT_LIBS="-lQt5FontDatabaseSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport], [QT_LIBS="-lQt5EventDispatcherSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport], [QT_LIBS="-lQt5ThemeSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport], [QT_LIBS="-lQt5DeviceDiscoverySupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport], [QT_LIBS="-lQt5AccessibilitySupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTFB], [Qt5FbSupport], [QT_LIBS="-lQt5FbSupport $QT_LIBS"])
+ if test "x$TARGET_OS" = xlinux; then
+ PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
+ elif test "x$TARGET_OS" = xdarwin; then
+ PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"])
+ PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport], [QT_LIBS="-lQt5CglSupport $QT_LIBS"])
+ fi
fi
])
@@ -321,23 +329,29 @@ dnl Internal. Find Qt libraries using pkg-config.
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],[
- m4_ifdef([PKG_CHECK_MODULES],[
- QT_LIB_PREFIX=Qt5
- qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets"
- BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" have_qt=yes],[have_qt=no])
+ BITCOIN_QT_CHECK([
+ PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Core $qt_version not found])])
+ ])
+ BITCOIN_QT_CHECK([
+ PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Gui $qt_version not found])])
+ ])
+ BITCOIN_QT_CHECK([
+ PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets $qt_version not found])])
+ ])
+ BITCOIN_QT_CHECK([
+ PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network $qt_version], [],
+ [BITCOIN_QT_FAIL([${qt_lib_prefix}Network $qt_version not found])])
+ ])
+ 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"
- if test "x$have_qt" != xyes; then
- have_qt=no
- BITCOIN_QT_FAIL([Qt dependencies not found])
- fi
- ])
- BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_TEST], [${QT_LIB_PREFIX}Test], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
- if test "x$use_dbus" != xno; then
- PKG_CHECK_MODULES([QT_DBUS], [${QT_LIB_PREFIX}DBus], [QT_DBUS_INCLUDES="$QT_DBUS_CFLAGS"; have_qt_dbus=yes], [have_qt_dbus=no])
- fi
- ])
+ BITCOIN_QT_CHECK([
+ PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
+ if test "x$use_dbus" != xno; then
+ PKG_CHECK_MODULES([QT_DBUS], [${qt_lib_prefix}DBus $qt_version], [QT_DBUS_INCLUDES="$QT_DBUS_CFLAGS"; have_qt_dbus=yes], [have_qt_dbus=no])
+ fi
])
- true; dnl
])
diff --git a/build_msvc/.gitignore b/build_msvc/.gitignore
index 3e71c7b8d3..ae8120fdf3 100644
--- a/build_msvc/.gitignore
+++ b/build_msvc/.gitignore
@@ -24,3 +24,4 @@ libtest_util/libtest_util.vcxproj
*/Win32
libbitcoin_qt/QtGeneratedFiles/*
test_bitcoin-qt/QtGeneratedFiles/*
+vcpkg_installed \ No newline at end of file
diff --git a/build_msvc/README.md b/build_msvc/README.md
index a489fb36b2..87ea556a23 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 2017 and 2019.
Building with Visual Studio is an alternative to the Linux based [cross-compiler build](https://github.com/bitcoin/bitcoin/blob/master/doc/build-windows.md).
@@ -12,10 +12,9 @@ Quick Start
The minimal steps required to build Bitcoin Core with the msbuild toolchain are below. More detailed instructions are contained in the following sections.
```
-vcpkg install --triplet x64-windows-static berkeleydb boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent[thread] zeromq double-conversion
-vcpkg integrate install
-py -3 build_msvc\msvc-autogen.py
-msbuild /m build_msvc\bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
+cd build_msvc
+py -3 msvc-autogen.py
+msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
```
Dependencies
@@ -28,14 +27,7 @@ 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:
-
-- Berkeley DB
-- Boost
-- DoubleConversion
-- libevent
-- Qt5
-- ZeroMQ
+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.
Qt
---------------------
@@ -52,12 +44,6 @@ Building
The instructions below use `vcpkg` to install the dependencies.
- Install [`vcpkg`](https://github.com/Microsoft/vcpkg).
-- Install the required packages (replace x64 with x86 as required). The list of required packages can be found in the `build_msvc\vcpkg-packages.txt` file. The PowerShell command below will work if run from the repository root directory and `vcpkg` is in the path. Alternatively the contents of the packages text file can be pasted in place of the `Get-Content` cmdlet.
-
-```
-PS >.\vcpkg install --triplet x64-windows-static $(Get-Content -Path build_msvc\vcpkg-packages.txt).split()
-PS >.\vcpkg integrate install
-```
- Use Python to generate `*.vcxproj` from Makefile
@@ -65,7 +51,7 @@ PS >.\vcpkg integrate install
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.
+- 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:
@@ -79,7 +65,7 @@ msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /p:PlatformTools
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.
AppVeyor
---------------------
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index 4fd516fff5..ed227519ae 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -9,6 +9,15 @@
<UseNativeEnvironment>true</UseNativeEnvironment>
</PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgEnableManifest>true</VcpkgEnableManifest>
+ <VcpkgManifestInstall>true</VcpkgManifestInstall>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgAutoLink>true</VcpkgAutoLink>
+ <VcpkgConfiguration>$(Configuration)</VcpkgConfiguration>
+ </PropertyGroup>
+
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' and !Exists('$(WindowsSdkDir)\DesignTime\CommonConfiguration\Neutral\Windows.props')">
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
@@ -110,7 +119,7 @@
<AdditionalOptions>/utf-8 /std:c++17 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
- <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
index 99fb63fb02..c42918d6e1 100644
--- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj
+++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj
@@ -12,7 +12,7 @@
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
- <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
diff --git a/build_msvc/vcpkg-packages.txt b/build_msvc/vcpkg-packages.txt
deleted file mode 100644
index edce8576c3..0000000000
--- a/build_msvc/vcpkg-packages.txt
+++ /dev/null
@@ -1 +0,0 @@
-berkeleydb boost-filesystem boost-multi-index boost-process boost-signals2 boost-test boost-thread libevent[thread] zeromq double-conversion
diff --git a/build_msvc/vcpkg.json b/build_msvc/vcpkg.json
new file mode 100644
index 0000000000..dfd3929c44
--- /dev/null
+++ b/build_msvc/vcpkg.json
@@ -0,0 +1,20 @@
+{
+ "name": "bitcoin-core",
+ "version-string": "1",
+ "dependencies": [
+ "berkeleydb",
+ "boost-filesystem",
+ "boost-multi-index",
+ "boost-process",
+ "boost-signals2",
+ "boost-test",
+ "boost-thread",
+ "sqlite3",
+ "double-conversion",
+ {
+ "name": "libevent",
+ "features": ["thread"]
+ },
+ "zeromq"
+ ]
+}
diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh
index 003bdf3c29..dc0f9b923b 100755
--- a/ci/lint/06_script.sh
+++ b/ci/lint/06_script.sh
@@ -7,7 +7,11 @@
export LC_ALL=C
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
- test/lint/commit-script-check.sh $TRAVIS_COMMIT_RANGE
+ # TRAVIS_BRANCH will be present in a Travis environment. For builds triggered
+ # by a pull request this is the name of the branch targeted by the pull request.
+ # https://docs.travis-ci.com/user/environment-variables/
+ COMMIT_RANGE="$TRAVIS_BRANCH..HEAD"
+ test/lint/commit-script-check.sh $COMMIT_RANGE
fi
test/lint/git-subtree-check.sh src/crypto/ctaes
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index 2413cfca9f..702e881862 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -35,6 +35,10 @@ export USE_BUSY_BOX=${USE_BUSY_BOX:-false}
export RUN_UNIT_TESTS=${RUN_UNIT_TESTS:-true}
export RUN_FUNCTIONAL_TESTS=${RUN_FUNCTIONAL_TESTS:-true}
export RUN_SECURITY_TESTS=${RUN_SECURITY_TESTS:-false}
+# By how much to scale the test_runner timeouts (option --timeout-factor).
+# This is needed because some ci machines have slow CPU or disk, so sanitizers
+# might be slow or a reindex might be waiting on disk IO.
+export TEST_RUNNER_TIMEOUT_FACTOR=${TEST_RUNNER_TIMEOUT_FACTOR:-4}
export TEST_RUNNER_ENV=${TEST_RUNNER_ENV:-}
export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}
export EXPECTED_TESTS_DURATION_IN_SECONDS=${EXPECTED_TESTS_DURATION_IN_SECONDS:-1000}
diff --git a/ci/test/00_setup_env_arm.sh b/ci/test/00_setup_env_arm.sh
index 2e445c126d..610e55c4c3 100644
--- a/ci/test/00_setup_env_arm.sh
+++ b/ci/test/00_setup_env_arm.sh
@@ -21,7 +21,7 @@ export CONTAINER_NAME=ci_arm_linux
export DOCKER_NAME_TAG="debian:buster"
export USE_BUSY_BOX=true
export RUN_UNIT_TESTS=true
-export RUN_FUNCTIONAL_TESTS=true
+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
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index 5995964f17..191b8049b0 100644
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -7,9 +7,8 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_asan
-export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
+export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev libsqlite3-dev"
export DOCKER_NAME_TAG=ubuntu:20.04
export NO_DEPENDS=1
-export TEST_RUNNER_EXTRA="--timeout-factor=4" # Increase timeout because sanitizers slow down
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"
diff --git a/ci/test/00_setup_env_native_msan.sh b/ci/test/00_setup_env_native_msan.sh
index 6a4979990b..b88ee2b50f 100644
--- a/ci/test/00_setup_env_native_msan.sh
+++ b/ci/test/00_setup_env_native_msan.sh
@@ -15,7 +15,7 @@ export BDB_PREFIX="${BASE_ROOT_DIR}/db4"
export CONTAINER_NAME="ci_native_msan"
export PACKAGES="clang-9 llvm-9 cmake"
-export DEP_OPTS="NO_WALLET=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++11 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' zeromq_cxxflags='-std=c++11 ${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++11 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' zeromq_cxxflags='-std=c++11 ${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 USE_MEMORY_SANITIZER="true"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index f9d869b4fd..6f2e39429c 100644
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -15,5 +15,5 @@ export RUN_SECURITY_TESTS="true"
export RUN_UNIT_TESTS_SEQUENTIAL="true"
export RUN_UNIT_TESTS="false"
export GOAL="install"
-export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.2 v0.16.3 v0.17.1 v0.18.1 v0.19.1"
+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-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-c++17 --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" --with-boost-process"
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index fc18483425..b14a46562c 100644
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -10,6 +10,6 @@ export CONTAINER_NAME=ci_native_tsan
export DOCKER_NAME_TAG=ubuntu:20.04
export PACKAGES="clang llvm libc++abi-dev libc++-dev python3-zmq"
export DEP_OPTS="CC=clang CXX='clang++ -stdlib=libc++'"
-export TEST_RUNNER_EXTRA="--exclude feature_block --timeout-factor=4" # Increase timeout because sanitizers slow down. Low memory on Travis machines, exclude feature_block.
+export TEST_RUNNER_EXTRA="--exclude feature_block" # Low memory on Travis machines, exclude feature_block.
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"
diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh
index 0041122f1e..bfaea13a25 100644
--- a/ci/test/00_setup_env_native_valgrind.sh
+++ b/ci/test/00_setup_env_native_valgrind.sh
@@ -7,9 +7,9 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_valgrind
-export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev"
+export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libsqlite3-dev"
export USE_VALGRIND=1
export NO_DEPENDS=1
-export TEST_RUNNER_EXTRA="--exclude rpc_bind --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
+export TEST_RUNNER_EXTRA="--exclude rpc_bind" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index d3566914ac..632bccf574 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -81,11 +81,10 @@ else
fi
if [ ! -d ${DIR_QA_ASSETS} ]; then
- if [ "$RUN_FUZZ_TESTS" = "true" ]; then
- DOCKER_EXEC git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
- fi
+ DOCKER_EXEC git clone --depth=1 https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
fi
export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/
+export DIR_UNIT_TEST_DATA=${DIR_QA_ASSETS}/unit_test_data/
DOCKER_EXEC mkdir -p "${BASE_SCRATCH_DIR}/sanitizer-output/"
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index 131ea21677..8ce839fc04 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -48,6 +48,6 @@ if [ -z "$NO_DEPENDS" ]; then
fi
if [ -n "$PREVIOUS_RELEASES_TO_DOWNLOAD" ]; then
BEGIN_FOLD previous-versions
- DOCKER_EXEC contrib/devtools/previous_release.py -b -t "$PREVIOUS_RELEASES_DIR" "${PREVIOUS_RELEASES_TO_DOWNLOAD}"
+ DOCKER_EXEC test/get_previous_releases.py -b -t "$PREVIOUS_RELEASES_DIR" "${PREVIOUS_RELEASES_TO_DOWNLOAD}"
END_FOLD
fi
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 96d44328b8..607a2820dd 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -23,19 +23,19 @@ fi
if [ "$RUN_UNIT_TESTS" = "true" ]; then
BEGIN_FOLD unit-tests
- DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
+ DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
END_FOLD
fi
if [ "$RUN_UNIT_TESTS_SEQUENTIAL" = "true" ]; then
BEGIN_FOLD unit-tests-seq
- DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
+ DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
END_FOLD
fi
if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
BEGIN_FOLD functional-tests
- DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib ${TEST_RUNNER_ENV} test/functional/test_runner.py --ci $MAKEJOBS --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" --ansi --combinedlogslen=4000 ${TEST_RUNNER_EXTRA} --quiet --failfast
+ DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib ${TEST_RUNNER_ENV} test/functional/test_runner.py --ci $MAKEJOBS --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" --ansi --combinedlogslen=4000 --timeout-factor=${TEST_RUNNER_TIMEOUT_FACTOR} ${TEST_RUNNER_EXTRA} --quiet --failfast
END_FOLD
fi
diff --git a/configure.ac b/configure.ac
index 2381c5dd08..ab40991ce4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,7 @@ AC_PATH_TOOL(AR, ar)
AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
AC_PATH_TOOL(GCOV, gcov)
+AC_PATH_TOOL(LLVM_COV, llvm-cov)
AC_PATH_PROG(LCOV, lcov)
dnl Python 3.5 is specified in .python-version and should be used if available, see doc/dependencies.md
AC_PATH_PROGS([PYTHON], [python3.5 python3.6 python3.7 python3.8 python3 python])
@@ -189,6 +190,16 @@ AC_ARG_ENABLE([ccache],
[use_ccache=$enableval],
[use_ccache=auto])
+dnl Suppress warnings from external headers (e.g. Boost, Qt).
+dnl May be useful if warnings from external headers clutter the build output
+dnl too much, so that it becomes difficult to spot Bitcoin Core warnings
+dnl or if they cause a build failure with --enable-werror.
+AC_ARG_ENABLE([suppress-external-warnings],
+ [AS_HELP_STRING([--enable-suppress-external-warnings],
+ [Suppress warnings from external headers (default is no)])],
+ [suppress_external_warnings=$enableval],
+ [suppress_external_warnings=no])
+
AC_ARG_ENABLE([lcov],
[AS_HELP_STRING([--enable-lcov],
[enable lcov testing (default is no)])],
@@ -376,6 +387,7 @@ if test "x$enable_werror" = "xyes"; then
AX_CHECK_COMPILE_FLAG([-Werror=shadow-field],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=shadow-field"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=switch],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=switch"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=thread-safety],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Werror=range-loop-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=range-loop-analysis"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=unused-variable],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unused-variable"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=date-time],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=date-time"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Werror=return-type],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=return-type"],,[[$CXXFLAG_WERROR]])
@@ -404,6 +416,10 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wdate-time],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wdate-time"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wconditional-uninitialized],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wconditional-uninitialized"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wsign-compare],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsign-compare"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wduplicated-branches],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-branches"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wduplicated-cond],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-cond"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Wlogical-op],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wlogical-op"],,[[$CXXFLAG_WERROR]])
+ AX_CHECK_COMPILE_FLAG([-Woverloaded-virtual],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Woverloaded-virtual"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wsuggest-override],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsuggest-override"],,[[$CXXFLAG_WERROR]],
[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]])
@@ -598,7 +614,7 @@ case $host in
AC_MSG_ERROR("windres not found")
fi
- CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601"
+ CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN"
dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against.
dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override
@@ -680,16 +696,37 @@ if test x$use_lcov = xyes; then
if test x$LCOV = x; then
AC_MSG_ERROR("lcov testing requested but lcov not found")
fi
- if test x$GCOV = x; then
- AC_MSG_ERROR("lcov testing requested but gcov not found")
- fi
if test x$PYTHON = x; then
AC_MSG_ERROR("lcov testing requested but python not found")
fi
if test x$GENHTML = x; then
AC_MSG_ERROR("lcov testing requested but genhtml not found")
fi
- LCOV="$LCOV --gcov-tool=$GCOV"
+
+ AC_MSG_CHECKING([whether compiler is Clang])
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+ #if defined(__clang__) && defined(__llvm__)
+ // Compiler is Clang
+ #else
+ # error Compiler is not Clang
+ #endif
+ ]])],[
+ AC_MSG_RESULT([yes])
+ if test x$LLVM_COV = x; then
+ AC_MSG_ERROR([lcov testing requested but llvm-cov not found])
+ fi
+ COV_TOOL="$LLVM_COV gcov"
+ ],[
+ AC_MSG_RESULT([no])
+ if test x$GCOV = x; then
+ AC_MSG_ERROR([lcov testing requested but gcov not found])
+ fi
+ COV_TOOL="$GCOV"
+ ])
+ AC_SUBST(COV_TOOL)
+ AC_SUBST(COV_TOOL_WRAPPER, "cov_tool_wrapper.sh")
+ LCOV="$LCOV --gcov-tool $(pwd)/$COV_TOOL_WRAPPER"
+
AX_CHECK_LINK_FLAG([[--coverage]], [LDFLAGS="$LDFLAGS --coverage"],
[AC_MSG_ERROR("lcov testing requested but --coverage linker flag does not work")])
AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"],
@@ -768,6 +805,14 @@ if test x$use_hardening != xno; then
AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"])
+ AX_CHECK_COMPILE_FLAG([-fcf-protection=full],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"])
+
+ dnl stack-clash-protection does not work properly when building for Windows.
+ dnl We use the test case from https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458
+ dnl to determine if it can be enabled.
+ AX_CHECK_COMPILE_FLAG([-fstack-clash-protection],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"],[],["-O0"],
+ [AC_LANG_SOURCE([[class D {public: unsigned char buf[32768];}; int main() {D d; return 0;}]])])
+
dnl When enable_debug is yes, all optimizations are disabled.
dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning.
dnl Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes.
@@ -1012,13 +1057,13 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdint.h>
[ AC_MSG_RESULT(no)]
)
-dnl LevelDB platform checks
AC_MSG_CHECKING(for fdatasync)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>]],
[[ fdatasync(0); ]])],
[ AC_MSG_RESULT(yes); HAVE_FDATASYNC=1 ],
[ AC_MSG_RESULT(no); HAVE_FDATASYNC=0 ]
)
+AC_DEFINE_UNQUOTED([HAVE_FDATASYNC], [$HAVE_FDATASYNC], [Define to 1 if fdatasync is available.])
AC_MSG_CHECKING(for F_FULLFSYNC)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>]],
@@ -1114,6 +1159,18 @@ AC_SUBST(LEVELDB_CPPFLAGS)
AC_SUBST(LIBLEVELDB)
AC_SUBST(LIBMEMENV)
+dnl SUPPRESSED_CPPFLAGS=SUPPRESS_WARNINGS([$SOME_CPPFLAGS])
+dnl Replace -I with -isystem in $SOME_CPPFLAGS to suppress warnings from
+dnl headers from its include directories and return the result.
+dnl See -isystem documentation:
+dnl https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html
+dnl https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-isystem-directory
+dnl Do not change "-I/usr/include" to "-isystem /usr/include" because that
+dnl is not necessary (/usr/include is already a system directory) and because
+dnl it would break GCC's #include_next.
+AC_DEFUN([SUPPRESS_WARNINGS],
+ [$(echo $1 |${SED} -E -e 's/(^| )-I/\1-isystem /g' -e 's;-isystem /usr/include([/ ]|$);-I/usr/include\1;g')])
+
dnl enable-fuzz should disable all other targets
if test "x$enable_fuzz" = "xyes"; then
AC_MSG_WARN(enable-fuzz will disable all other targets)
@@ -1130,16 +1187,44 @@ if test "x$enable_fuzz" = "xyes"; then
use_bench=no
use_upnp=no
use_zmq=no
+
+ AC_MSG_CHECKING([whether main function is needed])
+ AX_CHECK_LINK_FLAG(
+ [[-fsanitize=$use_sanitizers]],
+ [AC_MSG_RESULT([no])],
+ [AC_MSG_RESULT([yes])
+ CPPFLAGS="$CPPFLAGS -DPROVIDE_MAIN_FUNCTION"],
+ [],
+ [AC_LANG_PROGRAM([[
+ #include <cstdint>
+ #include <cstddef>
+ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; }
+ /* unterminated comment to remove the main function ...
+ ]],[[]])])
else
BITCOIN_QT_INIT
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
- BITCOIN_QT_CONFIGURE
+ BITCOIN_QT_CONFIGURE([5.5.1])
+
+ dnl Keep a copy of the original $QT_INCLUDES and use it when invoking qt's moc
+ QT_INCLUDES_UNSUPPRESSED=$QT_INCLUDES
+ if test x$suppress_external_warnings != xno ; then
+ QT_INCLUDES=SUPPRESS_WARNINGS($QT_INCLUDES)
+ QT_DBUS_INCLUDES=SUPPRESS_WARNINGS($QT_DBUS_INCLUDES)
+ QT_TEST_INCLUDES=SUPPRESS_WARNINGS($QT_TEST_INCLUDES)
+ fi
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
+ BDB_CPPFLAGS=SUPPRESS_WARNINGS($BDB_CPPFLAGS)
+ fi
+
+ dnl Check for sqlite3
+ PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.17], , [AC_MSG_ERROR([sqlite3 not found.])])
fi
dnl Check for libminiupnpc (optional)
@@ -1194,6 +1279,10 @@ AX_BOOST_THREAD
dnl Opt-in to boost-process
AS_IF([ test x$with_boost_process != x ], [ AX_BOOST_PROCESS ], [ ax_cv_boost_process=no ] )
+if test x$suppress_external_warnings != xno; then
+ BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS)
+fi
+
dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic
dnl counter implementations. In 1.63 and later the std::atomic approach is default.
m4_pattern_allow(DBOOST_AC_USE_STD_ATOMIC) dnl otherwise it's treated like a macro
@@ -1557,6 +1646,7 @@ AC_SUBST(LIBTOOL_APP_LDFLAGS)
AC_SUBST(USE_UPNP)
AC_SUBST(USE_QRCODE)
AC_SUBST(BOOST_LIBS)
+AC_SUBST(SQLITE_LIBS)
AC_SUBST(TESTDEFS)
AC_SUBST(MINIUPNPC_CPPFLAGS)
AC_SUBST(MINIUPNPC_LIBS)
@@ -1610,7 +1700,7 @@ if test x$need_bundled_univalue = xyes; then
AC_CONFIG_SUBDIRS([src/univalue])
fi
-ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery"
+ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_OUTPUT
@@ -1649,10 +1739,10 @@ echo " target os = $TARGET_OS"
echo " build os = $build_os"
echo
echo " CC = $CC"
-echo " CFLAGS = $CFLAGS"
+echo " CFLAGS = $PTHREAD_CFLAGS $CFLAGS"
echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS"
echo " CXX = $CXX"
echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $GPROF_CXXFLAGS $CXXFLAGS"
-echo " LDFLAGS = $PTHREAD_CFLAGS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS"
+echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS"
echo " ARFLAGS = $ARFLAGS"
echo
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index e86ff83798..65f9a2e5c9 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -78,7 +78,7 @@ script: |
echo "REAL=\`which -a ${i}-${prog}-8 | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
echo "export LD_PRELOAD='/usr/\$LIB/faketime/libfaketime.so.1'" >> ${WRAP_DIR}/${i}-${prog}
echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
- echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ echo "\$REAL \"\$@\"" >> $WRAP_DIR/${i}-${prog}
chmod +x ${WRAP_DIR}/${i}-${prog}
fi
done
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index d05b6d426d..5f671b95ce 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -81,7 +81,7 @@ script: |
echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
echo "export LD_PRELOAD='/usr/\$LIB/faketime/libfaketime.so.1'" >> ${WRAP_DIR}/${i}-${prog}
echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
- echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
+ echo "\$REAL \"\$@\"" >> $WRAP_DIR/${i}-${prog}
chmod +x ${WRAP_DIR}/${i}-${prog}
done
done
diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg
index 5990b9307a..5f566261ca 100644
--- a/contrib/linearize/example-linearize.cfg
+++ b/contrib/linearize/example-linearize.cfg
@@ -13,6 +13,9 @@ port=8332
#regtest default
#port=18443
+#signet default
+#port=38332
+
# bootstrap.dat hashlist settings (linearize-hashes)
max_height=313000
@@ -33,6 +36,11 @@ input=/home/example/.bitcoin/blocks
#genesis=0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
#input=/home/example/.bitcoin/regtest/blocks
+# signet
+#netmagic=0a03cf40
+#genesis=00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6
+#input=/home/example/.bitcoin/signet/blocks
+
# "output" option causes blockchain files to be written to the given location,
# with "output_file" ignored. If not used, "output_file" is used instead.
# output=/home/example/blockchain_directory
diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py
index a00acb1f41..49320d92e6 100755
--- a/contrib/testgen/gen_key_io_test_vectors.py
+++ b/contrib/testgen/gen_key_io_test_vectors.py
@@ -15,8 +15,7 @@ import os
from itertools import islice
from base58 import b58encode_chk, b58decode_chk, b58chars
import random
-from binascii import b2a_hex
-from segwit_addr import bech32_encode, decode, convertbits, CHARSET
+from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET
# key types
PUBKEY_ADDRESS = 0
@@ -109,7 +108,7 @@ def is_valid(v):
def is_valid_bech32(v):
'''Check vector v for bech32 validity'''
for hrp in ['bc', 'tb', 'bcrt']:
- if decode(hrp, v) != (None, None):
+ if decode_segwit_address(hrp, v) != (None, None):
return True
return False
@@ -141,9 +140,7 @@ def gen_valid_vectors():
rv, payload = valid_vector_generator(template)
assert is_valid(rv)
metadata = {x: y for x, y in zip(metadata_keys,template[3]) if y is not None}
- hexrepr = b2a_hex(payload)
- if isinstance(hexrepr, bytes):
- hexrepr = hexrepr.decode('utf8')
+ hexrepr = payload.hex()
yield (rv, hexrepr, metadata)
def gen_invalid_base58_vector(template):
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 06893407f5..8b8503331d 100644..100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -11,7 +11,8 @@
-zmqpubrawtx=tcp://127.0.0.1:28332 \
-zmqpubrawblock=tcp://127.0.0.1:28332 \
-zmqpubhashtx=tcp://127.0.0.1:28332 \
- -zmqpubhashblock=tcp://127.0.0.1:28332
+ -zmqpubhashblock=tcp://127.0.0.1:28332 \
+ -zmqpubsequence=tcp://127.0.0.1:28332
We use the asyncio library here. `self.handle()` installs itself as a
future at the end of the function. Since it never returns with the event
@@ -47,16 +48,14 @@ class ZMQHandler():
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashtx")
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawblock")
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
+ self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "sequence")
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
async def handle(self) :
- msg = await self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
+ topic, body, seq = await self.zmqSubSocket.recv_multipart()
sequence = "Unknown"
- if len(msg[-1]) == 4:
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- sequence = str(msgSequence)
+ if len(seq) == 4:
+ sequence = str(struct.unpack('<I', seq)[-1])
if topic == b"hashblock":
print('- HASH BLOCK ('+sequence+') -')
print(binascii.hexlify(body))
@@ -69,6 +68,12 @@ class ZMQHandler():
elif topic == b"rawtx":
print('- RAW TX ('+sequence+') -')
print(binascii.hexlify(body))
+ elif topic == b"sequence":
+ hash = binascii.hexlify(body[:32])
+ label = chr(body[32])
+ mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
+ print('- SEQUENCE ('+sequence+') -')
+ print(hash, label, mempool_sequence)
# schedule ourselves to receive the next message
asyncio.ensure_future(self.handle())
diff --git a/depends/Makefile b/depends/Makefile
index 2bc5df974a..1ad21f6821 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -134,7 +134,10 @@ qrencode_packages_$(NO_QR) = $(qrencode_packages)
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_)
-wallet_packages_$(NO_WALLET) = $(wallet_packages)
+bdb_packages_$(NO_BDB) = $(bdb_packages)
+sqlite_packages_$(NO_SQLITE) = $(sqlite_packages)
+wallet_packages_$(NO_WALLET) = $(bdb_packages_) $(sqlite_packages_)
+
upnp_packages_$(NO_UPNP) = $(upnp_packages)
zmq_packages_$(NO_ZMQ) = $(zmq_packages)
multiprocess_packages_$(MULTIPROCESS) = $(multiprocess_packages)
diff --git a/depends/README.md b/depends/README.md
index 63b265bcf9..5225a6d5c4 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -99,6 +99,10 @@ The following can be set when running make: `make FOO=bar`
<dd>Don't download/build/cache packages needed for enabling zeromq</dd>
<dt>NO_WALLET</dt>
<dd>Don't download/build/cache libs needed to enable the wallet</dd>
+<dt>NO_BDB</dt>
+<dd>Don't download/build/cache BerkeleyDB</dd>
+<dt>NO_SQLITE</dt>
+<dd>Don't download/build/cache SQLite</dd>
<dt>NO_UPNP</dt>
<dd>Don't download/build/cache packages needed for enabling upnp</dd>
<dt>ALLOW_HOST_PACKAGES</dt>
diff --git a/depends/funcs.mk b/depends/funcs.mk
index 6fc20543bb..58d882eb05 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -157,12 +157,17 @@ ifneq ($($(1)_ldflags),)
$(1)_autoconf += LDFLAGS="$$($(1)_ldflags)"
endif
-$(1)_cmake=cmake -DCMAKE_INSTALL_PREFIX=$($($(1)_type)_prefix)
+$(1)_cmake=env CC="$$($(1)_cc)" \
+ CFLAGS="$$($(1)_cppflags) $$($(1)_cflags)" \
+ CXX="$$($(1)_cxx)" \
+ CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \
+ LDFLAGS="$$($(1)_ldflags)" \
+ cmake -DCMAKE_INSTALL_PREFIX:PATH="$$($($(1)_type)_prefix)"
ifneq ($($(1)_type),build)
ifneq ($(host),$(build))
-$(1)_cmake += -DCMAKE_SYSTEM_NAME=$($(host_os)_cmake_system) -DCMAKE_SYSROOT=$(host_prefix)
-$(1)_cmake += -DCMAKE_C_COMPILER_TARGET=$(host) -DCMAKE_C_COMPILER=$(firstword $($($(1)_type)_CC)) -DCMAKE_C_FLAGS="$(wordlist 2,1000,$($($(1)_type)_CC))"
-$(1)_cmake += -DCMAKE_CXX_COMPILER_TARGET=$(host) -DCMAKE_CXX_COMPILER=$(firstword $($($(1)_type)_CXX)) -DCMAKE_CXX_FLAGS="$(wordlist 2,1000,$($($(1)_type)_CXX))"
+$(1)_cmake += -DCMAKE_SYSTEM_NAME=$($(host_os)_cmake_system)
+$(1)_cmake += -DCMAKE_C_COMPILER_TARGET=$(host)
+$(1)_cmake += -DCMAKE_CXX_COMPILER_TARGET=$(host)
endif
endif
endef
@@ -260,4 +265,4 @@ $(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$
$(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package))))
#special exception: if a toolchain package exists, all non-native packages depend on it
-$(foreach package,$(packages),$(eval $($(package)_unpacked): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) $($($(host_arch)_$(host_os)_native_binutils)_cached) ))
+$(foreach package,$(packages),$(eval $($(package)_extracted): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) $($($(host_arch)_$(host_os)_native_binutils)_cached) ))
diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk
index b679438c6f..06cf974f75 100644
--- a/depends/packages/bdb.mk
+++ b/depends/packages/bdb.mk
@@ -4,6 +4,7 @@ $(package)_download_path=https://download.oracle.com/berkeley-db
$(package)_file_name=db-$($(package)_version).NC.tar.gz
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
$(package)_build_subdir=build_unix
+$(package)_patches=clang_cxx_11.patch
define $(package)_set_vars
$(package)_config_opts=--disable-shared --enable-cxx --disable-replication --enable-option-checking
@@ -14,8 +15,7 @@ $(package)_cppflags_mingw32=-DUNICODE -D_UNICODE
endef
define $(package)_preprocess_cmds
- sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \
- sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \
+ patch -p1 < $($(package)_patch_dir)/clang_cxx_11.patch && \
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist
endef
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index 4f6b543aff..d8bce108b1 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -3,6 +3,7 @@ $(package)_version=1_70_0
$(package)_download_path=https://dl.bintray.com/boostorg/release/1.70.0/source/
$(package)_file_name=boost_$($(package)_version).tar.bz2
$(package)_sha256_hash=430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778
+$(package)_patches=unused_var_in_process.patch
define $(package)_set_vars
$(package)_config_opts_release=variant=release
@@ -31,9 +32,8 @@ $(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_android=-fPIC
endef
-# Fix unused variable in boost_process, can be removed after upgrading to 1.72
define $(package)_preprocess_cmds
- sed -i.old "s/int ret_sig = 0;//" boost/process/detail/posix/wait_group.hpp && \
+ patch -p1 < $($(package)_patch_dir)/unused_var_in_process.patch && \
echo "using $($(package)_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$($(package)_archiver_$(host_os))\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
endef
diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk
index 128599ba77..0d5f94f380 100644
--- a/depends/packages/fontconfig.mk
+++ b/depends/packages/fontconfig.mk
@@ -4,23 +4,23 @@ $(package)_download_path=https://www.freedesktop.org/software/fontconfig/release
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3
$(package)_dependencies=freetype expat
+$(package)_patches=remove_char_width_usage.patch gperf_header_regen.patch
define $(package)_set_vars
$(package)_config_opts=--disable-docs --disable-static --disable-libxml2 --disable-iconv
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
endef
+define $(package)_preprocess_cmds
+ patch -p1 < $($(package)_patch_dir)/remove_char_width_usage.patch && \
+ patch -p1 < $($(package)_patch_dir)/gperf_header_regen.patch
+endef
+
define $(package)_config_cmds
$($(package)_autoconf)
endef
-# 2.12.1 uses CHAR_WIDTH which is reserved and clashes with some glibc versions, but newer versions of fontconfig
-# have broken makefiles which needlessly attempt to re-generate headers with gperf.
-# Instead, change all uses of CHAR_WIDTH, and disable the rule that forces header re-generation.
-# This can be removed once the upstream build is fixed.
define $(package)_build_cmds
- sed -i 's/CHAR_WIDTH/CHARWIDTH/g' fontconfig/fontconfig.h src/fcobjshash.gperf src/fcobjs.h src/fcobjshash.h && \
- sed -i 's/fcobjshash.h: fcobjshash.gperf/fcobjshash.h:/' src/Makefile && \
$(MAKE)
endef
diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk
index fdbe22cda6..49a584e462 100644
--- a/depends/packages/miniupnpc.mk
+++ b/depends/packages/miniupnpc.mk
@@ -3,6 +3,7 @@ $(package)_version=2.0.20180203
$(package)_download_path=https://miniupnp.tuxfamily.org/files/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=90dda8c7563ca6cd4a83e23b3c66dbbea89603a1675bfdb852897c2c9cc220b7
+$(package)_patches=dont_use_wingen.patch
define $(package)_set_vars
$(package)_build_opts=CC="$($(package)_cc)"
@@ -14,7 +15,7 @@ endef
define $(package)_preprocess_cmds
mkdir dll && \
sed -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"$($(package)_version)\"|' -e 's|OS/version|$(host)|' miniupnpcstrings.h.in > miniupnpcstrings.h && \
- sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw
+ patch -p1 < $($(package)_patch_dir)/dont_use_wingen.patch
endef
define $(package)_build_cmds
diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk
index 5022ed980f..d56b636695 100644
--- a/depends/packages/native_cctools.mk
+++ b/depends/packages/native_cctools.mk
@@ -4,6 +4,8 @@ $(package)_download_path=https://github.com/tpoechtrager/cctools-port/archive
$(package)_file_name=$($(package)_version).tar.gz
$(package)_sha256_hash=e51995a843533a3dac155dd0c71362dd471597a2d23f13dff194c6285362f875
$(package)_build_subdir=cctools
+$(package)_patches=ld64_disable_threading.patch
+
ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),)
$(package)_clang_version=8.0.0
$(package)_clang_download_path=https://releases.llvm.org/$($(package)_clang_version)
@@ -78,7 +80,7 @@ endef
define $(package)_preprocess_cmds
CC=$($(package)_cc) CXX=$($(package)_cxx) INSTALLPREFIX=$($(package)_extract_dir) ./libtapi/build.sh && \
CC=$($(package)_cc) CXX=$($(package)_cxx) INSTALLPREFIX=$($(package)_extract_dir) ./libtapi/install.sh && \
- sed -i.old "/define HAVE_PTHREADS/d" $($(package)_build_subdir)/ld64/src/ld/InputFiles.h
+ patch -p1 < $($(package)_patch_dir)/ld64_disable_threading.patch
endef
define $(package)_config_cmds
diff --git a/depends/packages/native_cdrkit.mk b/depends/packages/native_cdrkit.mk
index 14c37a0fc7..7bdf2d7dfd 100644
--- a/depends/packages/native_cdrkit.mk
+++ b/depends/packages/native_cdrkit.mk
@@ -12,7 +12,7 @@ endef
# Starting with 10.1, GCC defaults to -fno-common, resulting in linking errors.
# Pass -fcommon to retain the legacy behaviour.
define $(package)_config_cmds
- cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) -DCMAKE_C_FLAGS="-fcommon"
+ $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -fcommon"
endef
define $(package)_build_cmds
diff --git a/depends/packages/native_libdmg-hfsplus.mk b/depends/packages/native_libdmg-hfsplus.mk
index c0f0ce74de..035b767188 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
- cmake -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix) -DCMAKE_C_FLAGS="-Wl,--build-id=none" ..
+ $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -Wl,--build-id=none" ..
endef
define $(package)_build_cmds
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 8fe2c771c9..4627acb521 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -10,7 +10,8 @@ qt_android_packages=qt
qt_darwin_packages=qt
qt_mingw32_packages=qt
-wallet_packages=bdb
+bdb_packages=bdb
+sqlite_packages=sqlite
zmq_packages=zeromq
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 500881e442..083bc68d66 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -8,7 +8,10 @@ $(package)_dependencies=zlib
$(package)_linux_dependencies=freetype fontconfig libxcb
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib network widgets gui plugins testlib
-$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch fix_android_qmake_conf.patch fix_android_jni_static.patch
+$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch
+$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
+$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
+$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch
# Update OSX_QT_TRANSLATIONS when this is updated
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
@@ -190,11 +193,11 @@ define $(package)_extract_cmds
endef
define $(package)_preprocess_cmds
- sed -i.old "s|FT_Get_Font_Format|FT_Get_X11_Font_Format|" qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp && \
+ patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \
+ patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
- sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \
- sed -i.old "s/src_plugins.depends = src_sql src_network/src_plugins.depends = src_network/" qtbase/src/src.pro && \
- sed -i.old -e 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' -e 's|/bin/pwd|pwd|' qtbase/configure && \
+ patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \
+ patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch &&\
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/Info.plist.lib qtbase/mkspecs/macx-clang-linux/ &&\
cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\
diff --git a/depends/packages/sqlite.mk b/depends/packages/sqlite.mk
new file mode 100644
index 0000000000..5b3a61b239
--- /dev/null
+++ b/depends/packages/sqlite.mk
@@ -0,0 +1,26 @@
+package=sqlite
+$(package)_version=3320100
+$(package)_download_path=https://sqlite.org/2020/
+$(package)_file_name=sqlite-autoconf-$($(package)_version).tar.gz
+$(package)_sha256_hash=486748abfb16abd8af664e3a5f03b228e5f124682b0c942e157644bf6fff7d10
+
+define $(package)_set_vars
+$(package)_config_opts=--disable-shared --disable-readline --disable-dynamic-extensions --enable-option-checking
+$(package)_config_opts_linux=--with-pic
+endef
+
+define $(package)_config_cmds
+ $($(package)_autoconf)
+endef
+
+define $(package)_build_cmds
+ $(MAKE) libsqlite3.la
+endef
+
+define $(package)_stage_cmds
+ $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA
+endef
+
+define $(package)_postprocess_cmds
+ rm lib/*.la
+endef
diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk
index 6f35ede248..c93aa1a74d 100644
--- a/depends/packages/zeromq.mk
+++ b/depends/packages/zeromq.mk
@@ -3,7 +3,7 @@ $(package)_version=4.3.1
$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d835cd21eb
-$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch
+$(package)_patches=remove_libstd_link.patch
define $(package)_set_vars
$(package)_config_opts=--without-docs --disable-shared --disable-curve --disable-curve-keygen --disable-perf
@@ -16,9 +16,8 @@ define $(package)_set_vars
endef
define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \
- patch -p1 < $($(package)_patch_dir)/0002-disable-pthread_set_name_np.patch && \
- cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub config
+ patch -p1 < $($(package)_patch_dir)/remove_libstd_link.patch && \
+ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub config
endef
define $(package)_config_cmds
@@ -34,6 +33,5 @@ define $(package)_stage_cmds
endef
define $(package)_postprocess_cmds
- sed -i.old "s/ -lstdc++//" lib/pkgconfig/libzmq.pc && \
rm -rf bin share lib/*.la
endef
diff --git a/depends/patches/bdb/clang_cxx_11.patch b/depends/patches/bdb/clang_cxx_11.patch
new file mode 100644
index 0000000000..58f7ddc7d5
--- /dev/null
+++ b/depends/patches/bdb/clang_cxx_11.patch
@@ -0,0 +1,147 @@
+commit 3311d68f11d1697565401eee6efc85c34f022ea7
+Author: fanquake <fanquake@gmail.com>
+Date: Mon Aug 17 20:03:56 2020 +0800
+
+ Fix C++11 compatibility
+
+diff --git a/dbinc/atomic.h b/dbinc/atomic.h
+index 0034dcc..7c11d4a 100644
+--- a/dbinc/atomic.h
++++ b/dbinc/atomic.h
+@@ -70,7 +70,7 @@ typedef struct {
+ * These have no memory barriers; the caller must include them when necessary.
+ */
+ #define atomic_read(p) ((p)->value)
+-#define atomic_init(p, val) ((p)->value = (val))
++#define atomic_init_db(p, val) ((p)->value = (val))
+
+ #ifdef HAVE_ATOMIC_SUPPORT
+
+@@ -144,7 +144,7 @@ typedef LONG volatile *interlocked_val;
+ #define atomic_inc(env, p) __atomic_inc(p)
+ #define atomic_dec(env, p) __atomic_dec(p)
+ #define atomic_compare_exchange(env, p, o, n) \
+- __atomic_compare_exchange((p), (o), (n))
++ __atomic_compare_exchange_db((p), (o), (n))
+ static inline int __atomic_inc(db_atomic_t *p)
+ {
+ int temp;
+@@ -176,7 +176,7 @@ static inline int __atomic_dec(db_atomic_t *p)
+ * http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
+ * which configure could be changed to use.
+ */
+-static inline int __atomic_compare_exchange(
++static inline int __atomic_compare_exchange_db(
+ db_atomic_t *p, atomic_value_t oldval, atomic_value_t newval)
+ {
+ atomic_value_t was;
+@@ -206,7 +206,7 @@ static inline int __atomic_compare_exchange(
+ #define atomic_dec(env, p) (--(p)->value)
+ #define atomic_compare_exchange(env, p, oldval, newval) \
+ (DB_ASSERT(env, atomic_read(p) == (oldval)), \
+- atomic_init(p, (newval)), 1)
++ atomic_init_db(p, (newval)), 1)
+ #else
+ #define atomic_inc(env, p) __atomic_inc(env, p)
+ #define atomic_dec(env, p) __atomic_dec(env, p)
+diff --git a/mp/mp_fget.c b/mp/mp_fget.c
+index 5fdee5a..0b75f57 100644
+--- a/mp/mp_fget.c
++++ b/mp/mp_fget.c
+@@ -617,7 +617,7 @@ alloc: /* Allocate a new buffer header and data space. */
+
+ /* Initialize enough so we can call __memp_bhfree. */
+ alloc_bhp->flags = 0;
+- atomic_init(&alloc_bhp->ref, 1);
++ atomic_init_db(&alloc_bhp->ref, 1);
+ #ifdef DIAGNOSTIC
+ if ((uintptr_t)alloc_bhp->buf & (sizeof(size_t) - 1)) {
+ __db_errx(env,
+@@ -911,7 +911,7 @@ alloc: /* Allocate a new buffer header and data space. */
+ MVCC_MPROTECT(bhp->buf, mfp->stat.st_pagesize,
+ PROT_READ);
+
+- atomic_init(&alloc_bhp->ref, 1);
++ atomic_init_db(&alloc_bhp->ref, 1);
+ MUTEX_LOCK(env, alloc_bhp->mtx_buf);
+ alloc_bhp->priority = bhp->priority;
+ alloc_bhp->pgno = bhp->pgno;
+diff --git a/mp/mp_mvcc.c b/mp/mp_mvcc.c
+index 34467d2..f05aa0c 100644
+--- a/mp/mp_mvcc.c
++++ b/mp/mp_mvcc.c
+@@ -276,7 +276,7 @@ __memp_bh_freeze(dbmp, infop, hp, bhp, need_frozenp)
+ #else
+ memcpy(frozen_bhp, bhp, SSZA(BH, buf));
+ #endif
+- atomic_init(&frozen_bhp->ref, 0);
++ atomic_init_db(&frozen_bhp->ref, 0);
+ if (mutex != MUTEX_INVALID)
+ frozen_bhp->mtx_buf = mutex;
+ else if ((ret = __mutex_alloc(env, MTX_MPOOL_BH,
+@@ -428,7 +428,7 @@ __memp_bh_thaw(dbmp, infop, hp, frozen_bhp, alloc_bhp)
+ #endif
+ alloc_bhp->mtx_buf = mutex;
+ MUTEX_LOCK(env, alloc_bhp->mtx_buf);
+- atomic_init(&alloc_bhp->ref, 1);
++ atomic_init_db(&alloc_bhp->ref, 1);
+ F_CLR(alloc_bhp, BH_FROZEN);
+ }
+
+diff --git a/mp/mp_region.c b/mp/mp_region.c
+index e6cece9..ddbe906 100644
+--- a/mp/mp_region.c
++++ b/mp/mp_region.c
+@@ -224,7 +224,7 @@ __memp_init(env, dbmp, reginfo_off, htab_buckets, max_nreg)
+ MTX_MPOOL_FILE_BUCKET, 0, &htab[i].mtx_hash)) != 0)
+ return (ret);
+ SH_TAILQ_INIT(&htab[i].hash_bucket);
+- atomic_init(&htab[i].hash_page_dirty, 0);
++ atomic_init_db(&htab[i].hash_page_dirty, 0);
+ }
+
+ /*
+@@ -269,7 +269,7 @@ __memp_init(env, dbmp, reginfo_off, htab_buckets, max_nreg)
+ hp->mtx_hash = (mtx_base == MUTEX_INVALID) ? MUTEX_INVALID :
+ mtx_base + i;
+ SH_TAILQ_INIT(&hp->hash_bucket);
+- atomic_init(&hp->hash_page_dirty, 0);
++ atomic_init_db(&hp->hash_page_dirty, 0);
+ #ifdef HAVE_STATISTICS
+ hp->hash_io_wait = 0;
+ hp->hash_frozen = hp->hash_thawed = hp->hash_frozen_freed = 0;
+diff --git a/mutex/mut_method.c b/mutex/mut_method.c
+index 2588763..5c6d516 100644
+--- a/mutex/mut_method.c
++++ b/mutex/mut_method.c
+@@ -426,7 +426,7 @@ atomic_compare_exchange(env, v, oldval, newval)
+ MUTEX_LOCK(env, mtx);
+ ret = atomic_read(v) == oldval;
+ if (ret)
+- atomic_init(v, newval);
++ atomic_init_db(v, newval);
+ MUTEX_UNLOCK(env, mtx);
+
+ return (ret);
+diff --git a/mutex/mut_tas.c b/mutex/mut_tas.c
+index f3922e0..e40fcdf 100644
+--- a/mutex/mut_tas.c
++++ b/mutex/mut_tas.c
+@@ -46,7 +46,7 @@ __db_tas_mutex_init(env, mutex, flags)
+
+ #ifdef HAVE_SHARED_LATCHES
+ if (F_ISSET(mutexp, DB_MUTEX_SHARED))
+- atomic_init(&mutexp->sharecount, 0);
++ atomic_init_db(&mutexp->sharecount, 0);
+ else
+ #endif
+ if (MUTEX_INIT(&mutexp->tas)) {
+@@ -486,7 +486,7 @@ __db_tas_mutex_unlock(env, mutex)
+ F_CLR(mutexp, DB_MUTEX_LOCKED);
+ /* Flush flag update before zeroing count */
+ MEMBAR_EXIT();
+- atomic_init(&mutexp->sharecount, 0);
++ atomic_init_db(&mutexp->sharecount, 0);
+ } else {
+ DB_ASSERT(env, sharecount > 0);
+ MEMBAR_EXIT();
diff --git a/depends/patches/boost/unused_var_in_process.patch b/depends/patches/boost/unused_var_in_process.patch
new file mode 100644
index 0000000000..722f7bb5ea
--- /dev/null
+++ b/depends/patches/boost/unused_var_in_process.patch
@@ -0,0 +1,22 @@
+commit dbd95cdaefdea95307d004f019a1c394cf9389f0
+Author: fanquake <fanquake@gmail.com>
+Date: Mon Aug 17 20:15:17 2020 +0800
+
+ Remove unused variable in Boost Process
+
+ This causes issues with our linters / CI.
+
+ Can be removed once depends Boost is 1.71.0 or later.
+
+diff --git a/boost/process/detail/posix/wait_group.hpp b/boost/process/detail/posix/wait_group.hpp
+index 9dc249803..2502d9772 100644
+--- a/boost/process/detail/posix/wait_group.hpp
++++ b/boost/process/detail/posix/wait_group.hpp
+@@ -137,7 +137,6 @@ inline bool wait_until(
+
+ do
+ {
+- int ret_sig = 0;
+ int status;
+ if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
+ && (WIFEXITED(status) || WIFSIGNALED(status)))
diff --git a/depends/patches/fontconfig/gperf_header_regen.patch b/depends/patches/fontconfig/gperf_header_regen.patch
new file mode 100644
index 0000000000..7401b83d84
--- /dev/null
+++ b/depends/patches/fontconfig/gperf_header_regen.patch
@@ -0,0 +1,24 @@
+commit 7b6eb33ecd88768b28c67ce5d2d68a7eed5936b6
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 25 14:34:53 2020 +0800
+
+ Remove rule that causes inadvertant header regeneration
+
+ Otherwise the makefile will needlessly attempt to re-generate the
+ headers with gperf. This can be dropped once the upstream build is fixed.
+
+ See #10851.
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index f4626ad..4ae1b00 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -903,7 +903,7 @@ fcobjshash.gperf: fcobjshash.gperf.h fcobjs.h
+ ' - > $@.tmp && \
+ mv -f $@.tmp $@ || ( $(RM) $@.tmp && false )
+
+-fcobjshash.h: fcobjshash.gperf
++fcobjshash.h:
+ $(AM_V_GEN) $(GPERF) -m 100 $< > $@.tmp && \
+ mv -f $@.tmp $@ || ( $(RM) $@.tmp && false )
+
diff --git a/depends/patches/fontconfig/remove_char_width_usage.patch b/depends/patches/fontconfig/remove_char_width_usage.patch
new file mode 100644
index 0000000000..9f69081890
--- /dev/null
+++ b/depends/patches/fontconfig/remove_char_width_usage.patch
@@ -0,0 +1,62 @@
+commit 28165a9b078583dc8e9e5c344510e37582284cef
+Author: fanquake <fanquake@gmail.com>
+Date: Mon Aug 17 20:35:42 2020 +0800
+
+ Remove usage of CHAR_WIDTH
+
+ CHAR_WIDTH which is reserved and clashes with glibc 2.25+
+
+ See #10851.
+
+diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
+index 5c72b22..843c532 100644
+--- a/fontconfig/fontconfig.h
++++ b/fontconfig/fontconfig.h
+@@ -128,7 +128,7 @@ typedef int FcBool;
+ #define FC_USER_CACHE_FILE ".fonts.cache-" FC_CACHE_VERSION
+
+ /* Adjust outline rasterizer */
+-#define FC_CHAR_WIDTH "charwidth" /* Int */
++#define FC_CHARWIDTH "charwidth" /* Int */
+ #define FC_CHAR_HEIGHT "charheight"/* Int */
+ #define FC_MATRIX "matrix" /* FcMatrix */
+
+diff --git a/src/fcobjs.h b/src/fcobjs.h
+index 1fc4f65..d27864b 100644
+--- a/src/fcobjs.h
++++ b/src/fcobjs.h
+@@ -51,7 +51,7 @@ FC_OBJECT (DPI, FcTypeDouble, NULL)
+ FC_OBJECT (RGBA, FcTypeInteger, NULL)
+ FC_OBJECT (SCALE, FcTypeDouble, NULL)
+ FC_OBJECT (MINSPACE, FcTypeBool, NULL)
+-FC_OBJECT (CHAR_WIDTH, FcTypeInteger, NULL)
++FC_OBJECT (CHARWIDTH, FcTypeInteger, NULL)
+ FC_OBJECT (CHAR_HEIGHT, FcTypeInteger, NULL)
+ FC_OBJECT (MATRIX, FcTypeMatrix, NULL)
+ FC_OBJECT (CHARSET, FcTypeCharSet, FcCompareCharSet)
+diff --git a/src/fcobjshash.gperf b/src/fcobjshash.gperf
+index 80a0237..eb4ad84 100644
+--- a/src/fcobjshash.gperf
++++ b/src/fcobjshash.gperf
+@@ -44,7 +44,7 @@ int id;
+ "rgba",FC_RGBA_OBJECT
+ "scale",FC_SCALE_OBJECT
+ "minspace",FC_MINSPACE_OBJECT
+-"charwidth",FC_CHAR_WIDTH_OBJECT
++"charwidth",FC_CHARWIDTH_OBJECT
+ "charheight",FC_CHAR_HEIGHT_OBJECT
+ "matrix",FC_MATRIX_OBJECT
+ "charset",FC_CHARSET_OBJECT
+diff --git a/src/fcobjshash.h b/src/fcobjshash.h
+index 5a4d1ea..4e66bb0 100644
+--- a/src/fcobjshash.h
++++ b/src/fcobjshash.h
+@@ -284,7 +284,7 @@ FcObjectTypeLookup (register const char *str, register unsigned int len)
+ {(int)(long)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str43,FC_CHARSET_OBJECT},
+ {-1},
+ #line 47 "fcobjshash.gperf"
+- {(int)(long)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str45,FC_CHAR_WIDTH_OBJECT},
++ {(int)(long)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str45,FC_CHARWIDTH_OBJECT},
+ #line 48 "fcobjshash.gperf"
+ {(int)(long)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str46,FC_CHAR_HEIGHT_OBJECT},
+ #line 55 "fcobjshash.gperf"
diff --git a/depends/patches/miniupnpc/dont_use_wingen.patch b/depends/patches/miniupnpc/dont_use_wingen.patch
new file mode 100644
index 0000000000..a1cc9b50d1
--- /dev/null
+++ b/depends/patches/miniupnpc/dont_use_wingen.patch
@@ -0,0 +1,26 @@
+commit e8077044df239bcf0d9e9980b0e1afb9f1f5c446
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 18 20:50:19 2020 +0800
+
+ Don't use wingenminiupnpcstrings when generating miniupnpcstrings.h
+
+ The wingenminiupnpcstrings tool is used on Windows to generate version
+ information. This information is irrelevant for us, and trying to use
+ wingenminiupnpcstrings would cause builds to fail, so just don't use it.
+
+ We should be able to drop this once we are using 2.1 or later. See
+ upstream commit: 9663c55c61408fdcc39a82987d2243f816b22932.
+
+diff --git a/Makefile.mingw b/Makefile.mingw
+index 574720e..fcc17bb 100644
+--- a/Makefile.mingw
++++ b/Makefile.mingw
+@@ -74,7 +74,7 @@ wingenminiupnpcstrings: wingenminiupnpcstrings.o
+
+ wingenminiupnpcstrings.o: wingenminiupnpcstrings.c
+
+-miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings
++miniupnpcstrings.h: miniupnpcstrings.h.in
+ wingenminiupnpcstrings $< $@
+
+ minixml.o: minixml.c minixml.h
diff --git a/depends/patches/native_cctools/ld64_disable_threading.patch b/depends/patches/native_cctools/ld64_disable_threading.patch
new file mode 100644
index 0000000000..d6c58c102f
--- /dev/null
+++ b/depends/patches/native_cctools/ld64_disable_threading.patch
@@ -0,0 +1,26 @@
+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 noticable 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/dont_hardcode_pwd.patch b/depends/patches/qt/dont_hardcode_pwd.patch
new file mode 100644
index 0000000000..a74e9cb098
--- /dev/null
+++ b/depends/patches/qt/dont_hardcode_pwd.patch
@@ -0,0 +1,27 @@
+commit 0e953866fc4672486e29e1ba6d83b4207e7b2f0b
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 18 15:09:06 2020 +0800
+
+ Don't hardcode pwd path
+
+ Let a man use his builtins if he wants to! Also, removes the unnecessary
+ assumption that pwd lives under /bin/pwd.
+
+ See #15581.
+
+diff --git a/qtbase/configure b/qtbase/configure
+index 08b49a8d..faea5b55 100755
+--- a/qtbase/configure
++++ b/qtbase/configure
+@@ -36,9 +36,9 @@
+ relconf=`basename $0`
+ # the directory of this script is the "source tree"
+ relpath=`dirname $0`
+-relpath=`(cd "$relpath"; /bin/pwd)`
++relpath=`(cd "$relpath"; pwd)`
+ # the current directory is the "build tree" or "object tree"
+-outpath=`/bin/pwd`
++outpath=`pwd`
+
+ WHICH="which"
+
diff --git a/depends/patches/qt/drop_lrelease_dependency.patch b/depends/patches/qt/drop_lrelease_dependency.patch
new file mode 100644
index 0000000000..f6b2c9fc80
--- /dev/null
+++ b/depends/patches/qt/drop_lrelease_dependency.patch
@@ -0,0 +1,20 @@
+commit 67b3ed7406e1d0762188dbad2c44a06824ba0778
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 18 15:24:01 2020 +0800
+
+ Drop dependency on lrelease
+
+ Qts buildsystem insists on using the installed lrelease, but gets
+ confused about how to find it. Since we manually control the build
+ order, just drop the dependency.
+
+ See #9469
+
+diff --git a/qttranslations/translations/translations.pro b/qttranslations/translations/translations.pro
+index 694544c..eff339d 100644
+--- a/qttranslations/translations/translations.pro
++++ b/qttranslations/translations/translations.pro
+@@ -109,3 +109,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT}
+ silent:updateqm.commands = @echo lrelease ${QMAKE_FILE_IN} && $$updateqm.commands
+-updateqm.depends = $$LRELEASE_EXE
+ updateqm.name = LRELEASE ${QMAKE_FILE_IN}
diff --git a/depends/patches/qt/fix_powerpc_libpng.patch b/depends/patches/qt/fix_powerpc_libpng.patch
new file mode 100644
index 0000000000..d37b6c7776
--- /dev/null
+++ b/depends/patches/qt/fix_powerpc_libpng.patch
@@ -0,0 +1,23 @@
+commit 6f9feb773a43c5abfa3455da2e324180e789285b
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Sep 15 21:44:31 2020 +0800
+
+ Fix PowerPC build of libpng
+
+ See https://bugreports.qt.io/browse/QTBUG-66388.
+
+ Can be dropped when we are building qt 5.12.0 or later.
+
+diff --git a/qtbase/src/3rdparty/libpng/libpng.pro b/qtbase/src/3rdparty/libpng/libpng.pro
+index 577b61d8..a2f56669 100644
+--- a/qtbase/src/3rdparty/libpng/libpng.pro
++++ b/qtbase/src/3rdparty/libpng/libpng.pro
+@@ -10,7 +10,7 @@ MODULE_INCLUDEPATH = $$PWD
+
+ load(qt_helper_lib)
+
+-DEFINES += PNG_ARM_NEON_OPT=0
++DEFINES += PNG_ARM_NEON_OPT=0 PNG_POWERPC_VSX_OPT=0
+ SOURCES += \
+ png.c \
+ pngerror.c \
diff --git a/depends/patches/qt/freetype_back_compat.patch b/depends/patches/qt/freetype_back_compat.patch
new file mode 100644
index 0000000000..b0f1c98aa6
--- /dev/null
+++ b/depends/patches/qt/freetype_back_compat.patch
@@ -0,0 +1,28 @@
+commit 14bc77db61bf9d56f9b6c8b84aa02573605c19c6
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 18 15:15:08 2020 +0800
+
+ Fix backwards compatibility with older Freetype versions at runtime
+
+ A few years ago, libfreetype introduced FT_Get_Font_Format() as an alias
+ for FT_Get_X11_Font_Format(), but FT_Get_X11_Font_Format() was kept for abi
+ backwards-compatibility.
+
+ Qt 5.9 introduced a call to FT_Get_Font_Format(). Replace it with FT_Get_X11_Font_Format()
+ in order to remain compatibile with older freetype, which is still used by e.g. Ubuntu Trusty.
+
+ See #14348.
+
+diff --git a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
+index 3f543755..8ecc1c8c 100644
+--- a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
++++ b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
+@@ -898,7 +898,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
+ }
+ }
+ #if defined(FT_FONT_FORMATS_H)
+- const char *fmt = FT_Get_Font_Format(face);
++ const char *fmt = FT_Get_X11_Font_Format(face);
+ if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
+ FT_Bool no_stem_darkening = true;
+ FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
diff --git a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch
deleted file mode 100644
index b911ac5672..0000000000
--- a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From f6866b0f166ad168618aae64c7fbee8775d3eb23 Mon Sep 17 00:00:00 2001
-From: mruddy <6440430+mruddy@users.noreply.github.com>
-Date: Sat, 30 Jun 2018 09:44:58 -0400
-Subject: [PATCH] fix build with older mingw64
-
----
- src/windows.hpp | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/src/windows.hpp b/src/windows.hpp
-index 6c3839fd..2c32ec79 100644
---- a/src/windows.hpp
-+++ b/src/windows.hpp
-@@ -58,6 +58,13 @@
- #include <winsock2.h>
- #include <windows.h>
- #include <mswsock.h>
-+
-+#if defined __MINGW64_VERSION_MAJOR && __MINGW64_VERSION_MAJOR < 4
-+// Workaround for mingw-w64 < v4.0 which did not include ws2ipdef.h in iphlpapi.h.
-+// Fixed in mingw-w64 by 9bd8fe9148924840d315b4c915dd099955ea89d1.
-+#include <ws2def.h>
-+#include <ws2ipdef.h>
-+#endif
- #include <iphlpapi.h>
-
- #if !defined __MINGW32__
---
-2.17.1
-
diff --git a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch
deleted file mode 100644
index b1c6f78a70..0000000000
--- a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From c9bbdd6581d07acfe8971e4bcebe278a3676cf03 Mon Sep 17 00:00:00 2001
-From: mruddy <6440430+mruddy@users.noreply.github.com>
-Date: Sat, 30 Jun 2018 09:57:18 -0400
-Subject: [PATCH] disable pthread_set_name_np
-
-pthread_set_name_np adds a Glibc requirement on >= 2.12.
----
- src/thread.cpp | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/src/thread.cpp b/src/thread.cpp
-index a1086b0c..9943f354 100644
---- a/src/thread.cpp
-+++ b/src/thread.cpp
-@@ -308,7 +308,7 @@ void zmq::thread_t::setThreadName (const char *name_)
- */
- if (!name_)
- return;
--
-+#if 0
- #if defined(ZMQ_HAVE_PTHREAD_SETNAME_1)
- int rc = pthread_setname_np (name_);
- if (rc)
-@@ -324,6 +324,8 @@ void zmq::thread_t::setThreadName (const char *name_)
- #elif defined(ZMQ_HAVE_PTHREAD_SET_NAME)
- pthread_set_name_np (_descriptor, name_);
- #endif
-+#endif
-+ return;
- }
-
- #endif
---
-2.17.1
-
diff --git a/depends/patches/zeromq/remove_libstd_link.patch b/depends/patches/zeromq/remove_libstd_link.patch
new file mode 100644
index 0000000000..ddf91e6abf
--- /dev/null
+++ b/depends/patches/zeromq/remove_libstd_link.patch
@@ -0,0 +1,25 @@
+commit 47d4cd12a2c051815ddda78adebdb3923b260d8a
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Aug 18 14:45:40 2020 +0800
+
+ Remove needless linking against libstdc++
+
+ This is broken for a number of reasons, including:
+ - g++ understands "static-libstdc++ -lstdc++" to mean "link against
+ whatever libstdc++ exists, probably shared", which in itself is buggy.
+ - another stdlib (libc++ for example) may be in use
+
+ See #11981.
+
+diff --git a/src/libzmq.pc.in b/src/libzmq.pc.in
+index 233bc3a..3c2bf0d 100644
+--- a/src/libzmq.pc.in
++++ b/src/libzmq.pc.in
+@@ -7,6 +7,6 @@ Name: libzmq
+ Description: 0MQ c++ library
+ Version: @VERSION@
+ Libs: -L${libdir} -lzmq
+-Libs.private: -lstdc++ @pkg_config_libs_private@
++Libs.private: @pkg_config_libs_private@
+ Requires.private: @pkg_config_names_private@
+ Cflags: -I${includedir} @pkg_config_defines@
diff --git a/doc/bips.md b/doc/bips.md
index 456fea7a5a..8c20533c9b 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -37,9 +37,12 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)), *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)).
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
-- [`BIP 158`](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki): Compact Block Filters for Light Clients can be indexed as of **v0.19.0** ([PR #14121](https://github.com/bitcoin/bitcoin/pull/14121)).
+* [`BIP 155`](https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki): The 'addrv2' and 'sendaddrv2' messages which enable relay of Tor V3 addresses (and other networks) are supported as of **v0.21.0** ([PR 19954](https://github.com/bitcoin/bitcoin/pull/19954)).
+* [`BIP 158`](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki): Compact Block Filters for Light Clients can be indexed as of **v0.19.0** ([PR #14121](https://github.com/bitcoin/bitcoin/pull/14121)).
* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): The `NODE_NETWORK_LIMITED` service bit is signalled as of **v0.16.0** ([PR 11740](https://github.com/bitcoin/bitcoin/pull/11740)), and such nodes are connected to as of **v0.17.0** ([PR 10387](https://github.com/bitcoin/bitcoin/pull/10387)).
* [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)). Bech32 addresses are generated by default as of **v0.20.0** ([PR 16884](https://github.com/bitcoin/bitcoin/pull/16884)).
* [`BIP 174`](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): RPCs to operate on Partially Signed Bitcoin Transactions (PSBT) are present as of **v0.17.0** ([PR 13557](https://github.com/bitcoin/bitcoin/pull/13557)).
* [`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.
diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md
index 584ee43d48..2b051c078c 100644
--- a/doc/build-openbsd.md
+++ b/doc/build-openbsd.md
@@ -2,9 +2,7 @@ OpenBSD build guide
======================
(updated for OpenBSD 6.7)
-This guide describes how to build bitcoind and command-line utilities on OpenBSD.
-
-OpenBSD is most commonly used as a server OS, so this guide does not contain instructions for building the GUI.
+This guide describes how to build bitcoind, bitcoin-qt, and command-line utilities on OpenBSD.
Preparation
-------------
@@ -13,6 +11,7 @@ Run the following as root to install the base dependencies for building:
```bash
pkg_add git gmake libevent libtool boost
+pkg_add qt5 # (optional for enabling the GUI)
pkg_add autoconf # (select highest version, e.g. 2.69)
pkg_add automake # (select highest version, e.g. 1.16)
pkg_add python # (select highest version, e.g. 3.8)
@@ -80,6 +79,14 @@ To configure without wallet:
./configure --disable-wallet --with-gui=no CC=cc CC_FOR_BUILD=cc CXX=c++ MAKE=gmake
```
+To configure with GUI:
+```bash
+./configure --with-gui=yes CC=cc CXX=c++ \
+ BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" \
+ BDB_CFLAGS="-I${BDB_PREFIX}/include" \
+ MAKE=gmake
+```
+
Build and run the tests:
```bash
gmake # use -jX here for parallelism
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 7b76117c8b..2a7d71eea6 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh).
## Dependencies
```shell
-brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode
+brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode sqlite
```
If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
@@ -79,7 +79,7 @@ compiled in `disable-wallet` mode with:
./configure --disable-wallet
```
-In this case there is no dependency on Berkeley DB 4.8.
+In this case there is no dependency on Berkeley DB 4.8 and SQLite.
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 6b51db5f55..c076fb6fff 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -46,6 +46,7 @@ Optional dependencies:
libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled)
univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure)
libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0)
+ sqlite3 | SQLite DB | Wallet storage (only needed when wallet enabled)
For the versions used, see [dependencies.md](dependencies.md)
@@ -91,6 +92,10 @@ pass `--with-incompatible-bdb` to configure.
Otherwise, you can build from self-compiled `depends` (see above).
+SQLite is required for the wallet:
+
+ sudo apt install libsqlite3-dev
+
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
@@ -144,6 +149,10 @@ libqrencode (optional) can be installed with:
sudo dnf install qrencode-devel
+SQLite can be installed with:
+
+ sudo dnf install sqlite-devel
+
Notes
-----
The release is built with GCC and then "strip bitcoind" to strip the debug
@@ -238,7 +247,7 @@ disable-wallet mode with:
./configure --disable-wallet
-In this case there is no dependency on Berkeley DB 4.8.
+In this case there is no dependency on Berkeley DB 4.8 and SQLite.
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 92dea65309..ddd50ef296 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -21,6 +21,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| Python (tests) | | [3.5](https://www.python.org/downloads) | | | |
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
| Qt | [5.9.8](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | |
+| SQLite | [3.32.1](https://sqlite.org/download.html) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) | | | |
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | |
@@ -33,6 +34,7 @@ Some dependencies are not needed in all configurations. The following are some f
#### Options passed to `./configure`
* MiniUPnPc is not needed with `--with-miniupnpc=no`.
* Berkeley DB is not needed with `--disable-wallet`.
+* SQLite is not needed with `--disable-wallet`.
* Qt is not needed with `--without-gui`.
* If the qrencode dependency is absent, QR support won't be added. To force an error when that happens, pass `--with-qrencode`.
* ZeroMQ is needed only with the `--with-zmq` option.
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 6ae7e770e8..fa188dbcd6 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -746,6 +746,53 @@ the upper cycle, etc.
Threads and synchronization
----------------------------
+- Prefer `Mutex` type to `RecursiveMutex` one
+
+- Consistently use [Clang Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) annotations to
+ get compile-time warnings about potential race conditions in code. Combine annotations in function declarations with
+ run-time asserts in function definitions:
+
+```C++
+// txmempool.h
+class CTxMemPool
+{
+public:
+ ...
+ mutable RecursiveMutex cs;
+ ...
+ void UpdateTransactionsFromBlock(...) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs);
+ ...
+}
+
+// txmempool.cpp
+void CTxMemPool::UpdateTransactionsFromBlock(...)
+{
+ AssertLockHeld(::cs_main);
+ AssertLockHeld(cs);
+ ...
+}
+```
+
+```C++
+// validation.h
+class ChainstateManager
+{
+public:
+ ...
+ bool ProcessNewBlock(...) EXCLUSIVE_LOCKS_REQUIRED(!::cs_main);
+ ...
+}
+
+// validation.cpp
+bool ChainstateManager::ProcessNewBlock(...)
+{
+ AssertLockNotHeld(::cs_main);
+ ...
+ LOCK(::cs_main);
+ ...
+}
+```
+
- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
deadlocks are introduced. As of 0.12, this is defined by default when
configuring with `--enable-debug`.
diff --git a/doc/files.md b/doc/files.md
index 52e094a60b..545e8fc92c 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -8,6 +8,10 @@
- [Multi-wallet environment](#multi-wallet-environment)
+ - [Berkeley DB database based wallets](#berkeley-db-database-based-wallets)
+
+ - [SQLite database based wallets](#sqlite-database-based-wallets)
+
- [GUI settings](#gui-settings)
- [Legacy subdirectories and files](#legacy-subdirectories-and-files)
@@ -26,15 +30,16 @@ Linux | `$HOME/.bitcoin/`
macOS | `$HOME/Library/Application Support/Bitcoin/`
Windows | `%APPDATA%\Bitcoin\` <sup>[\[1\]](#note1)</sup>
-2. The non-default data directory path can be specified by `-datadir` option.
+2. A custom data directory path can be specified with the `-datadir` option.
3. All content of the data directory, except for `bitcoin.conf` file, is chain-specific. This means the actual data directory paths for non-mainnet cases differ:
-Chain option | Data directory path
---------------------|--------------------
-no option (mainnet) | *path_to_datadir*`/`
-`-testnet` | *path_to_datadir*`/testnet3/`
-`-regtest` | *path_to_datadir*`/regtest/`
+Chain option | Data directory path
+-------------------------------|------------------------------
+`-chain=main` (default) | *path_to_datadir*`/`
+`-chain=test` or `-testnet` | *path_to_datadir*`/testnet3/`
+`-chain=signet` or `-signet` | *path_to_datadir*`/signet/`
+`-chain=regtest` or `-regtest` | *path_to_datadir*`/regtest/`
## Data directory layout
@@ -44,19 +49,21 @@ Subdirectory | File(s) | Description
`blocks/index/` | LevelDB database | Block index; `-blocksdir` option does not affect this path
`blocks/` | `blkNNNNN.dat`<sup>[\[2\]](#note2)</sup> | Actual Bitcoin blocks (in network format, dumped in raw on disk, 128 MiB per file)
`blocks/` | `revNNNNN.dat`<sup>[\[2\]](#note2)</sup> | Block undo data (custom format)
-`chainstate/` | LevelDB database | Blockchain state (a compact representation of all currently unspent transaction outputs and some metadata about the transactions they are from)
+`chainstate/` | LevelDB database | Blockchain state (a compact representation of all currently unspent transaction outputs (UTXOs) and metadata about the transactions they are from)
`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`
-`wallets/` | | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, a wallet resides in the data directory
+`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
`./` | `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
-`./` | `onion_private_key` | Cached Tor onion service private key for `-listenonion` option
+`./` | `onion_v3_private_key` | Cached Tor onion service private key for `-listenonion` option
`./` | `peers.dat` | Peer IP address database (custom format)
`./` | `settings.json` | Read-write settings set through GUI or RPC interfaces, augmenting manual settings from [bitcoin.conf](bitcoin-conf.md). File is created automatically if read-write settings storage is not disabled with `-nosettings` option. Path can be specified with `-settings` option
`./` | `.cookie` | Session RPC authentication cookie; if used, created at start and deleted on shutdown; can be specified by `-rpccookiefile` option
@@ -64,25 +71,36 @@ Subdirectory | File(s) | Description
## Multi-wallet environment
-Wallets are Berkeley DB (BDB) databases:
-
-Subdirectory | File(s) | Description
--------------|-------------------|------------
-`database/` | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
-`./` | `db.log` | BDB error file
-`./` | `wallet.dat` | Personal wallet (BDB) with keys and transactions
-`./` | `.walletlock` | Wallet lock file
+Wallets are Berkeley DB (BDB) or SQLite databases.
-1. Each user-defined wallet named "wallet_name" resides in `wallets/wallet_name/` subdirectory.
+1. Each user-defined wallet named "wallet_name" resides in the `wallets/wallet_name/` subdirectory.
2. The default (unnamed) wallet resides in `wallets/` subdirectory; if the latter does not exist, the wallet resides in the data directory.
-3. A wallet database path can be specified by `-wallet` option.
+3. A wallet database path can be specified with the `-wallet` option.
4. `wallet.dat` files must not be shared across different node instances, as that can result in key-reuse and double-spends due the lack of synchronization between instances.
5. Any copy or backup of the wallet should be done through a `backupwallet` call in order to update and lock the wallet, preventing any file corruption caused by updates during the copy.
+
+### Berkeley DB database based wallets
+
+Subdirectory | File(s) | Description
+-------------|-------------------|-------------
+`database/` | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
+`./` | `db.log` | BDB error file
+`./` | `wallet.dat` | Personal wallet (a BDB database) with keys and transactions
+`./` | `.walletlock` | BDB wallet lock file
+
+### SQLite database based wallets
+
+Subdirectory | File | Description
+-------------|----------------------|-------------
+`./` | `wallet.dat` | Personal wallet (a SQLite database) with keys and transactions
+`./` | `wallet.dat-journal` | SQLite Rollback Journal file for `wallet.dat`. Usually created at start and deleted on shutdown. A user *must keep it as safe* as the `wallet.dat` file.
+
+
## GUI settings
`bitcoin-qt` uses [`QSettings`](https://doc.qt.io/qt-5/qsettings.html) class; this implies platform-specific [locations where application settings are stored](https://doc.qt.io/qt-5/qsettings.html#locations-where-application-settings-are-stored).
@@ -98,10 +116,10 @@ Path | Description | Repository notes
`blkindex.dat` | Blockchain index BDB database; replaced by {`chainstate/`, `blocks/index/`, `blocks/revNNNNN.dat`<sup>[\[2\]](#note2)</sup>} in 0.8.0 | [PR #1677](https://github.com/bitcoin/bitcoin/pull/1677)
`blk000?.dat` | Block data (custom format, 2 GiB per file); replaced by `blocks/blkNNNNN.dat`<sup>[\[2\]](#note2)</sup> in 0.8.0 | [PR #1677](https://github.com/bitcoin/bitcoin/pull/1677)
`addr.dat` | Peer IP address BDB database; replaced by `peers.dat` in [0.7.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.7.0.md) | [PR #1198](https://github.com/bitcoin/bitcoin/pull/1198), [`928d3a01`](https://github.com/bitcoin/bitcoin/commit/928d3a011cc66c7f907c4d053f674ea77dc611cc)
+`onion_private_key` | Cached Tor onion service private key for `-listenonion` option. Was used for Tor v2 services; replaced by `onion_v3_private_key` in [0.21.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md) | [PR #19954](https://github.com/bitcoin/bitcoin/pull/19954)
## Notes
-<a name="note1">1</a>. The `/` (slash, U+002F) is used as the platform-independent path component separator in this paper.
+<a name="note1">1</a>. The `/` (slash, U+002F) is used as the platform-independent path component separator in this document.
<a name="note2">2</a>. `NNNNN` matches `[0-9]{5}` regex.
-
diff --git a/doc/release-notes-16525.md b/doc/release-notes-16525.md
deleted file mode 100644
index 220cb78de4..0000000000
--- a/doc/release-notes-16525.md
+++ /dev/null
@@ -1,9 +0,0 @@
-RPC changes
------------
-
-Exposed transaction version numbers are now treated as unsigned 32-bit integers
-instead of signed 32-bit integers. This matches their treatment in consensus
-logic. Versions greater than 2 continue to be non-standard (matching previous
-behavior of smaller than 1 or greater than 2 being non-standard). Note that
-this includes the joinpsbt command, which combines partially-signed
-transactions by selecting the highest version number.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 23983dcd7b..4c69c61390 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -60,6 +60,14 @@ 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.
+The node's known peers are persisted to disk in a file called `peers.dat`. The
+format of this file has been changed in a backwards-incompatible way in order to
+accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
+the file is modified by 0.21.0 or newer then older versions will not be able to
+read it. Those old versions, in the event of a downgrade, will log an error
+message that deserialization has failed and will continue normal operation
+as if the file was missing, creating a new empty one. (#19954)
+
Notable changes
===============
@@ -74,9 +82,45 @@ P2P and network changes
node using P2P relay. This version reduces the initial broadcast guarantees
for wallet transactions submitted via P2P to a node running the wallet. (#18038)
+- The size of the set of transactions that peers have announced and we consider
+ for requests has been reduced from 100000 to 5000 (per peer), and further
+ announcements will be ignored when that limit is reached. If you need to dump
+ (very) large batches of transactions, exceptions can be made for trusted
+ peers using the "relay" network permission. For localhost for example it can
+ be enabled using the command line option `-whitelist=relay@127.0.0.1`.
+ (#19988)
+
+- The Tor onion service that is automatically created by setting the
+ `-listenonion` configuration parameter will now be created as a Tor v3 service
+ instead of Tor v2. The private key that was used for Tor v2 (if any) will be
+ left untouched in the `onion_private_key` file in the data directory (see
+ `-datadir`) and can be removed if not needed. Bitcoin Core will no longer
+ attempt to read it. The private key for the Tor v3 service will be saved in a
+ file named `onion_v3_private_key`. To use the deprecated Tor v2 service (not
+ recommended), then `onion_private_key` can be copied over
+ `onion_v3_private_key`, e.g.
+ `cp -f onion_private_key onion_v3_private_key`. (#19954)
+
Updated RPCs
------------
+- The `getpeerinfo` RPC now has additional `last_block` and `last_transaction`
+ fields that return the UNIX epoch time of the last block and the last valid
+ transaction received from each peer. (#19731)
+
+- `getnetworkinfo` now returns two new fields, `connections_in` and
+ `connections_out`, that provide the number of inbound and outbound peer
+ connections. These new fields are in addition to the existing `connections`
+ field, which returns the total number of peer connections. (#19405)
+
+- Exposed transaction version numbers are now treated as unsigned 32-bit
+ integers instead of signed 32-bit integers. This matches their treatment in
+ consensus logic. Versions greater than 2 continue to be non-standard
+ (matching previous behavior of smaller than 1 or greater than 2 being
+ non-standard). Note that this includes the joinpsbt command, which combines
+ partially-signed transactions by selecting the highest version number.
+ (#16525)
+
- `getmempoolinfo` now returns an additional `unbroadcastcount` field. The
mempool tracks locally submitted transactions until their initial broadcast
is acknowledged by a peer. This field returns the count of transactions
@@ -102,6 +146,20 @@ will trigger BIP 125 (replace-by-fee) opt-in. (#11413)
option `-deprecatedrpc=banscore` is used. The `banscore` field will be fully
removed in the next major release. (#19469)
+- The `testmempoolaccept` RPC returns `vsize` and a `fees` object with the `base` fee
+ if the transaction would pass validation. (#19940)
+
+- The `getpeerinfo` RPC now returns a `connection_type` field. This indicates
+ the type of connection established with the peer. It will return one of six
+ options. For more information, see the `getpeerinfo` help documentation.
+ (#19725)
+
+- The `getpeerinfo` RPC no longer returns the `addnode` field by default. This
+ field will be fully removed in the next major release. It can be accessed
+ with the configuration option `-deprecatedrpc=getpeerinfo_addnode`. However,
+ it is recommended to instead use the `connection_type` field (it will return
+ `manual` when addnode is true). (#19725)
+
- The `walletcreatefundedpsbt` RPC call will now fail with
`Insufficient funds` when inputs are manually selected but are not enough to cover
the outputs and fee. Additional inputs can automatically be added through the
@@ -115,12 +173,20 @@ Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section
New RPCs
--------
+- The `getindexinfo` RPC returns the actively running indices of the node,
+ including their current sync status and height. It also accepts an `index_name`
+ to specify returning only the status of that index. (#19550)
+
Build System
------------
Updated settings
----------------
+- The same ZeroMQ notification (e.g. `-zmqpubhashtx=address`) can now be
+ specified multiple times to publish the same notification to different ZeroMQ
+ sockets. (#18309)
+
- The `-banscore` configuration option, which modified the default threshold for
disconnecting and discouraging misbehaving peers, has been removed as part of
changes in 0.20.1 and in this release to the handling of misbehaving peers.
@@ -135,11 +201,20 @@ Updated settings
in future releases. Refer to the help of the affected settings `-whitebind`
and `-whitelist` for more details. (#19191)
+- Netmasks that contain 1-bits after 0-bits (the 1-bits are not contiguous on
+ the left side, e.g. 255.0.255.255) are no longer accepted. They are invalid
+ according to RFC 4632. Netmasks are used in the `-rpcallowip` and `-whitelist`
+ configuration options and in the `setban` RPC. (#19628)
+
Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below.
Tools and Utilities
-------------------
+- The `connections` field of `bitcoin-cli -getinfo` is expanded to return a JSON
+ object with `in`, `out` and `total` numbers of peer connections. It previously
+ returned a single integer value for the total number of peer connections. (#19405)
+
- A new `bitcoin-cli -generate` command, equivalent to RPC `generatenewaddress`
followed by `generatetoaddress`, can generate blocks for command line testing
purposes. This is a client-side version of the
@@ -152,6 +227,10 @@ Tools and Utilities
New settings
------------
+- The `startupnotify` option is used to specify a command to
+ execute when Bitcoin Core has finished with its startup
+ sequence. (#15367)
+
Wallet
------
@@ -168,6 +247,9 @@ Wallet
introduced unbroadcast set. See the "P2P and network changes" section for
more information on the unbroadcast set. (#18038)
+- The `sendtoaddress` and `sendmany` RPCs accept an optional `verbose=True`
+ argument to also return the fee reason about the sent tx. (#19501)
+
- The wallet can create a transaction without change even when the keypool is
empty. Previously it failed. (#17219)
@@ -175,6 +257,51 @@ Wallet
has been added to the `bitcoin-wallet` tool which performs the salvage
operations that `-salvagewallet` did. (#18918)
+- A new configuration flag `-maxapsfee` has been added, which sets the max
+ allowed avoid partial spends (APS) fee. It defaults to 0 (i.e. fee is the
+ same with and without APS). Setting it to -1 will disable APS, unless
+ `-avoidpartialspends` is set. (#14582)
+
+- The wallet will now avoid partial spends (APS) by default, if this does not
+ result in a difference in fees compared to the non-APS variant. The allowed
+ fee threshold can be adjusted using the new `-maxapsfee` configuration
+ option. (#14582)
+
+- The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept
+ `load_on_startup` options to modify the settings list. Unless these options
+ are explicitly set to true or false, the list is not modified, so the RPC
+ methods remain backwards compatible. (#15937)
+
+- A new `send` RPC with similar syntax to `walletcreatefundedpsbt`, including
+ support for coin selection and a custom fee rate. The `send` RPC is
+ experimental and may change in subsequent releases. Using it is encouraged
+ once it's no longer experimental: `sendmany` and `sendtoaddress` may be
+ deprecated in a future release. (#16378)
+
+- `fundrawtransaction` and `walletcreatefundedpsbt` when used with the
+ `lockUnspents` argument now lock manually selected coins, in addition to
+ automatically selected coins. Note that locked coins are never used in
+ automatic coin selection, but can still be manually selected. (#18244)
+
+- The `-zapwallettxes` startup option has been removed and its functionality
+ removed from the wallet. This option was originally intended to allow for
+ the fee bumping of transactions that did not signal RBF. This functionality
+ has been superseded with the abandon transaction feature. (#19671)
+
+- The error code when no wallet is loaded, but a wallet RPC is called, has been
+ changed from `-32601` (method not found) to `-18` (wallet not found).
+ (#20101)
+
+### Default Wallet
+
+Bitcoin Core will no longer create an unnamed `""` wallet by default when no
+wallet is specified on the command line or in the configuration files. For
+backwards compatibility, if an unnamed `""` wallet already exists and would
+have been loaded previously, then it will still be loaded. Users without an
+unnamed `""` wallet and without any other wallets to be loaded on startup will
+be prompted to either choose a wallet to load, or to create a new wallet.
+(#15454)
+
### Experimental Descriptor Wallets
Please note that Descriptor Wallets are still experimental and not all expected functionality
@@ -304,6 +431,14 @@ issue.
GUI changes
-----------
+- Wallets created or loaded in the GUI will now be automatically loaded on
+ startup, so they don't need to be manually reloaded next time Bitcoin Core is
+ started. The list of wallets to load on startup is stored in
+ `\<datadir\>/settings.json` and augments any command line or `bitcoin.conf`
+ `-wallet=` settings that specify more wallets to load. Wallets that are
+ unloaded in the GUI get removed from the settings list so they won't load
+ again automatically next startup. (#19754)
+
- The GUI Peers window no longer displays a "Ban Score" field. This is part of
changes in 0.20.1 and in this release to the handling of misbehaving
peers. Refer to "Changes regarding misbehaving peers" in the 0.20.1 release
@@ -321,9 +456,23 @@ RPC
- Fee estimation failed
- Transaction has too long of a mempool chain
+- The `sendrawtransaction` error code for exceeding `maxfeerate` has been changed from
+ `-26` to `-25`. The error string has been changed from "absurdly-high-fee" to
+ "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)." The
+ `testmempoolaccept` RPC returns `max-fee-exceeded` rather than `absurdly-high-fee`
+ as the `reject-reason`. (#19339)
+
+- To make wallet and rawtransaction RPCs more consistent, the error message for
+ exceeding maximum feerate has been changed to "Fee exceeds maximum configured by user
+ (e.g. -maxtxfee, maxfeerate)." (#19339)
+
Tests
-----
+- The BIP 325 default signet can be enabled by the `-chain=signet` or `-signet`
+ setting. The settings `-signetchallenge` and `-signetseednode` allow
+ enabling a custom signet.
+
Credits
=======
diff --git a/doc/tor.md b/doc/tor.md
index 17807856e5..12b5f70245 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -45,11 +45,12 @@ config file): *Needed for Tor version 0.2.7.0 and older versions of Tor only. Fo
versions of Tor see [Section 3](#3-automatically-listen-on-tor).*
HiddenServiceDir /var/lib/tor/bitcoin-service/
- HiddenServicePort 8333 127.0.0.1:8333
- HiddenServicePort 18333 127.0.0.1:18333
+ HiddenServicePort 8333 127.0.0.1:8334
+ HiddenServicePort 18333 127.0.0.1:18334
-The directory can be different of course, but (both) port numbers should be equal to
-your bitcoind's P2P listen port (8333 by default).
+The directory can be different of course, but virtual port numbers should be equal to
+your bitcoind's P2P listen port (8333 by default), and target addresses and ports
+should be equal to binding address and port for inbound Tor connections (127.0.0.1:8334 by default).
-externalip=X You can tell bitcoin about its publicly reachable address using
this option, and this can be a .onion address. Given the above
diff --git a/doc/zmq.md b/doc/zmq.md
index 3a1194de1c..85f3370130 100644
--- a/doc/zmq.md
+++ b/doc/zmq.md
@@ -1,6 +1,6 @@
# Block and Transaction Broadcasting with ZeroMQ
-[ZeroMQ](http://zeromq.org/) is a lightweight wrapper around TCP
+[ZeroMQ](https://zeromq.org/) is a lightweight wrapper around TCP
connections, inter-process communication, and shared-memory,
providing various message-oriented semantics such as publish/subscribe,
request/reply, and push/pull.
@@ -39,8 +39,9 @@ For version information, see [dependencies.md](dependencies.md).
Typically, it is packaged by distributions as something like
*libzmq3-dev*. The C++ wrapper for ZeroMQ is *not* needed.
-In order to run the example Python client scripts in contrib/ one must
-also install *python3-zmq*, though this is not necessary for daemon
+In order to run the example Python client scripts in the `contrib/zmq/`
+directory, one must also install [PyZMQ](https://github.com/zeromq/pyzmq)
+(generally with `pip install pyzmq`), though this is not necessary for daemon
operation.
## Enabling
@@ -62,9 +63,11 @@ Currently, the following notifications are supported:
-zmqpubhashblock=address
-zmqpubrawblock=address
-zmqpubrawtx=address
+ -zmqpubsequence=address
The socket type is PUB and the address must be a valid ZeroMQ socket
address. The same address can be used in more than one notification.
+The same notification can be specified more than once.
The option to set the PUB socket's outbound message high water mark
(SNDHWM) may be set individually for each notification:
@@ -73,12 +76,14 @@ The option to set the PUB socket's outbound message high water mark
-zmqpubhashblockhwm=n
-zmqpubrawblockhwm=n
-zmqpubrawtxhwm=n
+ -zmqpubsequencehwm=address
The high water mark value must be an integer greater than or equal to 0.
For instance:
$ bitcoind -zmqpubhashtx=tcp://127.0.0.1:28332 \
+ -zmqpubhashtx=tcp://192.168.1.2:28332 \
-zmqpubrawtx=ipc:///tmp/bitcoind.tx.raw \
-zmqpubhashtxhwm=10000
@@ -86,7 +91,15 @@ Each PUB notification has a topic and body, where the header
corresponds to the notification type. For instance, for the
notification `-zmqpubhashtx` the topic is `hashtx` (no null
terminator) and the body is the transaction hash (32
-bytes).
+bytes) for all but `sequence` topic. For `sequence`, the body
+is structured as the following based on the type of message:
+
+ <32-byte hash>C : Blockhash connected
+ <32-byte hash>D : Blockhash disconnected
+ <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason
+ <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
+
+Where the 8-byte uints correspond to the mempool sequence number.
These options can also be provided in bitcoin.conf.
@@ -98,6 +111,20 @@ ZMQ_SUBSCRIBE option set to one or either of these prefixes (for
instance, just `hash`); without doing so will result in no messages
arriving. Please see [`contrib/zmq/zmq_sub.py`](/contrib/zmq/zmq_sub.py) for a working example.
+The ZMQ_PUB socket's ZMQ_TCP_KEEPALIVE option is enabled. This means that
+the underlying SO_KEEPALIVE option is enabled when using a TCP transport.
+The effective TCP keepalive values are managed through the underlying
+operating system configuration and must be configured prior to connection establishment.
+
+For example, when running on GNU/Linux, one might use the following
+to lower the keepalive setting to 10 minutes:
+
+sudo sysctl -w net.ipv4.tcp_keepalive_time=600
+
+Setting the keepalive values appropriately for your operating environment may
+improve connectivity in situations where long-lived connections are silently
+dropped by network middle boxes.
+
## Remarks
From the perspective of bitcoind, the ZeroMQ socket is write-only; PUB
@@ -109,13 +136,20 @@ No authentication or authorization is done on connecting clients; it
is assumed that the ZeroMQ port is exposed only to trusted entities,
using other means such as firewalling.
-Note that when the block chain tip changes, a reorganisation may occur
-and just the tip will be notified. It is up to the subscriber to
-retrieve the chain from the last known block to the new tip. Also note
-that no notification occurs if the tip was in the active chain - this
-is the case after calling invalidateblock RPC.
+Note that for `*block` topics, when the block chain tip changes,
+a reorganisation may occur and just the tip will be notified.
+It is up to the subscriber to retrieve the chain from the last known
+block to the new tip. Also note that no notification will occur if the tip
+was in the active chain--as would be the case after calling invalidateblock RPC.
+In contrast, the `sequence` topic publishes all block connections and
+disconnections.
There are several possibilities that ZMQ notification can get lost
during transmission depending on the communication type you are
using. Bitcoind appends an up-counting sequence number to each
notification which allows listeners to detect lost notifications.
+
+The `sequence` topic refers specifically to the mempool sequence
+number, which is also published along with all mempool events. This
+is a different sequence value than in ZMQ itself in order to allow a total
+ordering of mempool events to be constructed.
diff --git a/src/Makefile.am b/src/Makefile.am
index cd3cc95707..b0d36717ce 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,10 +4,11 @@
DIST_SUBDIRS = secp256k1 univalue
-AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
+AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS)
AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS)
AM_LIBTOOLFLAGS = --preserve-dup-deps
+PTHREAD_FLAGS = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
EXTRA_LIBRARIES =
if EMBEDDED_UNIVALUE
@@ -140,6 +141,7 @@ BITCOIN_CORE_H = \
httpserver.h \
index/base.h \
index/blockfilterindex.h \
+ index/disktxpos.h \
index/txindex.h \
indirectmap.h \
init.h \
@@ -149,7 +151,6 @@ BITCOIN_CORE_H = \
interfaces/wallet.h \
key.h \
key_io.h \
- limitedmap.h \
logging.h \
logging/timer.h \
memusage.h \
@@ -200,6 +201,7 @@ BITCOIN_CORE_H = \
script/signingprovider.h \
script/standard.h \
shutdown.h \
+ signet.h \
streams.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
@@ -212,6 +214,7 @@ BITCOIN_CORE_H = \
timedata.h \
torcontrol.h \
txdb.h \
+ txrequest.h \
txmempool.h \
undo.h \
util/asmap.h \
@@ -254,6 +257,7 @@ BITCOIN_CORE_H = \
wallet/rpcwallet.h \
wallet/salvage.h \
wallet/scriptpubkeyman.h \
+ wallet/sqlite.h \
wallet/wallet.h \
wallet/walletdb.h \
wallet/wallettool.h \
@@ -261,10 +265,10 @@ BITCOIN_CORE_H = \
walletinitinterface.h \
warnings.h \
zmq/zmqabstractnotifier.h \
- zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
zmq/zmqpublishnotifier.h \
- zmq/zmqrpc.h
+ zmq/zmqrpc.h \
+ zmq/zmqutil.h
obj/build.h: FORCE
@@ -320,9 +324,11 @@ libbitcoin_server_a_SOURCES = \
rpc/server.cpp \
script/sigcache.cpp \
shutdown.cpp \
+ signet.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
+ txrequest.cpp \
txmempool.cpp \
validation.cpp \
validationinterface.cpp \
@@ -343,7 +349,8 @@ libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
zmq/zmqnotificationinterface.cpp \
zmq/zmqpublishnotifier.cpp \
- zmq/zmqrpc.cpp
+ zmq/zmqrpc.cpp \
+ zmq/zmqutil.cpp
endif
@@ -365,6 +372,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/rpcwallet.cpp \
wallet/salvage.cpp \
wallet/scriptpubkeyman.cpp \
+ wallet/sqlite.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
wallet/walletutil.cpp \
@@ -402,6 +410,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/sha1.h \
crypto/sha256.cpp \
crypto/sha256.h \
+ crypto/sha3.cpp \
+ crypto/sha3.h \
crypto/sha512.cpp \
crypto/sha512.h \
crypto/siphash.cpp \
@@ -563,7 +573,7 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
bitcoin_daemon_sources = bitcoind.cpp
bitcoin_bin_cppflags = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_bin_cxxflags = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_bin_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_bin_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_daemon_sources += bitcoind-res.rc
@@ -582,7 +592,7 @@ bitcoin_bin_ldadd = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
bitcoind_SOURCES = $(bitcoin_daemon_sources)
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
@@ -600,7 +610,7 @@ bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
bitcoin_cli_SOURCES = bitcoin-cli.cpp
bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
@@ -619,7 +629,7 @@ bitcoin_cli_LDADD += $(BOOST_LIBS) $(EVENT_LIBS)
bitcoin_tx_SOURCES = bitcoin-tx.cpp
bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_tx_SOURCES += bitcoin-tx-res.rc
@@ -676,12 +686,18 @@ CLEANFILES = $(EXTRA_LIBRARIES)
CLEANFILES += *.gcda *.gcno
CLEANFILES += compat/*.gcda compat/*.gcno
CLEANFILES += consensus/*.gcda consensus/*.gcno
+CLEANFILES += crc32c/src/*.gcda crc32c/src/*.gcno
CLEANFILES += crypto/*.gcda crypto/*.gcno
+CLEANFILES += index/*.gcda index/*.gcno
+CLEANFILES += interfaces/*.gcda interfaces/*.gcno
+CLEANFILES += node/*.gcda node/*.gcno
CLEANFILES += policy/*.gcda policy/*.gcno
CLEANFILES += primitives/*.gcda primitives/*.gcno
+CLEANFILES += rpc/*.gcda rpc/*.gcno
CLEANFILES += script/*.gcda script/*.gcno
CLEANFILES += support/*.gcda support/*.gcno
CLEANFILES += univalue/*.gcda univalue/*.gcno
+CLEANFILES += util/*.gcda util/*.gcno
CLEANFILES += wallet/*.gcda wallet/*.gcno
CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno
CLEANFILES += zmq/*.gcda zmq/*.gcno
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index c224ca7bf6..beb3f8dfd2 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -74,8 +74,8 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
-bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS)
+bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 848053e841..f46310a603 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -321,8 +321,8 @@ bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
- $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
+bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
@@ -379,11 +379,11 @@ 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) $(MOC_DEFS) $< | \
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
moc_%.cpp: %.h
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES) $(MOC_DEFS) $< | \
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
%.qm: %.ts
diff --git a/src/Makefile.qt_locale.include b/src/Makefile.qt_locale.include
index 3ac21b1326..17ba0780e6 100644
--- a/src/Makefile.qt_locale.include
+++ b/src/Makefile.qt_locale.include
@@ -27,6 +27,7 @@ QT_TS = \
qt/locale/bitcoin_fi.ts \
qt/locale/bitcoin_fil.ts \
qt/locale/bitcoin_fr.ts \
+ qt/locale/bitcoin_gl_ES.ts \
qt/locale/bitcoin_he.ts \
qt/locale/bitcoin_hi.ts \
qt/locale/bitcoin_hr.ts \
@@ -71,7 +72,6 @@ QT_TS = \
qt/locale/bitcoin_ta.ts \
qt/locale/bitcoin_te.ts \
qt/locale/bitcoin_th.ts \
- qt/locale/bitcoin_tr.ts \
qt/locale/bitcoin_uk.ts \
qt/locale/bitcoin_ur.ts \
qt/locale/bitcoin_uz@Cyrl.ts \
@@ -82,4 +82,5 @@ QT_TS = \
qt/locale/bitcoin_zh.ts \
qt/locale/bitcoin_zh_CN.ts \
qt/locale/bitcoin_zh_HK.ts \
- qt/locale/bitcoin_zh_TW.ts
+ qt/locale/bitcoin_zh_TW.ts \
+ qt/locale/bitcoin_zu.ts
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8c47fabad9..c05dd38737 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -56,8 +56,8 @@ endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
- $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
+qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
CLEAN_BITCOIN_QT_TEST = $(TEST_QT_MOC_CPP) qt/test/*.gcda qt/test/*.gcno
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index c3e46c0def..7fac78f973 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -70,6 +70,7 @@ FUZZ_TARGETS = \
test/fuzz/message \
test/fuzz/messageheader_deserialize \
test/fuzz/multiplication_overflow \
+ test/fuzz/net \
test/fuzz/net_permissions \
test/fuzz/netaddr_deserialize \
test/fuzz/netaddress \
@@ -128,12 +129,16 @@ FUZZ_TARGETS = \
test/fuzz/script_deserialize \
test/fuzz/script_flags \
test/fuzz/script_interpreter \
+ test/fuzz/script_assets_test_minimizer \
test/fuzz/script_ops \
test/fuzz/script_sigcache \
test/fuzz/script_sign \
test/fuzz/scriptnum_ops \
+ test/fuzz/secp256k1_ec_seckey_import_export_der \
+ test/fuzz/secp256k1_ecdsa_signature_parse_der_lax \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
+ test/fuzz/signet \
test/fuzz/snapshotmetadata_deserialize \
test/fuzz/span \
test/fuzz/spanparsing \
@@ -147,6 +152,7 @@ FUZZ_TARGETS = \
test/fuzz/tx_in_deserialize \
test/fuzz/tx_out \
test/fuzz/txoutcompressor_deserialize \
+ test/fuzz/txrequest \
test/fuzz/txundo_deserialize \
test/fuzz/uint160_deserialize \
test/fuzz/uint256_deserialize
@@ -231,7 +237,6 @@ BITCOIN_TESTS =\
test/interfaces_tests.cpp \
test/key_io_tests.cpp \
test/key_tests.cpp \
- test/limitedmap_tests.cpp \
test/logging_tests.cpp \
test/dbwrapper_tests.cpp \
test/validation_tests.cpp \
@@ -271,6 +276,7 @@ BITCOIN_TESTS =\
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
test/txindex_tests.cpp \
+ test/txrequest_tests.cpp \
test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
test/uint256_tests.cpp \
@@ -311,8 +317,8 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS)
-test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS)
+test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -320,886 +326,924 @@ endif
if ENABLE_FUZZ
+FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
+
test_fuzz_addition_overflow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_addition_overflow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addition_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addition_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addition_overflow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addition_overflow_SOURCES = test/fuzz/addition_overflow.cpp
test_fuzz_addr_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDR_INFO_DESERIALIZE=1
test_fuzz_addr_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addr_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addr_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addr_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_addrdb_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_addrdb_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addrdb_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addrdb_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addrdb_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addrdb_SOURCES = test/fuzz/addrdb.cpp
test_fuzz_address_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_DESERIALIZE=1
test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_address_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_address_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_addrman_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRMAN_DESERIALIZE=1
test_fuzz_addrman_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addrman_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addrman_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addrman_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addrman_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_asmap_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_asmap_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_asmap_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_asmap_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_asmap_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_asmap_SOURCES = test/fuzz/asmap.cpp
test_fuzz_asmap_direct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_asmap_direct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_asmap_direct_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_asmap_direct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_asmap_direct_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_asmap_direct_SOURCES = test/fuzz/asmap_direct.cpp
test_fuzz_autofile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_autofile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_autofile_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_autofile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_autofile_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_autofile_SOURCES = test/fuzz/autofile.cpp
test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1
test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_banentry_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_banentry_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_banman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_banman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banman_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_banman_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_banman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_banman_SOURCES = test/fuzz/banman.cpp
test_fuzz_base_encode_decode_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_base_encode_decode_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_base_encode_decode_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_base_encode_decode_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_base_encode_decode_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_base_encode_decode_SOURCES = test/fuzz/base_encode_decode.cpp
test_fuzz_bech32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_bech32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bech32_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bech32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bech32_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bech32_SOURCES = test/fuzz/bech32.cpp
test_fuzz_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_SOURCES = test/fuzz/block.cpp
test_fuzz_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_DESERIALIZE=1
test_fuzz_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_file_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILE_INFO_DESERIALIZE=1
test_fuzz_block_file_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_file_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_file_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_file_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_file_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_filter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILTER_DESERIALIZE=1
test_fuzz_block_filter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_filter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_filter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_filter_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_filter_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_header_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_block_header_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_header_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_header_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_header_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_header_SOURCES = test/fuzz/block_header.cpp
test_fuzz_block_header_and_short_txids_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE=1
test_fuzz_block_header_and_short_txids_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_header_and_short_txids_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_header_and_short_txids_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blockfilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_blockfilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockfilter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockfilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockfilter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockfilter_SOURCES = test/fuzz/blockfilter.cpp
test_fuzz_blockheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKHEADER_DESERIALIZE=1
test_fuzz_blockheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockheader_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockheader_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blocklocator_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKLOCATOR_DESERIALIZE=1
test_fuzz_blocklocator_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocklocator_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocklocator_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocklocator_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocklocator_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blockmerkleroot_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKMERKLEROOT=1
test_fuzz_blockmerkleroot_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockmerkleroot_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockmerkleroot_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockmerkleroot_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockmerkleroot_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blocktransactions_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONS_DESERIALIZE=1
test_fuzz_blocktransactions_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactions_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactions_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocktransactions_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocktransactions_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blocktransactionsrequest_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONSREQUEST_DESERIALIZE=1
test_fuzz_blocktransactionsrequest_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactionsrequest_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocktransactionsrequest_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blockundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKUNDO_DESERIALIZE=1
test_fuzz_blockundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockundo_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockundo_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bloom_filter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bloom_filter_SOURCES = test/fuzz/bloom_filter.cpp
test_fuzz_bloomfilter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOOMFILTER_DESERIALIZE=1
test_fuzz_bloomfilter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bloomfilter_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bloomfilter_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_buffered_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_buffered_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_buffered_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_buffered_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_buffered_file_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_buffered_file_SOURCES = test/fuzz/buffered_file.cpp
test_fuzz_chain_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_chain_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_chain_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_chain_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_chain_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_chain_SOURCES = test/fuzz/chain.cpp
test_fuzz_checkqueue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_checkqueue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_checkqueue_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_checkqueue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_checkqueue_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_checkqueue_SOURCES = test/fuzz/checkqueue.cpp
test_fuzz_coins_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DCOINS_DESERIALIZE=1
test_fuzz_coins_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_coins_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_coins_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_coins_view_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_coins_view_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_coins_view_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
test_fuzz_crypto_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_SOURCES = test/fuzz/crypto.cpp
test_fuzz_crypto_aes256_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_aes256_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_aes256_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_aes256_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_aes256_SOURCES = test/fuzz/crypto_aes256.cpp
test_fuzz_crypto_aes256cbc_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_aes256cbc_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_aes256cbc_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_aes256cbc_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256cbc_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_aes256cbc_SOURCES = test/fuzz/crypto_aes256cbc.cpp
test_fuzz_crypto_chacha20_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_chacha20_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_chacha20_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_chacha20_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_chacha20_SOURCES = test/fuzz/crypto_chacha20.cpp
test_fuzz_crypto_chacha20_poly1305_aead_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_chacha20_poly1305_aead_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_chacha20_poly1305_aead_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_chacha20_poly1305_aead_SOURCES = test/fuzz/crypto_chacha20_poly1305_aead.cpp
test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_common_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
test_fuzz_crypto_hkdf_hmac_sha256_l32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_hkdf_hmac_sha256_l32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_hkdf_hmac_sha256_l32_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_hkdf_hmac_sha256_l32_SOURCES = test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
test_fuzz_crypto_poly1305_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_poly1305_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_poly1305_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_poly1305_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_poly1305_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_poly1305_SOURCES = test/fuzz/crypto_poly1305.cpp
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_cuckoocache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_cuckoocache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_cuckoocache_SOURCES = test/fuzz/cuckoocache.cpp
test_fuzz_decode_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_decode_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_decode_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_decode_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_decode_tx_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_decode_tx_SOURCES = test/fuzz/decode_tx.cpp
test_fuzz_descriptor_parse_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_descriptor_parse_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_descriptor_parse_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_descriptor_parse_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_descriptor_parse_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_descriptor_parse_SOURCES = test/fuzz/descriptor_parse.cpp
test_fuzz_diskblockindex_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DDISKBLOCKINDEX_DESERIALIZE=1
test_fuzz_diskblockindex_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_diskblockindex_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_diskblockindex_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_diskblockindex_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_diskblockindex_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_eval_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_eval_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_eval_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_eval_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_eval_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_eval_script_SOURCES = test/fuzz/eval_script.cpp
test_fuzz_fee_rate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fee_rate_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fee_rate_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fee_rate_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fee_rate_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fee_rate_SOURCES = test/fuzz/fee_rate.cpp
test_fuzz_fee_rate_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFEE_RATE_DESERIALIZE=1
test_fuzz_fee_rate_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fee_rate_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fee_rate_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fee_rate_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fee_rate_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_fees_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fees_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fees_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fees_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fees_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fees_SOURCES = test/fuzz/fees.cpp
test_fuzz_flat_file_pos_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFLAT_FILE_POS_DESERIALIZE=1
test_fuzz_flat_file_pos_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_flat_file_pos_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_flatfile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_flatfile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_flatfile_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_flatfile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_flatfile_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_flatfile_SOURCES = test/fuzz/flatfile.cpp
test_fuzz_float_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_float_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_float_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_float_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_float_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_float_SOURCES = test/fuzz/float.cpp
test_fuzz_golomb_rice_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_golomb_rice_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_golomb_rice_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_golomb_rice_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_golomb_rice_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_golomb_rice_SOURCES = test/fuzz/golomb_rice.cpp
test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_hex_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_hex_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_hex_SOURCES = test/fuzz/hex.cpp
test_fuzz_http_request_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_http_request_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_http_request_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_http_request_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_http_request_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_http_request_SOURCES = test/fuzz/http_request.cpp
test_fuzz_integer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_integer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_integer_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_integer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_integer_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_integer_SOURCES = test/fuzz/integer.cpp
test_fuzz_inv_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DINV_DESERIALIZE=1
test_fuzz_inv_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_inv_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_inv_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_SOURCES = test/fuzz/key.cpp
test_fuzz_key_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_key_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_io_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_io_SOURCES = test/fuzz/key_io.cpp
test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1
test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_origin_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_origin_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_kitchen_sink_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_kitchen_sink_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_kitchen_sink_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_kitchen_sink_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_kitchen_sink_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_kitchen_sink_SOURCES = test/fuzz/kitchen_sink.cpp
test_fuzz_load_external_block_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_load_external_block_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_load_external_block_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_load_external_block_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_load_external_block_file_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_load_external_block_file_SOURCES = test/fuzz/load_external_block_file.cpp
test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_locale_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_locale_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_locale_SOURCES = test/fuzz/locale.cpp
test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1
test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_merkle_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_merkle_block_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_merkle_block_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_merkleblock_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_merkleblock_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_merkleblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_merkleblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_merkleblock_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_merkleblock_SOURCES = test/fuzz/merkleblock.cpp
test_fuzz_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_message_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_message_SOURCES = test/fuzz/message.cpp
test_fuzz_messageheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGEHEADER_DESERIALIZE=1
test_fuzz_messageheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_messageheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_messageheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_messageheader_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_messageheader_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_multiplication_overflow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_multiplication_overflow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_multiplication_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_multiplication_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_multiplication_overflow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_multiplication_overflow_SOURCES = test/fuzz/multiplication_overflow.cpp
+test_fuzz_net_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_net_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_net_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_net_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_net_SOURCES = test/fuzz/net.cpp
+
test_fuzz_net_permissions_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_net_permissions_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_net_permissions_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_net_permissions_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_net_permissions_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_net_permissions_SOURCES = test/fuzz/net_permissions.cpp
test_fuzz_netaddr_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DNETADDR_DESERIALIZE=1
test_fuzz_netaddr_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_netaddr_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_netaddr_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_netaddr_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_netaddr_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_netaddress_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_netaddress_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_netaddress_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_netaddress_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_netaddress_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_netaddress_SOURCES = test/fuzz/netaddress.cpp
test_fuzz_out_point_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DOUT_POINT_DESERIALIZE=1
test_fuzz_out_point_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_out_point_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_out_point_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_out_point_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_out_point_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_p2p_transport_deserializer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_p2p_transport_deserializer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_p2p_transport_deserializer_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_p2p_transport_deserializer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_p2p_transport_deserializer_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_p2p_transport_deserializer_SOURCES = test/fuzz/p2p_transport_deserializer.cpp
test_fuzz_parse_hd_keypath_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_hd_keypath_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_hd_keypath_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_hd_keypath_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_hd_keypath_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_hd_keypath_SOURCES = test/fuzz/parse_hd_keypath.cpp
test_fuzz_parse_iso8601_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_iso8601_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_iso8601_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_iso8601_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_iso8601_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_iso8601_SOURCES = test/fuzz/parse_iso8601.cpp
test_fuzz_parse_numbers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_numbers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_numbers_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_numbers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_numbers_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_numbers_SOURCES = test/fuzz/parse_numbers.cpp
test_fuzz_parse_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_script_SOURCES = test/fuzz/parse_script.cpp
test_fuzz_parse_univalue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_univalue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_univalue_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_univalue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_univalue_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_univalue_SOURCES = test/fuzz/parse_univalue.cpp
test_fuzz_prevector_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_prevector_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_prevector_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_prevector_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_prevector_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_prevector_SOURCES = test/fuzz/prevector.cpp
test_fuzz_partial_merkle_tree_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIAL_MERKLE_TREE_DESERIALIZE=1
test_fuzz_partial_merkle_tree_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_partial_merkle_tree_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_partial_merkle_tree_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_partially_signed_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIALLY_SIGNED_TRANSACTION_DESERIALIZE=1
test_fuzz_partially_signed_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_partially_signed_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_partially_signed_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_policy_estimator_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_policy_estimator_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_policy_estimator_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_policy_estimator_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_policy_estimator_SOURCES = test/fuzz/policy_estimator.cpp
test_fuzz_policy_estimator_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_policy_estimator_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_policy_estimator_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_policy_estimator_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_io_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_policy_estimator_io_SOURCES = test/fuzz/policy_estimator_io.cpp
test_fuzz_pow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_pow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_pow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_pow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_pow_SOURCES = test/fuzz/pow.cpp
test_fuzz_prefilled_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPREFILLED_TRANSACTION_DESERIALIZE=1
test_fuzz_prefilled_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_prefilled_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_primitives_transaction_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_primitives_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_primitives_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_primitives_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_primitives_transaction_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_primitives_transaction_SOURCES = test/fuzz/primitives_transaction.cpp
test_fuzz_process_messages_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_process_messages_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_messages_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_messages_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_messages_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_messages_SOURCES = test/fuzz/process_messages.cpp
test_fuzz_process_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_process_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_addr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=addr
test_fuzz_process_message_addr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_addr_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_addr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_addr_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_addr_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=block
test_fuzz_process_message_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_block_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_block_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_block_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_blocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=blocktxn
test_fuzz_process_message_blocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_blocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_blocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_blocktxn_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_blocktxn_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_cmpctblock_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=cmpctblock
test_fuzz_process_message_cmpctblock_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_cmpctblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_cmpctblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_cmpctblock_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_cmpctblock_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_feefilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=feefilter
test_fuzz_process_message_feefilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_feefilter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_feefilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_feefilter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_feefilter_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filteradd_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filteradd
test_fuzz_process_message_filteradd_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filteradd_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filteradd_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filteradd_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filteradd_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filterclear_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterclear
test_fuzz_process_message_filterclear_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filterclear_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filterclear_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterclear_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filterclear_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filterload_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterload
test_fuzz_process_message_filterload_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filterload_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filterload_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterload_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filterload_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getaddr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getaddr
test_fuzz_process_message_getaddr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getaddr_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getaddr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getaddr_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getaddr_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getblocks_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocks
test_fuzz_process_message_getblocks_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getblocks_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getblocks_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocks_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getblocks_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getblocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocktxn
test_fuzz_process_message_getblocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getblocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getblocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocktxn_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getblocktxn_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getdata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getdata
test_fuzz_process_message_getdata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getdata_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getdata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getdata_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getdata_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getheaders
test_fuzz_process_message_getheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getheaders_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getheaders_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_headers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=headers
test_fuzz_process_message_headers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_headers_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_headers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_headers_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_headers_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_inv_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=inv
test_fuzz_process_message_inv_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_inv_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_inv_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_inv_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_inv_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_mempool_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=mempool
test_fuzz_process_message_mempool_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_mempool_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_mempool_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_mempool_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_mempool_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_notfound_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=notfound
test_fuzz_process_message_notfound_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_notfound_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_notfound_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_notfound_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_notfound_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_ping_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=ping
test_fuzz_process_message_ping_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_ping_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_ping_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_ping_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_ping_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_pong_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=pong
test_fuzz_process_message_pong_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_pong_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_pong_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_pong_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_pong_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_sendcmpct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendcmpct
test_fuzz_process_message_sendcmpct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_sendcmpct_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_sendcmpct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendcmpct_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_sendcmpct_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_sendheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendheaders
test_fuzz_process_message_sendheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_sendheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_sendheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendheaders_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_sendheaders_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=tx
test_fuzz_process_message_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_tx_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_tx_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_verack_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=verack
test_fuzz_process_message_verack_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_verack_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_verack_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_verack_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_verack_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_version_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=version
test_fuzz_process_message_version_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_version_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_version_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_version_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_version_SOURCES = test/fuzz/process_message.cpp
test_fuzz_protocol_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_protocol_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_protocol_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_protocol_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_protocol_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_protocol_SOURCES = test/fuzz/protocol.cpp
test_fuzz_psbt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_psbt_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_SOURCES = test/fuzz/psbt.cpp
test_fuzz_psbt_input_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_INPUT_DESERIALIZE=1
test_fuzz_psbt_input_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_input_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_input_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_input_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_input_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_psbt_output_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_OUTPUT_DESERIALIZE=1
test_fuzz_psbt_output_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_output_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_output_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_output_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_output_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_pub_key_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPUB_KEY_DESERIALIZE=1
test_fuzz_pub_key_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pub_key_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_pub_key_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_pub_key_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_pub_key_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_random_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_random_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_random_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_random_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_random_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_random_SOURCES = test/fuzz/random.cpp
test_fuzz_rbf_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_rbf_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_rbf_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_rbf_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rbf_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_rbf_SOURCES = test/fuzz/rbf.cpp
test_fuzz_rolling_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_rolling_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_rolling_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_rolling_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rolling_bloom_filter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_rolling_bloom_filter_SOURCES = test/fuzz/rolling_bloom_filter.cpp
test_fuzz_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_SOURCES = test/fuzz/script.cpp
test_fuzz_script_bitcoin_consensus_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_bitcoin_consensus_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_bitcoin_consensus_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_bitcoin_consensus_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_bitcoin_consensus_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_bitcoin_consensus_SOURCES = test/fuzz/script_bitcoin_consensus.cpp
test_fuzz_script_descriptor_cache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_descriptor_cache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_descriptor_cache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_descriptor_cache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_descriptor_cache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_descriptor_cache_SOURCES = test/fuzz/script_descriptor_cache.cpp
test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1
test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_script_flags_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_flags_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_flags_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_flags_SOURCES = test/fuzz/script_flags.cpp
test_fuzz_script_interpreter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_interpreter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_interpreter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_interpreter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
+test_fuzz_script_assets_test_minimizer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_script_assets_test_minimizer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_script_assets_test_minimizer_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_script_assets_test_minimizer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_assets_test_minimizer_SOURCES = test/fuzz/script_assets_test_minimizer.cpp
+
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_ops_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_ops_SOURCES = test/fuzz/script_ops.cpp
test_fuzz_script_sigcache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_sigcache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_sigcache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_sigcache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sigcache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_sigcache_SOURCES = test/fuzz/script_sigcache.cpp
test_fuzz_script_sign_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_sign_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_sign_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_sign_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sign_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_sign_SOURCES = test/fuzz/script_sign.cpp
test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_scriptnum_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_scriptnum_ops_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_scriptnum_ops_SOURCES = test/fuzz/scriptnum_ops.cpp
+test_fuzz_secp256k1_ec_seckey_import_export_der_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ec_seckey_import_export_der_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_secp256k1_ec_seckey_import_export_der_SOURCES = test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
+
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_SOURCES = test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
+
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_service_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_service_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_service_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_signature_checker_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_signature_checker_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_signature_checker_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_signature_checker_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_signature_checker_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_signature_checker_SOURCES = test/fuzz/signature_checker.cpp
+test_fuzz_signet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_signet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_signet_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_signet_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_signet_SOURCES = test/fuzz/signet.cpp
+
test_fuzz_snapshotmetadata_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSNAPSHOTMETADATA_DESERIALIZE=1
test_fuzz_snapshotmetadata_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_snapshotmetadata_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_snapshotmetadata_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_snapshotmetadata_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_snapshotmetadata_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_span_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_span_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_span_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_span_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_span_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_span_SOURCES = test/fuzz/span.cpp
test_fuzz_spanparsing_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_spanparsing_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_spanparsing_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_spanparsing_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_spanparsing_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_spanparsing_SOURCES = test/fuzz/spanparsing.cpp
test_fuzz_string_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_string_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_string_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_string_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_string_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_string_SOURCES = test/fuzz/string.cpp
test_fuzz_strprintf_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_strprintf_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_strprintf_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_strprintf_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_strprintf_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_strprintf_SOURCES = test/fuzz/strprintf.cpp
test_fuzz_sub_net_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSUB_NET_DESERIALIZE=1
test_fuzz_sub_net_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_sub_net_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_sub_net_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_sub_net_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_sub_net_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_system_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_system_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_system_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_system_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_system_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_system_SOURCES = test/fuzz/system.cpp
test_fuzz_timedata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_timedata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_timedata_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_timedata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_timedata_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_timedata_SOURCES = test/fuzz/timedata.cpp
test_fuzz_transaction_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_transaction_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_transaction_SOURCES = test/fuzz/transaction.cpp
test_fuzz_tx_in_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_tx_in_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_in_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_in_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_in_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_in_SOURCES = test/fuzz/tx_in.cpp
test_fuzz_tx_in_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTX_IN_DESERIALIZE=1
test_fuzz_tx_in_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_in_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_in_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_in_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_in_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_tx_out_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_tx_out_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_out_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_out_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_out_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_out_SOURCES = test/fuzz/tx_out.cpp
test_fuzz_txoutcompressor_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXOUTCOMPRESSOR_DESERIALIZE=1
test_fuzz_txoutcompressor_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txoutcompressor_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_txoutcompressor_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_txrequest_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_txrequest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_txrequest_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_txrequest_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_txrequest_SOURCES = test/fuzz/txrequest.cpp
+
test_fuzz_txundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXUNDO_DESERIALIZE=1
test_fuzz_txundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_txundo_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_txundo_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_uint160_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DUINT160_DESERIALIZE=1
test_fuzz_uint160_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_uint160_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_uint160_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_uint160_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_uint160_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_uint256_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DUINT256_DESERIALIZE=1
test_fuzz_uint256_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_uint256_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_uint256_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_uint256_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_uint256_deserialize_SOURCES = test/fuzz/deserialize.cpp
endif # ENABLE_FUZZ
@@ -1208,7 +1252,7 @@ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
-CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno test/fuzz/*.gcda test/fuzz/*.gcno $(GENERATED_TEST_FILES) $(BITCOIN_TESTS:=.log)
+CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno test/fuzz/*.gcda test/fuzz/*.gcno test/util/*.gcda test/util/*.gcno $(GENERATED_TEST_FILES) $(BITCOIN_TESTS:=.log)
CLEANFILES += $(CLEAN_BITCOIN_TEST)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index f3e8a19de2..27f22826a9 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -10,6 +10,7 @@
#include <clientversion.h>
#include <cstdint>
#include <hash.h>
+#include <logging/timer.h>
#include <random.h>
#include <streams.h>
#include <tinyformat.h>
@@ -156,3 +157,22 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
}
return ret;
}
+
+void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
+{
+ LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
+ SerializeFileDB("anchors", anchors_db_path, anchors);
+}
+
+std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
+{
+ std::vector<CAddress> anchors;
+ if (DeserializeFileDB(anchors_db_path, anchors)) {
+ LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
+ } else {
+ anchors.clear();
+ }
+
+ fs::remove(anchors_db_path);
+ return anchors;
+}
diff --git a/src/addrdb.h b/src/addrdb.h
index 8410c3776c..4ac0e3e1b5 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -11,9 +11,9 @@
#include <serialize.h>
#include <string>
-#include <map>
+#include <vector>
-class CSubNet;
+class CAddress;
class CAddrMan;
class CDataStream;
@@ -73,4 +73,20 @@ public:
bool Read(banmap_t& banSet);
};
+/**
+ * Dump the anchor IP address database (anchors.dat)
+ *
+ * Anchors are last known outgoing block-relay-only peers that are
+ * tried to re-connect to on startup.
+ */
+void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors);
+
+/**
+ * Read the anchor IP address database (anchors.dat)
+ *
+ * Deleting anchors.dat is intentional as it avoids renewed peering to anchors after
+ * an unclean shutdown and thus potential exploitation of the anchor peer policy.
+ */
+std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path);
+
#endif // BITCOIN_ADDRDB_H
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 7aba340d9d..7636c6bad2 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -479,11 +479,15 @@ int CAddrMan::Check_()
}
#endif
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
+void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct)
{
- unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100;
- if (nNodes > ADDRMAN_GETADDR_MAX)
- nNodes = ADDRMAN_GETADDR_MAX;
+ size_t nNodes = vRandom.size();
+ if (max_pct != 0) {
+ nNodes = max_pct * nNodes / 100;
+ }
+ if (max_addresses != 0) {
+ nNodes = std::min(nNodes, max_addresses);
+ }
// gather a list of random nodes, skipping those of low quality
for (unsigned int n = 0; n < vRandom.size(); n++) {
diff --git a/src/addrman.h b/src/addrman.h
index 9e742339db..b4089dc894 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -12,6 +12,7 @@
#include <random.h>
#include <sync.h>
#include <timedata.h>
+#include <tinyformat.h>
#include <util/system.h>
#include <fs.h>
@@ -153,12 +154,6 @@ public:
//! how recent a successful connection should be before we allow an address to be evicted from tried
#define ADDRMAN_REPLACEMENT_HOURS 4
-//! the maximum percentage of nodes to return in a getaddr call
-#define ADDRMAN_GETADDR_MAX_PCT 23
-
-//! the maximum number of nodes to return in a getaddr call
-#define ADDRMAN_GETADDR_MAX 1000
-
//! Convenience
#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
@@ -261,7 +256,7 @@ protected:
#endif
//! Select several addresses at once.
- void GetAddr_(std::vector<CAddress> &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void GetAddr_(std::vector<CAddress> &vAddr, size_t max_addresses, size_t max_pct) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry as currently-connected-to.
void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -270,6 +265,14 @@ protected:
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
+ //! Serialization versions.
+ enum class 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
+ };
+
// Compressed IP->ASN mapping, loaded from a file when a node starts.
// Should be always empty if no file was provided.
// This mapping is then used for bucketing nodes in Addrman.
@@ -291,8 +294,8 @@ public:
/**
- * serialized format:
- * * version byte (1 for pre-asmap files, 2 for files including asmap version)
+ * Serialized format.
+ * * version byte (@see `Format`)
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
* * nNew
* * nTried
@@ -319,13 +322,16 @@ public:
* We don't use SERIALIZE_METHODS since the serialization and deserialization code has
* very little in common.
*/
- template<typename Stream>
- void Serialize(Stream &s) const
+ template <typename Stream>
+ void Serialize(Stream& s_) const
{
LOCK(cs);
- unsigned char nVersion = 2;
- s << nVersion;
+ // Always serialize in the latest version (currently Format::V3_BIP155).
+
+ OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
+
+ s << static_cast<uint8_t>(Format::V3_BIP155);
s << ((unsigned char)32);
s << nKey;
s << nNew;
@@ -376,14 +382,34 @@ public:
s << asmap_version;
}
- template<typename Stream>
- void Unserialize(Stream& s)
+ template <typename Stream>
+ void Unserialize(Stream& s_)
{
LOCK(cs);
Clear();
- unsigned char nVersion;
- s >> nVersion;
+
+ Format format;
+ s_ >> Using<CustomUintFormatter<1>>(format);
+
+ static constexpr Format maximum_supported_format = Format::V3_BIP155;
+ if (format > maximum_supported_format) {
+ throw std::ios_base::failure(strprintf(
+ "Unsupported format of addrman database: %u. Maximum supported is %u. "
+ "Continuing operation without using the saved list of peers.",
+ static_cast<uint8_t>(format),
+ static_cast<uint8_t>(maximum_supported_format)));
+ }
+
+ int stream_version = s_.GetVersion();
+ if (format >= Format::V3_BIP155) {
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
+ // unserialize methods know that an address in addrv2 format is coming.
+ stream_version |= ADDRV2_FORMAT;
+ }
+
+ OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
+
unsigned char nKeySize;
s >> nKeySize;
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
@@ -392,7 +418,7 @@ public:
s >> nTried;
int nUBuckets = 0;
s >> nUBuckets;
- if (nVersion != 0) {
+ if (format >= Format::V1_DETERMINISTIC) {
nUBuckets ^= (1 << 30);
}
@@ -455,7 +481,7 @@ public:
supplied_asmap_version = SerializeHash(m_asmap);
}
uint256 serialized_asmap_version;
- if (nVersion > 1) {
+ if (format >= Format::V2_ASMAP) {
s >> serialized_asmap_version;
}
@@ -463,13 +489,13 @@ public:
CAddrInfo &info = mapInfo[n];
int bucket = entryToBucket[n];
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
- if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
+ if (format >= Format::V2_ASMAP && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
// Bucketing has not changed, using existing bucket positions for the new table
vvNew[bucket][nUBucketPos] = n;
info.nRefCount++;
} else {
- // In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
+ // In case the new table data cannot be used (format unknown, bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
bucket = info.GetNewBucket(nKey, m_asmap);
@@ -638,13 +664,13 @@ public:
}
//! Return a bunch of addresses, selected at random.
- std::vector<CAddress> GetAddr()
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct)
{
Check();
std::vector<CAddress> vAddr;
{
LOCK(cs);
- GetAddr_(vAddr);
+ GetAddr_(vAddr, max_addresses, max_pct);
}
Check();
return vAddr;
diff --git a/src/banman.cpp b/src/banman.cpp
index 8752185a60..995fef3d07 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -184,7 +184,7 @@ void BanMan::SweepBanned()
while (it != m_banned.end()) {
CSubNet sub_net = (*it).first;
CBanEntry ban_entry = (*it).second;
- if (now > ban_entry.nBanUntil) {
+ if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
m_banned.erase(it++);
m_is_dirty = true;
notify_ui = true;
diff --git a/src/base58.cpp b/src/base58.cpp
index 9b2946e7a9..0dc6044145 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -35,7 +35,7 @@ static const int8_t mapBase58[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
+NODISCARD static bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && IsSpace(*psz))
@@ -84,21 +84,21 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_
return true;
}
-std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
+std::string EncodeBase58(Span<const unsigned char> input)
{
// Skip & count leading zeroes.
int zeroes = 0;
int length = 0;
- while (pbegin != pend && *pbegin == 0) {
- pbegin++;
+ while (input.size() > 0 && input[0] == 0) {
+ input = input.subspan(1);
zeroes++;
}
// Allocate enough space in big-endian base58 representation.
- int size = (pend - pbegin) * 138 / 100 + 1; // log(256) / log(58), rounded up.
+ int size = input.size() * 138 / 100 + 1; // log(256) / log(58), rounded up.
std::vector<unsigned char> b58(size);
// Process the bytes.
- while (pbegin != pend) {
- int carry = *pbegin;
+ while (input.size() > 0) {
+ int carry = input[0];
int i = 0;
// Apply "b58 = b58 * 256 + ch".
for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) {
@@ -109,7 +109,7 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
assert(carry == 0);
length = i;
- pbegin++;
+ input = input.subspan(1);
}
// Skip leading zeroes in base58 result.
std::vector<unsigned char>::iterator it = b58.begin() + (size - length);
@@ -124,11 +124,6 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
return str;
}
-std::string EncodeBase58(const std::vector<unsigned char>& vch)
-{
- return EncodeBase58(vch.data(), vch.data() + vch.size());
-}
-
bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len)
{
if (!ValidAsCString(str)) {
@@ -137,16 +132,16 @@ bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, in
return DecodeBase58(str.c_str(), vchRet, max_ret_len);
}
-std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
+std::string EncodeBase58Check(Span<const unsigned char> input)
{
// add 4-byte hash check to the end
- std::vector<unsigned char> vch(vchIn);
+ std::vector<unsigned char> vch(input.begin(), input.end());
uint256 hash = Hash(vch);
vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
return EncodeBase58(vch);
}
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
+NODISCARD static bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
{
if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) ||
(vchRet.size() < 4)) {
diff --git a/src/base58.h b/src/base58.h
index 042ad671d3..468c3e2589 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -15,27 +15,15 @@
#define BITCOIN_BASE58_H
#include <attributes.h>
+#include <span.h>
#include <string>
#include <vector>
/**
- * Encode a byte sequence as a base58-encoded string.
- * pbegin and pend cannot be nullptr, unless both are.
+ * Encode a byte span as a base58-encoded string
*/
-std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend);
-
-/**
- * Encode a byte vector as a base58-encoded string
- */
-std::string EncodeBase58(const std::vector<unsigned char>& vch);
-
-/**
- * Decode a base58-encoded string (psz) into a byte vector (vchRet).
- * return true if decoding is successful.
- * psz cannot be nullptr.
- */
-NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
+std::string EncodeBase58(Span<const unsigned char> input);
/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
@@ -44,15 +32,9 @@ NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet,
NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len);
/**
- * Encode a byte vector into a base58-encoded string, including checksum
- */
-std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
-
-/**
- * Decode a base58-encoded string (psz) that includes a checksum into a byte
- * vector (vchRet), return true if decoding is successful
+ * Encode a byte span into a base58-encoded string, including checksum
*/
-NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
+std::string EncodeBase58Check(Span<const unsigned char> input);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index 26d9340768..ebdad5a4b8 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -98,7 +98,7 @@ static void AddrManGetAddr(benchmark::Bench& bench)
FillAddrMan(addrman);
bench.run([&] {
- const auto& addresses = addrman.GetAddr();
+ const auto& addresses = addrman.GetAddr(2500, 23);
assert(addresses.size() > 0);
});
}
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 00544cba31..18cb5de196 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -20,7 +20,7 @@ static void Base58Encode(benchmark::Bench& bench)
}
};
bench.batch(buff.size()).unit("byte").run([&] {
- EncodeBase58(buff.data(), buff.data() + buff.size());
+ EncodeBase58(buff);
});
}
@@ -34,10 +34,8 @@ static void Base58CheckEncode(benchmark::Bench& bench)
200, 24
}
};
- std::vector<unsigned char> vch;
- vch.assign(buff.begin(), buff.end());
bench.batch(buff.size()).unit("byte").run([&] {
- EncodeBase58Check(vch);
+ EncodeBase58Check(buff);
});
}
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 01466d0b6f..012057e792 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -70,7 +70,10 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
std::cout << bench.complexityBigO() << std::endl;
}
- benchmarkResults.push_back(bench.results().back());
+
+ if (!bench.results().empty()) {
+ benchmarkResults.push_back(bench.results().back());
+ }
}
GenerateTemplateResults(benchmarkResults, args.output_csv, "# Benchmark, evals, iterations, total, min, max, median\n"
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 3f15f3f856..99a7ad237b 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -49,7 +49,7 @@ static void AssembleBlock(benchmark::Bench& bench)
for (const auto& txr : txs) {
TxValidationState state;
- bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */, /* nAbsurdFee */ 0)};
+ bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
assert(ret);
}
}
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index dc0aa4031c..a9f3f5f84d 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -34,7 +34,8 @@ static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
char a = '\0';
stream.write(&a, 1); // Prevent compaction
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ ArgsManager bench_args;
+ const auto chainParams = CreateChainParams(bench_args, CBaseChainParams::MAIN);
bench.unit("block").run([&] {
CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 19d7bc0dbc..ffa772d8c1 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -14,8 +14,6 @@
#include <vector>
-
-static const int MIN_CORES = 2;
static const size_t BATCHES = 101;
static const size_t BATCH_SIZE = 30;
static const int PREVECTOR_SIZE = 28;
@@ -26,6 +24,9 @@ static const unsigned int QUEUE_BATCH_SIZE = 128;
// and there is a little bit of work done between calls to Add.
static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
{
+ // We shouldn't ever be running with the checkqueue on a single core machine.
+ if (GetNumCores() <= 1) return;
+
const ECCVerifyHandle verify_handle;
ECC_Start();
@@ -44,7 +45,9 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
};
CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
boost::thread_group tg;
- for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) {
+ // The main thread should be counted to prevent thread oversubscription, and
+ // to decrease the variance of benchmark results.
+ for (auto x = 0; x < GetNumCores() - 1; ++x) {
tg.create_thread([&]{queue.Thread();});
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 3a71a6ca03..99aafd8dfc 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -31,7 +31,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
wallet.SetupLegacyScriptPubKeyMan();
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -65,7 +65,7 @@ static void CoinSelection(benchmark::Bench& bench)
typedef std::set<CInputCoin> CoinSet;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
+static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 36be86bcc8..65d16d47d8 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -7,6 +7,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <crypto/siphash.h>
#include <hash.h>
@@ -43,6 +44,15 @@ static void SHA256(benchmark::Bench& bench)
});
}
+static void SHA3_256_1M(benchmark::Bench& bench)
+{
+ uint8_t hash[SHA3_256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(BUFFER_SIZE,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ SHA3_256().Write(in).Finalize(hash);
+ });
+}
+
static void SHA256_32b(benchmark::Bench& bench)
{
std::vector<uint8_t> in(32,0);
@@ -99,6 +109,7 @@ BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
BENCHMARK(SHA512);
+BENCHMARK(SHA3_256_1M);
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index e16182b48e..b3b73284d8 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -26,7 +26,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet{chain.get(), WalletLocation(), CreateMockWalletDatabase()};
+ CWallet wallet{chain.get(), "", CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
bool first_run;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index cf52b710cb..ed58f1bbab 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -49,6 +49,7 @@ static void SetupCliArgs(ArgsManager& argsman)
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
+ const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -56,13 +57,15 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress 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("-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).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -83,11 +86,6 @@ static void libevent_log_cb(int severity, const char *msg)
}
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// Start
-//
-
//
// Exception thrown on connection error. This error is used to determine
// when to wait if -rpcwait is given.
@@ -108,9 +106,6 @@ public:
//
static int AppInitRPC(int argc, char* argv[])
{
- //
- // Parameters
- //
SetupCliArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
@@ -143,7 +138,7 @@ static int AppInitRPC(int argc, char* argv[])
tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
return EXIT_FAILURE;
}
- // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
+ // Check for chain settings (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(gArgs.GetChainName());
} catch (const std::exception& e) {
@@ -271,7 +266,13 @@ public:
result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
- result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]);
+
+ UniValue connections(UniValue::VOBJ);
+ connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
+ connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
+ connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
+ result.pushKV("connections", connections);
+
result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
@@ -291,6 +292,176 @@ public:
}
};
+/** Process netinfo requests */
+class NetinfoRequestHandler : public BaseRequestHandler
+{
+private:
+ static constexpr int8_t UNKNOWN_NETWORK{-1};
+ static constexpr size_t m_networks_size{3};
+ const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion"}};
+ std::array<std::array<uint16_t, m_networks_size + 2>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total/block-relay)
+ 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;
+ }
+ uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
+ bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
+ bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
+ bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
+ bool m_is_asmap_on{false};
+ size_t m_max_addr_length{0};
+ size_t m_max_id_length{2};
+ struct Peer {
+ int id;
+ int mapped_as;
+ int version;
+ int64_t conn_time;
+ int64_t last_blck;
+ int64_t last_recv;
+ int64_t last_send;
+ int64_t last_trxn;
+ double min_ping;
+ double ping;
+ std::string addr;
+ std::string network;
+ std::string sub_version;
+ bool is_block_relay;
+ bool is_outbound;
+ bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
+ };
+ std::vector<Peer> m_peers;
+ std::string ChainToString() const
+ {
+ if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet";
+ if (gArgs.GetChainName() == CBaseChainParams::REGTEST) return " regtest";
+ return "";
+ }
+ const int64_t m_time_now{GetSystemTimeInSeconds()};
+
+public:
+ static constexpr int ID_PEERINFO = 0;
+ static constexpr int ID_NETWORKINFO = 1;
+
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ if (!args.empty()) {
+ uint8_t n{0};
+ if (ParseUInt8(args.at(0), &n)) {
+ m_details_level = n;
+ }
+ }
+ UniValue result(UniValue::VARR);
+ result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
+ result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
+ return result;
+ }
+
+ UniValue ProcessReply(const UniValue& batch_in) override
+ {
+ const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
+ if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
+ if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
+
+ const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
+ if (networkinfo["version"].get_int() < 209900) {
+ throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
+ }
+
+ // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
+ for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
+ const std::string network{peer["network"].get_str()};
+ const int8_t network_id{NetworkStringToId(network)};
+ if (network_id == UNKNOWN_NETWORK) continue;
+ const bool is_outbound{!peer["inbound"].get_bool()};
+ const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ ++m_counts.at(is_outbound).at(network_id); // in/out by network
+ ++m_counts.at(is_outbound).at(m_networks_size); // in/out overall
+ ++m_counts.at(2).at(network_id); // total by network
+ ++m_counts.at(2).at(m_networks_size); // total overall
+ if (is_block_relay) {
+ ++m_counts.at(is_outbound).at(m_networks_size + 1); // in/out block-relay
+ ++m_counts.at(2).at(m_networks_size + 1); // total block-relay
+ }
+ if (DetailsRequested()) {
+ // Push data for this peer to the peers vector.
+ const int peer_id{peer["id"].get_int()};
+ const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
+ const int version{peer["version"].get_int()};
+ const int64_t conn_time{peer["conntime"].get_int64()};
+ const int64_t last_blck{peer["last_block"].get_int64()};
+ const int64_t last_recv{peer["lastrecv"].get_int64()};
+ const int64_t last_send{peer["lastsend"].get_int64()};
+ const int64_t last_trxn{peer["last_transaction"].get_int64()};
+ const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
+ const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
+ const std::string addr{peer["addr"].get_str()};
+ const std::string sub_version{peer["subver"].get_str()};
+ m_peers.push_back({peer_id, mapped_as, version, conn_time, last_blck, last_recv, last_send, last_trxn, min_ping, ping, addr, network, sub_version, is_block_relay, is_outbound});
+ m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
+ m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
+ m_is_asmap_on |= (mapped_as != 0);
+ }
+ }
+
+ // Generate report header.
+ std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
+
+ // Report detailed peer connections list sorted by direction and minimum ping time.
+ if (DetailsRequested() && !m_peers.empty()) {
+ std::sort(m_peers.begin(), m_peers.end());
+ result += "Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk uptime ";
+ if (m_is_asmap_on) result += " asmap ";
+ result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
+ for (const Peer& peer : m_peers) {
+ std::string version{ToString(peer.version) + peer.sub_version};
+ result += strprintf(
+ "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s%s\n",
+ peer.is_outbound ? "out" : "in",
+ peer.is_block_relay ? "block" : "full",
+ peer.network,
+ peer.min_ping == -1 ? "" : ToString(round(1000 * peer.min_ping)),
+ peer.ping == -1 ? "" : ToString(round(1000 * peer.ping)),
+ peer.last_send == 0 ? "" : ToString(m_time_now - peer.last_send),
+ peer.last_recv == 0 ? "" : ToString(m_time_now - peer.last_recv),
+ peer.last_trxn == 0 ? "" : ToString((m_time_now - peer.last_trxn) / 60),
+ peer.last_blck == 0 ? "" : ToString((m_time_now - peer.last_blck) / 60),
+ peer.conn_time == 0 ? "" : ToString((m_time_now - peer.conn_time) / 60),
+ m_is_asmap_on ? 7 : 0, // variable spacing
+ m_is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "",
+ m_max_id_length, // variable spacing
+ peer.id,
+ IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
+ IsAddressSelected() ? peer.addr : "",
+ IsVersionSelected() && version != "0" ? version : "");
+ }
+ result += " ms ms sec sec min min min\n\n";
+ }
+
+ // Report peer connection totals by type.
+ result += " ipv4 ipv6 onion total block-relay\n";
+ const std::array<std::string, 3> rows{{"in", "out", "total"}};
+ for (size_t i = 0; i < m_networks_size; ++i) {
+ result += strprintf("%-5s %5i %5i %5i %5i %5i\n", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2), m_counts.at(i).at(m_networks_size), m_counts.at(i).at(m_networks_size + 1));
+ }
+
+ // Report local addresses, ports, and scores.
+ result += "\nLocal addresses";
+ const UniValue& local_addrs{networkinfo["localaddresses"]};
+ if (local_addrs.empty()) {
+ result += ": n/a\n";
+ } else {
+ for (const UniValue& addr : local_addrs.getValues()) {
+ result += strprintf("\n%-40i port %5i score %6i", addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int());
+ }
+ }
+
+ return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
+ }
+};
+
/** Process RPC generatetoaddress request. */
class GenerateToAddressRequestHandler : public BaseRequestHandler
{
@@ -388,6 +559,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
assert(output_headers);
evhttp_add_header(output_headers, "Host", host.c_str());
evhttp_add_header(output_headers, "Connection", "close");
+ evhttp_add_header(output_headers, "Content-Type", "application/json");
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
// Attach request data
@@ -618,6 +790,8 @@ static int CommandLineRPC(int argc, char *argv[])
std::string method;
if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
+ } else if (gArgs.GetBoolArg("-netinfo", false)) {
+ rh.reset(new NetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-generate", false)) {
const UniValue getnewaddress{GetNewAddress()};
const UniValue& error{find_value(getnewaddress, "error")};
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 56afcb6ded..e22b3766cf 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -78,9 +78,6 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
//
static int AppInitRawTx(int argc, char* argv[])
{
- //
- // Parameters
- //
SetupBitcoinTxArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
@@ -88,7 +85,7 @@ static int AppInitRawTx(int argc, char* argv[])
return EXIT_FAILURE;
}
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
@@ -320,8 +317,8 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
if (!pubkey.IsCompressed()) {
throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
}
- // Call GetScriptForWitness() to build a P2WSH scriptPubKey
- scriptPubKey = GetScriptForWitness(scriptPubKey);
+ // Build a P2WPKH script
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for it.
@@ -390,8 +387,8 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs");
}
}
- // Call GetScriptForWitness() to build a P2WSH scriptPubKey
- scriptPubKey = GetScriptForWitness(scriptPubKey);
+ // Build a P2WSH with the multisig script
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(scriptPubKey));
}
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
@@ -464,7 +461,7 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
}
if (bSegWit) {
- scriptPubKey = GetScriptForWitness(scriptPubKey);
+ scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(scriptPubKey));
}
if (bScriptHash) {
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
@@ -597,7 +594,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
const int nOut = prevOut["vout"].get_int();
if (nOut < 0)
- throw std::runtime_error("vout must be positive");
+ throw std::runtime_error("vout cannot be negative");
COutPoint out(txid, nOut);
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 06b0c86476..8fdf1bae0f 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -62,7 +62,7 @@ static bool WalletAppInit(int argc, char* argv[])
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
return false;
}
- // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
SelectParams(gArgs.GetChainName());
return true;
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b04cc12059..455a82e390 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -37,41 +37,31 @@ static void WaitForShutdown(NodeContext& node)
Interrupt(node);
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// Start
-//
static bool AppInit(int argc, char* argv[])
{
NodeContext node;
- node.chain = interfaces::MakeChain(node);
bool fRet = false;
util::ThreadSetInternalName("init");
- //
- // Parameters
- //
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
SetupServerArgs(node);
+ ArgsManager& args = *Assert(node.args);
std::string error;
- if (!gArgs.ParseParameters(argc, argv, error)) {
+ if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
}
// Process help and version before taking care about datadir
- if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
- if (gArgs.IsArgSet("-version"))
- {
+ if (args.IsArgSet("-version")) {
strUsage += FormatParagraph(LicenseInfo()) + "\n";
- }
- else
- {
+ } else {
strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n";
- strUsage += "\n" + gArgs.GetHelpMessage();
+ strUsage += "\n" + args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
@@ -82,14 +72,14 @@ static bool AppInit(int argc, char* argv[])
try
{
if (!CheckDataDirOption()) {
- return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))));
+ return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""))));
}
- if (!gArgs.ReadConfigFiles(error, true)) {
+ if (!args.ReadConfigFiles(error, true)) {
return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error)));
}
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
- SelectParams(gArgs.GetChainName());
+ SelectParams(args.GetChainName());
} catch (const std::exception& e) {
return InitError(Untranslated(strprintf("%s\n", e.what())));
}
@@ -101,23 +91,21 @@ static bool AppInit(int argc, char* argv[])
}
}
- if (!gArgs.InitSettings(error)) {
+ if (!args.InitSettings(error)) {
InitError(Untranslated(error));
return false;
}
// -server defaults to true for bitcoind but not for the GUI so do this here
- gArgs.SoftSetBoolArg("-server", true);
+ args.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
- InitLogging();
- InitParameterInteraction();
- if (!AppInitBasicSetup())
- {
+ InitLogging(args);
+ InitParameterInteraction(args);
+ if (!AppInitBasicSetup(args)) {
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (!AppInitParameterInteraction())
- {
+ if (!AppInitParameterInteraction(args)) {
// InitError will have been called with detailed error, which ends up on console
return false;
}
@@ -126,8 +114,7 @@ static bool AppInit(int argc, char* argv[])
// InitError will have been called with detailed error, which ends up on console
return false;
}
- if (gArgs.GetBoolArg("-daemon", false))
- {
+ if (args.GetBoolArg("-daemon", false)) {
#if HAVE_DECL_DAEMON
#if defined(MAC_OSX)
#pragma GCC diagnostic push
@@ -152,7 +139,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitMain(context, node);
+ fRet = AppInitInterfaces(node) && AppInitMain(context, node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
diff --git a/src/chain.h b/src/chain.h
index 802e23f775..43e8a39f36 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -398,12 +398,6 @@ public:
return vChain[nHeight];
}
- /** Compare two chains efficiently. */
- friend bool operator==(const CChain &a, const CChain &b) {
- return a.vChain.size() == b.vChain.size() &&
- a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
- }
-
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
return (*this)[pindex->nHeight] == pindex;
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index a7c9e33f07..be2f5b53a6 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -7,6 +7,7 @@
#include <chainparamsseeds.h>
#include <consensus/merkle.h>
+#include <hash.h> // for signet block challenge hash
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -63,6 +64,8 @@ class CMainParams : public CChainParams {
public:
CMainParams() {
strNetworkID = CBaseChainParams::MAIN;
+ consensus.signet_blocks = false;
+ consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
consensus.BIP34Height = 227931;
@@ -83,10 +86,12 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // The best chain should have at least this much work.
- consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000e1ab5ec9348e9f4b8eb8154");
+ // 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
- // By default assume that the signatures in ancestors of this block are valid.
+ consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000e1ab5ec9348e9f4b8eb8154");
consensus.defaultAssumeValid = uint256S("0x0000000000000000000f2adce67e49b0b6bdeb9de8b7c3d7e93b21e7fc1e819d"); // 623950
/**
@@ -110,7 +115,7 @@ public:
// Note that of those which support the service bits prefix, most only support a subset of
// possible options.
- // This is fine at runtime as we'll fall back to using them as a oneshot if they don't support the
+ // This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the
// service bits we want, but we should get them updated to support all service bits wanted by any
// release ASAP to avoid it where possible.
vSeeds.emplace_back("seed.bitcoin.sipa.be"); // Pieter Wuille, only supports x1, x5, x9, and xd
@@ -172,6 +177,8 @@ class CTestNetParams : public CChainParams {
public:
CTestNetParams() {
strNetworkID = CBaseChainParams::TESTNET;
+ consensus.signet_blocks = false;
+ consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105");
consensus.BIP34Height = 21111;
@@ -192,10 +199,12 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
- // The best chain should have at least this much work.
- consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001495c1d5a01e2af8a23");
+ // 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
- // By default assume that the signatures in ancestors of this block are valid.
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001495c1d5a01e2af8a23");
consensus.defaultAssumeValid = uint256S("0x000000000000056c49030c174179b52a928c870e6e8a822c75973b7970cfbd01"); // 1692000
pchMessageStart[0] = 0x0b;
@@ -251,12 +260,121 @@ public:
};
/**
+ * Signet
+ */
+class SigNetParams : public CChainParams {
+public:
+ explicit SigNetParams(const ArgsManager& args) {
+ std::vector<uint8_t> bin;
+ vSeeds.clear();
+
+ if (!args.IsArgSet("-signetchallenge")) {
+ bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
+ vSeeds.emplace_back("178.128.221.177");
+ vSeeds.emplace_back("2a01:7c8:d005:390::5");
+ vSeeds.emplace_back("ntv3mtqw5wt63red.onion:38333");
+
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000d145533ce");
+ consensus.defaultAssumeValid = uint256S("0x00000128807d9175c494e24d805fc7854f7d79aa965cbb128342ad8b70cecfa5"); // 5348
+ m_assumed_blockchain_size = 1;
+ m_assumed_chain_state_size = 0;
+ chainTxData = ChainTxData{
+ // Data from RPC: getchaintxstats 4096 00000128807d9175c494e24d805fc7854f7d79aa965cbb128342ad8b70cecfa5
+ /* nTime */ 1601382000,
+ /* nTxCount */ 5435,
+ /* dTxRate */ 0.001898346323372538,
+ };
+ } else {
+ const auto signet_challenge = args.GetArgs("-signetchallenge");
+ if (signet_challenge.size() != 1) {
+ throw std::runtime_error(strprintf("%s: -signetchallenge cannot be multiple values.", __func__));
+ }
+ bin = ParseHex(signet_challenge[0]);
+
+ consensus.nMinimumChainWork = uint256{};
+ consensus.defaultAssumeValid = uint256{};
+ m_assumed_blockchain_size = 0;
+ m_assumed_chain_state_size = 0;
+ chainTxData = ChainTxData{
+ 0,
+ 0,
+ 0,
+ };
+ LogPrintf("Signet with challenge %s\n", signet_challenge[0]);
+ }
+
+ if (args.IsArgSet("-signetseednode")) {
+ vSeeds = args.GetArgs("-signetseednode");
+ }
+
+ strNetworkID = CBaseChainParams::SIGNET;
+ consensus.signet_blocks = true;
+ consensus.signet_challenge.assign(bin.begin(), bin.end());
+ consensus.nSubsidyHalvingInterval = 210000;
+ consensus.BIP16Exception = uint256{};
+ consensus.BIP34Height = 1;
+ consensus.BIP34Hash = uint256{};
+ consensus.BIP65Height = 1;
+ consensus.BIP66Height = 1;
+ consensus.CSVHeight = 1;
+ consensus.SegwitHeight = 1;
+ consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
+ consensus.nPowTargetSpacing = 10 * 60;
+ consensus.fPowAllowMinDifficultyBlocks = false;
+ consensus.fPowNoRetargeting = false;
+ consensus.nRuleChangeActivationThreshold = 1916;
+ consensus.nMinerConfirmationWindow = 2016;
+ 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
+
+ // 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;
+
+ // message start is defined as the first 4 bytes of the sha256d of the block script
+ CHashWriter h(SER_DISK, 0);
+ h << consensus.signet_challenge;
+ uint256 hash = h.GetHash();
+ memcpy(pchMessageStart, hash.begin(), 4);
+
+ nDefaultPort = 38333;
+ nPruneAfterHeight = 1000;
+
+ genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN);
+ consensus.hashGenesisBlock = genesis.GetHash();
+ assert(consensus.hashGenesisBlock == uint256S("0x00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"));
+ assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
+
+ vFixedSeeds.clear();
+
+ base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
+ base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
+ base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
+ base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
+ base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
+
+ bech32_hrp = "tb";
+
+ fDefaultConsistencyChecks = false;
+ fRequireStandard = true;
+ m_is_test_chain = true;
+ m_is_mockable_chain = false;
+ }
+};
+
+/**
* Regression test
*/
class CRegTestParams : public CChainParams {
public:
explicit CRegTestParams(const ArgsManager& args) {
strNetworkID = CBaseChainParams::REGTEST;
+ consensus.signet_blocks = false;
+ consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests)
@@ -276,12 +394,12 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
- // The best chain should have at least this much work.
- consensus.nMinimumChainWork = uint256S("0x00");
-
- // By default assume that the signatures in ancestors of this block are valid.
- consensus.defaultAssumeValid = uint256S("0x00");
+ consensus.nMinimumChainWork = uint256{};
+ consensus.defaultAssumeValid = uint256{};
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
@@ -389,19 +507,22 @@ const CChainParams &Params() {
return *globalChainParams;
}
-std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
+std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain)
{
- if (chain == CBaseChainParams::MAIN)
+ if (chain == CBaseChainParams::MAIN) {
return std::unique_ptr<CChainParams>(new CMainParams());
- else if (chain == CBaseChainParams::TESTNET)
+ } else if (chain == CBaseChainParams::TESTNET) {
return std::unique_ptr<CChainParams>(new CTestNetParams());
- else if (chain == CBaseChainParams::REGTEST)
- return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs));
+ } else if (chain == CBaseChainParams::SIGNET) {
+ return std::unique_ptr<CChainParams>(new SigNetParams(args));
+ } else if (chain == CBaseChainParams::REGTEST) {
+ return std::unique_ptr<CChainParams>(new CRegTestParams(args));
+ }
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
void SelectParams(const std::string& network)
{
SelectBaseParams(network);
- globalChainParams = CreateChainParams(network);
+ globalChainParams = CreateChainParams(gArgs, network);
}
diff --git a/src/chainparams.h b/src/chainparams.h
index 542ef329da..d8b25c7220 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -23,6 +23,11 @@ typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
MapCheckpoints mapCheckpoints;
+
+ int GetHeight() const {
+ const auto& final_checkpoint = mapCheckpoints.rbegin();
+ return final_checkpoint->first /* height */;
+ }
};
/**
@@ -114,7 +119,7 @@ protected:
* @returns a CChainParams* of the chosen chain.
* @throws a std::runtime_error if the chain is not supported.
*/
-std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain);
+std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain);
/**
* Return the currently selected parameters. This won't change after app
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 1825ced640..603969aaea 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -13,16 +13,20 @@
const std::string CBaseChainParams::MAIN = "main";
const std::string CBaseChainParams::TESTNET = "test";
+const std::string CBaseChainParams::SIGNET = "signet";
const std::string CBaseChainParams::REGTEST = "regtest";
void SetupChainParamsBaseOptions(ArgsManager& argsman)
{
- argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. 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("-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);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
@@ -33,16 +37,22 @@ const CBaseChainParams& BaseParams()
return *globalChainBaseParams;
}
+/**
+ * Port numbers for incoming Tor connections (8334, 18334, 38334, 18445) have
+ * been chosen arbitrarily to keep ranges of used ports tight.
+ */
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
- if (chain == CBaseChainParams::MAIN)
- return MakeUnique<CBaseChainParams>("", 8332);
- else if (chain == CBaseChainParams::TESTNET)
- return MakeUnique<CBaseChainParams>("testnet3", 18332);
- else if (chain == CBaseChainParams::REGTEST)
- return MakeUnique<CBaseChainParams>("regtest", 18443);
- else
- throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
+ if (chain == CBaseChainParams::MAIN) {
+ return MakeUnique<CBaseChainParams>("", 8332, 8334);
+ } else if (chain == CBaseChainParams::TESTNET) {
+ return MakeUnique<CBaseChainParams>("testnet3", 18332, 18334);
+ } else if (chain == CBaseChainParams::SIGNET) {
+ return MakeUnique<CBaseChainParams>("signet", 38332, 38334);
+ } else if (chain == CBaseChainParams::REGTEST) {
+ return MakeUnique<CBaseChainParams>("regtest", 18443, 18445);
+ }
+ throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
void SelectBaseParams(const std::string& chain)
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 1c52d0ea97..9b4ae2f7ab 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -21,17 +21,21 @@ public:
/** Chain name strings */
static const std::string MAIN;
static const std::string TESTNET;
+ static const std::string SIGNET;
static const std::string REGTEST;
///@}
const std::string& DataDir() const { return strDataDir; }
- int RPCPort() const { return nRPCPort; }
+ uint16_t RPCPort() const { return m_rpc_port; }
+ uint16_t OnionServiceTargetPort() const { return m_onion_service_target_port; }
CBaseChainParams() = delete;
- CBaseChainParams(const std::string& data_dir, int rpc_port) : nRPCPort(rpc_port), strDataDir(data_dir) {}
+ CBaseChainParams(const std::string& data_dir, uint16_t rpc_port, uint16_t onion_service_target_port)
+ : m_rpc_port(rpc_port), m_onion_service_target_port(onion_service_target_port), strDataDir(data_dir) {}
private:
- int nRPCPort;
+ const uint16_t m_rpc_port;
+ const uint16_t m_onion_service_target_port;
std::string strDataDir;
};
diff --git a/src/compat.h b/src/compat.h
index 68f6eb692c..0be02cae03 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -11,9 +11,6 @@
#endif
#ifdef WIN32
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN 1
-#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 61b1fbc2e5..0983595c6a 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -14,6 +14,7 @@ namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
+ DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
@@ -78,8 +79,17 @@ struct Params {
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
+ /** The best chain should have at least this much work */
uint256 nMinimumChainWork;
+ /** By default assume that the signatures in ancestors of this block are valid */
uint256 defaultAssumeValid;
+
+ /**
+ * If true, witness commitments contain a payload equal to a Bitcoin Script solution
+ * to the signet challenge. See BIP325.
+ */
+ bool signet_blocks{false};
+ std::vector<uint8_t> signet_challenge;
};
} // namespace Consensus
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 2a93a090d6..e007c481df 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -12,6 +12,12 @@
#include <primitives/transaction.h>
#include <primitives/block.h>
+/** Index marker for when no witness commitment is present in a coinbase transaction. */
+static constexpr int NO_WITNESS_COMMITMENT{-1};
+
+/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
+static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
+
/** A "reason" why a transaction was invalid, suitable for determining whether the
* provider of the transaction should be banned/ignored/disconnected/etc.
*/
@@ -151,4 +157,25 @@ static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION);
}
+/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
+inline int GetWitnessCommitmentIndex(const CBlock& block)
+{
+ int commitpos = NO_WITNESS_COMMITMENT;
+ if (!block.vtx.empty()) {
+ for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
+ const CTxOut& vout = block.vtx[0]->vout[o];
+ if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
+ vout.scriptPubKey[0] == OP_RETURN &&
+ vout.scriptPubKey[1] == 0x24 &&
+ vout.scriptPubKey[2] == 0xaa &&
+ vout.scriptPubKey[3] == 0x21 &&
+ vout.scriptPubKey[4] == 0xa9 &&
+ vout.scriptPubKey[5] == 0xed) {
+ commitpos = o;
+ }
+ }
+ }
+ return commitpos;
+}
+
#endif // BITCOIN_CONSENSUS_VALIDATION_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 1c0a8a096d..121e62457c 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -117,19 +117,14 @@ static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
return true;
}
-bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)
+static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness)
{
- if (!IsHex(hex_tx)) {
- return false;
- }
-
- std::vector<unsigned char> txData(ParseHex(hex_tx));
-
- if (try_no_witness) {
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
+ if (try_witness) {
+ CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> tx;
- if (ssData.eof() && (!try_witness || CheckTxScriptsSanity(tx))) {
+ // If transaction looks sane, we don't try other mode even if requested
+ if (ssData.empty() && (!try_no_witness || CheckTxScriptsSanity(tx))) {
return true;
}
} catch (const std::exception&) {
@@ -137,8 +132,8 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no
}
}
- if (try_witness) {
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
+ if (try_no_witness) {
+ CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
try {
ssData >> tx;
if (ssData.empty()) {
@@ -152,6 +147,16 @@ bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no
return false;
}
+bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)
+{
+ if (!IsHex(hex_tx)) {
+ return false;
+ }
+
+ std::vector<unsigned char> txData(ParseHex(hex_tx));
+ return DecodeTx(tx, txData, try_no_witness, try_witness);
+}
+
bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)
{
if (!IsHex(hex_header)) return false;
diff --git a/src/core_write.cpp b/src/core_write.cpp
index f9d918cb6d..3980d8cb2e 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -111,8 +111,9 @@ std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDeco
// checks in CheckSignatureEncoding.
if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, nullptr)) {
const unsigned char chSigHashType = vch.back();
- if (mapSigHashTypes.count(chSigHashType)) {
- strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
+ const auto it = mapSigHashTypes.find(chSigHashType);
+ if (it != mapSigHashTypes.end()) {
+ strSigHashDecode = "[" + it->second + "]";
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
}
}
diff --git a/src/crypto/common.h b/src/crypto/common.h
index 5b4932c992..c1acf8b22e 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -53,6 +53,13 @@ void static inline WriteLE64(unsigned char* ptr, uint64_t x)
memcpy(ptr, (char*)&v, 8);
}
+uint16_t static inline ReadBE16(const unsigned char* ptr)
+{
+ uint16_t x;
+ memcpy((char*)&x, ptr, 2);
+ return be16toh(x);
+}
+
uint32_t static inline ReadBE32(const unsigned char* ptr)
{
uint32_t x;
diff --git a/src/crypto/sha3.cpp b/src/crypto/sha3.cpp
new file mode 100644
index 0000000000..9c0c42fa77
--- /dev/null
+++ b/src/crypto/sha3.cpp
@@ -0,0 +1,161 @@
+// 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.
+
+// Based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
+// by Markku-Juhani O. Saarinen <mjos@iki.fi>
+
+#include <crypto/sha3.h>
+#include <crypto/common.h>
+#include <span.h>
+
+#include <algorithm>
+#include <array> // For std::begin and std::end.
+
+#include <stdint.h>
+
+// Internal implementation code.
+namespace
+{
+uint64_t Rotl(uint64_t x, int n) { return (x << n) | (x >> (64 - n)); }
+} // namespace
+
+void KeccakF(uint64_t (&st)[25])
+{
+ static constexpr uint64_t RNDC[24] = {
+ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
+ 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+ 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
+ 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
+ 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
+ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+ };
+ static constexpr int ROUNDS = 24;
+
+ for (int round = 0; round < ROUNDS; ++round) {
+ uint64_t bc0, bc1, bc2, bc3, bc4, t;
+
+ // Theta
+ bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
+ bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
+ bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
+ bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
+ bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
+ t = bc4 ^ Rotl(bc1, 1); st[0] ^= t; st[5] ^= t; st[10] ^= t; st[15] ^= t; st[20] ^= t;
+ t = bc0 ^ Rotl(bc2, 1); st[1] ^= t; st[6] ^= t; st[11] ^= t; st[16] ^= t; st[21] ^= t;
+ t = bc1 ^ Rotl(bc3, 1); st[2] ^= t; st[7] ^= t; st[12] ^= t; st[17] ^= t; st[22] ^= t;
+ t = bc2 ^ Rotl(bc4, 1); st[3] ^= t; st[8] ^= t; st[13] ^= t; st[18] ^= t; st[23] ^= t;
+ t = bc3 ^ Rotl(bc0, 1); st[4] ^= t; st[9] ^= t; st[14] ^= t; st[19] ^= t; st[24] ^= t;
+
+ // Rho Pi
+ t = st[1];
+ bc0 = st[10]; st[10] = Rotl(t, 1); t = bc0;
+ bc0 = st[7]; st[7] = Rotl(t, 3); t = bc0;
+ bc0 = st[11]; st[11] = Rotl(t, 6); t = bc0;
+ bc0 = st[17]; st[17] = Rotl(t, 10); t = bc0;
+ bc0 = st[18]; st[18] = Rotl(t, 15); t = bc0;
+ bc0 = st[3]; st[3] = Rotl(t, 21); t = bc0;
+ bc0 = st[5]; st[5] = Rotl(t, 28); t = bc0;
+ bc0 = st[16]; st[16] = Rotl(t, 36); t = bc0;
+ bc0 = st[8]; st[8] = Rotl(t, 45); t = bc0;
+ bc0 = st[21]; st[21] = Rotl(t, 55); t = bc0;
+ bc0 = st[24]; st[24] = Rotl(t, 2); t = bc0;
+ bc0 = st[4]; st[4] = Rotl(t, 14); t = bc0;
+ bc0 = st[15]; st[15] = Rotl(t, 27); t = bc0;
+ bc0 = st[23]; st[23] = Rotl(t, 41); t = bc0;
+ bc0 = st[19]; st[19] = Rotl(t, 56); t = bc0;
+ bc0 = st[13]; st[13] = Rotl(t, 8); t = bc0;
+ bc0 = st[12]; st[12] = Rotl(t, 25); t = bc0;
+ bc0 = st[2]; st[2] = Rotl(t, 43); t = bc0;
+ bc0 = st[20]; st[20] = Rotl(t, 62); t = bc0;
+ bc0 = st[14]; st[14] = Rotl(t, 18); t = bc0;
+ bc0 = st[22]; st[22] = Rotl(t, 39); t = bc0;
+ bc0 = st[9]; st[9] = Rotl(t, 61); t = bc0;
+ bc0 = st[6]; st[6] = Rotl(t, 20); t = bc0;
+ st[1] = Rotl(t, 44);
+
+ // Chi Iota
+ bc0 = st[0]; bc1 = st[1]; bc2 = st[2]; bc3 = st[3]; bc4 = st[4];
+ st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round];
+ st[1] = bc1 ^ (~bc2 & bc3);
+ st[2] = bc2 ^ (~bc3 & bc4);
+ st[3] = bc3 ^ (~bc4 & bc0);
+ st[4] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[5]; bc1 = st[6]; bc2 = st[7]; bc3 = st[8]; bc4 = st[9];
+ st[5] = bc0 ^ (~bc1 & bc2);
+ st[6] = bc1 ^ (~bc2 & bc3);
+ st[7] = bc2 ^ (~bc3 & bc4);
+ st[8] = bc3 ^ (~bc4 & bc0);
+ st[9] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[10]; bc1 = st[11]; bc2 = st[12]; bc3 = st[13]; bc4 = st[14];
+ st[10] = bc0 ^ (~bc1 & bc2);
+ st[11] = bc1 ^ (~bc2 & bc3);
+ st[12] = bc2 ^ (~bc3 & bc4);
+ st[13] = bc3 ^ (~bc4 & bc0);
+ st[14] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[15]; bc1 = st[16]; bc2 = st[17]; bc3 = st[18]; bc4 = st[19];
+ st[15] = bc0 ^ (~bc1 & bc2);
+ st[16] = bc1 ^ (~bc2 & bc3);
+ st[17] = bc2 ^ (~bc3 & bc4);
+ st[18] = bc3 ^ (~bc4 & bc0);
+ st[19] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[20]; bc1 = st[21]; bc2 = st[22]; bc3 = st[23]; bc4 = st[24];
+ st[20] = bc0 ^ (~bc1 & bc2);
+ st[21] = bc1 ^ (~bc2 & bc3);
+ st[22] = bc2 ^ (~bc3 & bc4);
+ st[23] = bc3 ^ (~bc4 & bc0);
+ st[24] = bc4 ^ (~bc0 & bc1);
+ }
+}
+
+SHA3_256& SHA3_256::Write(Span<const unsigned char> data)
+{
+ if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) {
+ // Fill the buffer and process it.
+ std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize, m_buffer + m_bufsize);
+ data = data.subspan(sizeof(m_buffer) - m_bufsize);
+ m_state[m_pos++] ^= ReadLE64(m_buffer);
+ m_bufsize = 0;
+ if (m_pos == RATE_BUFFERS) {
+ KeccakF(m_state);
+ m_pos = 0;
+ }
+ }
+ while (data.size() >= sizeof(m_buffer)) {
+ // Process chunks directly from the buffer.
+ m_state[m_pos++] ^= ReadLE64(data.data());
+ data = data.subspan(8);
+ if (m_pos == RATE_BUFFERS) {
+ KeccakF(m_state);
+ m_pos = 0;
+ }
+ }
+ if (data.size()) {
+ // Keep the remainder in the buffer.
+ std::copy(data.begin(), data.end(), m_buffer + m_bufsize);
+ m_bufsize += data.size();
+ }
+ return *this;
+}
+
+SHA3_256& SHA3_256::Finalize(Span<unsigned char> output)
+{
+ assert(output.size() == OUTPUT_SIZE);
+ std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0);
+ m_buffer[m_bufsize] ^= 0x06;
+ m_state[m_pos] ^= ReadLE64(m_buffer);
+ m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000;
+ KeccakF(m_state);
+ for (unsigned i = 0; i < 4; ++i) {
+ WriteLE64(output.data() + 8 * i, m_state[i]);
+ }
+ return *this;
+}
+
+SHA3_256& SHA3_256::Reset()
+{
+ m_bufsize = 0;
+ m_pos = 0;
+ std::fill(std::begin(m_state), std::end(m_state), 0);
+ return *this;
+}
diff --git a/src/crypto/sha3.h b/src/crypto/sha3.h
new file mode 100644
index 0000000000..88d8c1204d
--- /dev/null
+++ b/src/crypto/sha3.h
@@ -0,0 +1,41 @@
+// 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_CRYPTO_SHA3_H
+#define BITCOIN_CRYPTO_SHA3_H
+
+#include <span.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+//! The Keccak-f[1600] transform.
+void KeccakF(uint64_t (&st)[25]);
+
+class SHA3_256
+{
+private:
+ uint64_t m_state[25] = {0};
+ unsigned char m_buffer[8];
+ unsigned m_bufsize = 0;
+ unsigned m_pos = 0;
+
+ //! Sponge rate in bits.
+ static constexpr unsigned RATE_BITS = 1088;
+
+ //! Sponge rate expressed as a multiple of the buffer size.
+ static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer));
+
+ static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0, "Rate must be a multiple of 8 bytes");
+
+public:
+ static constexpr size_t OUTPUT_SIZE = 32;
+
+ SHA3_256() {}
+ SHA3_256& Write(Span<const unsigned char> data);
+ SHA3_256& Finalize(Span<unsigned char> output);
+ SHA3_256& Reset();
+};
+
+#endif // BITCOIN_CRYPTO_SHA3_H
diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp
index e81957111a..2e0106b165 100644
--- a/src/crypto/siphash.cpp
+++ b/src/crypto/siphash.cpp
@@ -49,7 +49,7 @@ CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size)
{
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
uint64_t t = tmp;
- int c = count;
+ uint8_t c = count;
while (size--) {
t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h
index b312f913f9..6b38950f8e 100644
--- a/src/crypto/siphash.h
+++ b/src/crypto/siphash.h
@@ -15,7 +15,7 @@ class CSipHasher
private:
uint64_t v[4];
uint64_t tmp;
- int count;
+ uint8_t count; // Only the low 8 bits of the input size matter.
public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 18dc7a69e2..8d2dcd0279 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -4,11 +4,8 @@
#include <util/system.h>
#include <walletinitinterface.h>
-#include <support/allocators/secure.h>
class CWallet;
-enum class WalletCreationStatus;
-struct bilingual_str;
namespace interfaces {
class Chain;
@@ -35,6 +32,7 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-discardfee=<amt>",
"-fallbackfee=<amt>",
"-keypool=<n>",
+ "-maxapsfee=<n>",
"-maxtxfee=<amt>",
"-mintxfee=<amt>",
"-paytxfee=<amt>",
@@ -48,7 +46,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-walletdir=<dir>",
"-walletnotify=<cmd>",
"-walletrbf",
- "-zapwallettxes=<mode>",
"-dblogsize=<n>",
"-flushwallet",
"-privdb",
@@ -58,37 +55,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
-fs::path GetWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<fs::path> ListWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<std::shared_ptr<CWallet>> GetWallets()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
namespace interfaces {
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
diff --git a/src/hash.cpp b/src/hash.cpp
index 4c09f5f646..3657b38639 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -6,6 +6,7 @@
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
+#include <string>
inline uint32_t ROTL32(uint32_t x, int8_t r)
{
@@ -77,3 +78,19 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he
num[3] = (nChild >> 0) & 0xFF;
CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output);
}
+
+uint256 SHA256Uint256(const uint256& input)
+{
+ uint256 result;
+ CSHA256().Write(input.begin(), 32).Finalize(result.begin());
+ return result;
+}
+
+CHashWriter TaggedHash(const std::string& tag)
+{
+ CHashWriter writer(SER_GETHASH, 0);
+ uint256 taghash;
+ CSHA256().Write((const unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
+ writer << taghash << taghash;
+ return writer;
+}
diff --git a/src/hash.h b/src/hash.h
index 71806483ff..6d876076ee 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
+#include <attributes.h>
#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
@@ -14,6 +15,7 @@
#include <uint256.h>
#include <version.h>
+#include <string>
#include <vector>
typedef uint256 ChainCode;
@@ -98,7 +100,7 @@ inline uint160 Hash160(const T1& in1)
class CHashWriter
{
private:
- CHash256 ctx;
+ CSHA256 ctx;
const int nType;
const int nVersion;
@@ -110,13 +112,27 @@ public:
int GetVersion() const { return nVersion; }
void write(const char *pch, size_t size) {
- ctx.Write({(const unsigned char*)pch, size});
+ ctx.Write((const unsigned char*)pch, size);
}
- // invalidates the object
+ /** Compute the double-SHA256 hash of all data written to this object.
+ *
+ * Invalidates this object.
+ */
uint256 GetHash() {
uint256 result;
- ctx.Finalize(result);
+ ctx.Finalize(result.begin());
+ ctx.Reset().Write(result.begin(), CSHA256::OUTPUT_SIZE).Finalize(result.begin());
+ return result;
+ }
+
+ /** Compute the SHA256 hash of all data written to this object.
+ *
+ * Invalidates this object.
+ */
+ uint256 GetSHA256() {
+ uint256 result;
+ ctx.Finalize(result.begin());
return result;
}
@@ -124,9 +140,8 @@ public:
* Returns the first 64 bits from the resulting hash.
*/
inline uint64_t GetCheapHash() {
- unsigned char result[CHash256::OUTPUT_SIZE];
- ctx.Finalize(result);
- return ReadLE64(result);
+ uint256 result = GetHash();
+ return ReadLE64(result.begin());
}
template<typename T>
@@ -181,8 +196,19 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL
return ss.GetHash();
}
+/** Single-SHA256 a 32-byte input (represented as uint256). */
+NODISCARD uint256 SHA256Uint256(const uint256& input);
+
unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vDataToHash);
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
+/** Return a CHashWriter primed for tagged hashes (as specified in BIP 340).
+ *
+ * The returned object will have SHA256(tag) written to it twice (= 64 bytes).
+ * A tagged hash can be computed by feeding the message into this object, and
+ * then calling CHashWriter::GetSHA256().
+ */
+CHashWriter TaggedHash(const std::string& tag);
+
#endif // BITCOIN_HASH_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index f587205a28..e67b813763 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -319,3 +319,12 @@ void BaseIndex::Stop()
m_thread_sync.join();
}
}
+
+IndexSummary BaseIndex::GetSummary() const
+{
+ IndexSummary summary{};
+ summary.name = GetName();
+ summary.synced = m_synced;
+ summary.best_block_height = m_best_block_index.load()->nHeight;
+ return summary;
+}
diff --git a/src/index/base.h b/src/index/base.h
index 3fab810bb2..8559e3cb64 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -13,6 +13,12 @@
class CBlockIndex;
+struct IndexSummary {
+ std::string name;
+ bool synced{false};
+ int best_block_height{0};
+};
+
/**
* Base class for indices of blockchain data. This implements
* CValidationInterface and ensures blocks are indexed sequentially according
@@ -21,6 +27,13 @@ class CBlockIndex;
class BaseIndex : public CValidationInterface
{
protected:
+ /**
+ * The database stores a block locator of the chain the database is synced to
+ * so that the index can efficiently determine the point it last stopped at.
+ * A locator is used instead of a simple hash of the chain tip because blocks
+ * and block index entries may not be flushed to disk until after this database
+ * is updated.
+ */
class DB : public CDBWrapper
{
public:
@@ -106,6 +119,9 @@ public:
/// Stops the instance from staying in sync with blockchain updates.
void Stop();
+
+ /// Get a summary of the index and its state.
+ IndexSummary GetSummary() const;
};
#endif // BITCOIN_INDEX_BASE_H
diff --git a/src/index/disktxpos.h b/src/index/disktxpos.h
new file mode 100644
index 0000000000..69696b0ec5
--- /dev/null
+++ b/src/index/disktxpos.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INDEX_DISKTXPOS_H
+#define BITCOIN_INDEX_DISKTXPOS_H
+
+#include <flatfile.h>
+#include <serialize.h>
+
+struct CDiskTxPos : public FlatFilePos
+{
+ unsigned int nTxOffset; // after header
+
+ SERIALIZE_METHODS(CDiskTxPos, obj)
+ {
+ READWRITEAS(FlatFilePos, obj);
+ READWRITE(VARINT(obj.nTxOffset));
+ }
+
+ CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
+ }
+
+ CDiskTxPos() {
+ SetNull();
+ }
+
+ void SetNull() {
+ FlatFilePos::SetNull();
+ nTxOffset = 0;
+ }
+};
+
+
+#endif // BITCOIN_INDEX_DISKTXPOS_H
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 64472714cc..462ac5962f 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.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 <index/disktxpos.h>
#include <index/txindex.h>
#include <node/ui_interface.h>
#include <shutdown.h>
@@ -15,38 +16,9 @@ constexpr char DB_TXINDEX_BLOCK = 'T';
std::unique_ptr<TxIndex> g_txindex;
-struct CDiskTxPos : public FlatFilePos
-{
- unsigned int nTxOffset; // after header
-
- SERIALIZE_METHODS(CDiskTxPos, obj)
- {
- READWRITEAS(FlatFilePos, obj);
- READWRITE(VARINT(obj.nTxOffset));
- }
-
- CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
- }
- CDiskTxPos() {
- SetNull();
- }
- void SetNull() {
- FlatFilePos::SetNull();
- nTxOffset = 0;
- }
-};
-
-/**
- * Access to the txindex database (indexes/txindex/)
- *
- * The database stores a block locator of the chain the database is synced to
- * so that the TxIndex can efficiently determine the point it last stopped at.
- * A locator is used instead of a simple hash of the chain tip because blocks
- * and block index entries may not be flushed to disk until after this database
- * is updated.
- */
+/** Access to the txindex database (indexes/txindex/) */
class TxIndex::DB : public BaseIndex::DB
{
public:
diff --git a/src/init.cpp b/src/init.cpp
index 08944b79a5..1387d6b982 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -24,6 +24,7 @@
#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <interfaces/chain.h>
+#include <interfaces/node.h>
#include <key.h>
#include <miner.h>
#include <net.h>
@@ -36,6 +37,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
+#include <protocol.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
@@ -106,14 +108,14 @@ static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
*/
static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
-static fs::path GetPidFile()
+static fs::path GetPidFile(const ArgsManager& args)
{
- return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
+ return AbsPathForConfigVal(fs::path(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
}
-NODISCARD static bool CreatePidFile()
+NODISCARD static bool CreatePidFile(const ArgsManager& args)
{
- fsbridge::ofstream file{GetPidFile()};
+ fsbridge::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
@@ -122,7 +124,7 @@ NODISCARD static bool CreatePidFile()
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile(args).string(), std::strerror(errno)));
}
}
@@ -179,13 +181,14 @@ void Shutdown(NodeContext& node)
TRY_LOCK(g_shutdown_mutex, lock_shutdown);
if (!lock_shutdown) return;
LogPrintf("%s: In progress...\n", __func__);
+ Assert(node.args);
/// Note: Shutdown() must be able to handle cases in which initialization failed part of the way,
/// for example if the data directory was found to be locked.
/// Be sure that anything that writes files or flushes caches only does this if the respective
/// module was initialized.
util::ThreadRename("shutoff");
- mempool.AddTransactionsUpdated(1);
+ if (node.mempool) node.mempool->AddTransactionsUpdated(1);
StopHTTPRPC();
StopREST();
@@ -198,7 +201,7 @@ 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.peer_logic) UnregisterValidationInterface(node.peer_logic.get());
+ if (node.peerman) UnregisterValidationInterface(node.peerman.get());
// Follow the lock order requirements:
// * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
// which locks cs_vNodes.
@@ -225,12 +228,12 @@ void Shutdown(NodeContext& node)
// After the threads that potentially access these pointers have been stopped,
// destruct and reset all to nullptr.
- node.peer_logic.reset();
+ node.peerman.reset();
node.connman.reset();
node.banman.reset();
- if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- DumpMempool(::mempool);
+ if (node.mempool && node.mempool->IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ DumpMempool(*node.mempool);
}
if (fFeeEstimatesInitialized)
@@ -300,19 +303,19 @@ void Shutdown(NodeContext& node)
GetMainSignals().UnregisterBackgroundSignalScheduler();
globalVerifyHandle.reset();
ECC_Stop();
- node.args = nullptr;
- node.mempool = nullptr;
+ node.mempool.reset();
node.chainman = nullptr;
node.scheduler.reset();
try {
- if (!fs::remove(GetPidFile())) {
+ if (!fs::remove(GetPidFile(*node.args))) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
}
} catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
+ node.args = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -371,15 +374,17 @@ void SetupServerArgs(NodeContext& node)
node.args = &gArgs;
ArgsManager& argsman = *node.args;
- SetupHelpOptions(gArgs);
+ SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
+ const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
- const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
- const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET);
- const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST);
+ const auto defaultChainParams = CreateChainParams(argsman, CBaseChainParams::MAIN);
+ const auto testnetChainParams = CreateChainParams(argsman, CBaseChainParams::TESTNET);
+ const auto signetChainParams = CreateChainParams(argsman, CBaseChainParams::SIGNET);
+ const auto regtestChainParams = CreateChainParams(argsman, CBaseChainParams::REGTEST);
// Hidden Options
std::vector<std::string> hidden_args = {
@@ -391,7 +396,7 @@ void SetupServerArgs(NodeContext& node)
#if HAVE_SYSTEM
argsman.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- argsman.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -409,7 +414,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), 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);
@@ -420,6 +425,9 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#if HAVE_SYSTEM
+ argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#endif
#ifndef WIN32
argsman.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
@@ -434,7 +442,7 @@ void SetupServerArgs(NodeContext& node)
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("-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>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, 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);
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -453,7 +461,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u signet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -486,26 +494,30 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ argsman.AddArg("-zmqpubsequence=<address>", "Enable publish hash block and tx sequence in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ argsman.AddArg("-zmqpubsequencehwm=<n>", strprintf("Set publish hash sequence message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
hidden_args.emplace_back("-zmqpubrawblock=<address>");
hidden_args.emplace_back("-zmqpubrawtx=<address>");
+ hidden_args.emplace_back("-zmqpubsequence=<n>");
hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
+ hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
#endif
argsman.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block 295000 (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -559,7 +571,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
- argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
+ argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
@@ -598,21 +610,6 @@ std::string LicenseInfo()
"\n";
}
-#if HAVE_SYSTEM
-static void BlockNotifyCallback(SynchronizationState sync_state, const CBlockIndex* pBlockIndex)
-{
- if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex)
- return;
-
- std::string strCmd = gArgs.GetArg("-blocknotify", "");
- if (!strCmd.empty()) {
- boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
- std::thread t(runCommand, strCmd);
- t.detach(); // thread runs free
- }
-}
-#endif
-
static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
@@ -683,7 +680,18 @@ static void CleanupBlockRevFiles()
}
}
-static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles)
+#if HAVE_SYSTEM
+static void StartupNotify(const ArgsManager& args)
+{
+ std::string cmd = args.GetArg("-startupnotify", "");
+ if (!cmd.empty()) {
+ std::thread t(runCommand, cmd);
+ t.detach(); // thread runs free
+ }
+}
+#endif
+
+static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
{
const CChainParams& chainparams = Params();
ScheduleBatchPriority();
@@ -745,16 +753,13 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
}
}
- if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
+ if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
return;
}
} // End scope of CImportingNow
- if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- LoadMempool(::mempool);
- }
- ::mempool.SetIsLoaded(!ShutdownRequested());
+ chainman.ActiveChainstate().LoadMempool(args);
}
/** Sanity checks
@@ -779,6 +784,7 @@ static bool InitSanityCheck()
static bool AppInitServers(const util::Ref& context, NodeContext& node)
{
+ const ArgsManager& args = *Assert(node.args);
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
@@ -787,71 +793,71 @@ static bool AppInitServers(const util::Ref& context, NodeContext& node)
node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(context))
return false;
- if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
+ if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
StartHTTPServer();
return true;
}
// Parameter interaction based on rules
-void InitParameterInteraction()
+void InitParameterInteraction(ArgsManager& args)
{
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
- if (gArgs.IsArgSet("-bind")) {
- if (gArgs.SoftSetBoolArg("-listen", true))
+ if (args.IsArgSet("-bind")) {
+ if (args.SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
}
- if (gArgs.IsArgSet("-whitebind")) {
- if (gArgs.SoftSetBoolArg("-listen", true))
+ if (args.IsArgSet("-whitebind")) {
+ if (args.SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
}
- if (gArgs.IsArgSet("-connect")) {
+ if (args.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
- if (gArgs.SoftSetBoolArg("-dnsseed", false))
+ if (args.SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-listen", false))
+ if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
}
- if (gArgs.IsArgSet("-proxy")) {
+ if (args.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy server is specified
- if (gArgs.SoftSetBoolArg("-listen", false))
+ if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
// to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
// to listen locally, so don't rely on this happening through -listen below.
- if (gArgs.SoftSetBoolArg("-upnp", false))
+ if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
// to protect privacy, do not discover addresses by default
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
}
- if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
+ if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening (pointless)
- if (gArgs.SoftSetBoolArg("-upnp", false))
+ if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
- if (gArgs.SoftSetBoolArg("-listenonion", false))
+ if (args.SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
}
- if (gArgs.IsArgSet("-externalip")) {
+ if (args.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
- if (gArgs.SoftSetBoolArg("-discover", false))
+ if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
// disable whitelistrelay in blocksonly mode
- if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
- if (gArgs.SoftSetBoolArg("-whitelistrelay", false))
+ if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
+ if (args.SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
}
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
- if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
- if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
+ if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ if (args.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
}
@@ -862,18 +868,18 @@ void InitParameterInteraction()
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
-void InitLogging()
+void InitLogging(const ArgsManager& args)
{
- LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
- LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
- LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
- LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
- LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ 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 = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
+ LogInstance().m_log_threadnames = args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
#endif
- fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
+ fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
std::string version_string = FormatFullVersion();
#ifdef DEBUG
@@ -908,7 +914,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
std::terminate();
};
-bool AppInitBasicSetup()
+bool AppInitBasicSetup(ArgsManager& args)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
@@ -928,7 +934,7 @@ bool AppInitBasicSetup()
}
#ifndef WIN32
- if (!gArgs.GetBoolArg("-sysperms", false)) {
+ if (!args.GetBoolArg("-sysperms", false)) {
umask(077);
}
@@ -950,7 +956,7 @@ bool AppInitBasicSetup()
return true;
}
-bool AppInitParameterInteraction()
+bool AppInitParameterInteraction(const ArgsManager& args)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 2: parameter interactions
@@ -960,9 +966,12 @@ bool AppInitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
- std::string network = gArgs.GetChainName();
+ std::string network = args.GetChainName();
+ if (network == CBaseChainParams::SIGNET) {
+ LogPrintf("Signet derived magic (message start): %s\n", HexStr(chainparams.MessageStart()));
+ }
bilingual_str errors;
- for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ for (const auto& arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
}
@@ -972,7 +981,7 @@ bool AppInitParameterInteraction()
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
- for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ for (const auto& section : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
}
@@ -981,15 +990,15 @@ bool AppInitParameterInteraction()
}
if (!fs::is_directory(GetBlocksDir())) {
- return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "")));
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
- std::string blockfilterindex_value = gArgs.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
+ std::string blockfilterindex_value = args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
- const std::vector<std::string> names = gArgs.GetArgs("-blockfilterindex");
+ const std::vector<std::string> names = args.GetArgs("-blockfilterindex");
for (const auto& name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
@@ -999,16 +1008,18 @@ bool AppInitParameterInteraction()
}
}
- // Basic filters are the only supported filters. The basic filters index must be enabled
- // to serve compact filters
- if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS) &&
- g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
- return InitError(_("Cannot set -peerblockfilters without -blockfilterindex."));
+ // Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index are both enabled.
+ if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
+ if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
+ return InitError(_("Cannot set -peerblockfilters without -blockfilterindex."));
+ }
+
+ nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
// if using block pruning, then disallow txindex
- if (gArgs.GetArg("-prune", 0)) {
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
+ if (args.GetArg("-prune", 0)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (!g_enabled_filter_types.empty()) {
return InitError(_("Prune mode is incompatible with -blockfilterindex."));
@@ -1016,14 +1027,14 @@ bool AppInitParameterInteraction()
}
// -bind and -whitebind can't be set when not listening
- size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
- if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
+ size_t nUserBind = args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
+ if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
- nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
+ nUserMaxConnections = args.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
@@ -1043,9 +1054,9 @@ bool AppInitParameterInteraction()
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
- if (gArgs.IsArgSet("-debug")) {
+ if (args.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
- const std::vector<std::string> categories = gArgs.GetArgs("-debug");
+ 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";})) {
@@ -1058,28 +1069,23 @@ bool AppInitParameterInteraction()
}
// Now remove the logging categories which were explicitly excluded
- for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
+ for (const std::string& cat : args.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
}
}
- // Checkmempool and checkblockindex default to true in regtest mode
- int ratio = std::min<int>(std::max<int>(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
- if (ratio != 0) {
- mempool.setSanityCheck(1.0 / ratio);
- }
- fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
- fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
+ fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
+ fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
- hashAssumeValid = uint256S(gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
+ hashAssumeValid = uint256S(args.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());
else
LogPrintf("Validating signatures for all blocks.\n");
- if (gArgs.IsArgSet("-minimumchainwork")) {
- const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
+ if (args.IsArgSet("-minimumchainwork")) {
+ const std::string minChainWorkStr = args.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr));
}
@@ -1093,22 +1099,21 @@ bool AppInitParameterInteraction()
}
// mempool limits
- int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
+ int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMin = args.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
- if (gArgs.IsArgSet("-incrementalrelayfee"))
- {
+ if (args.IsArgSet("-incrementalrelayfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
- return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
+ if (!ParseMoney(args.GetArg("-incrementalrelayfee", ""), n))
+ return InitError(AmountErrMsg("incrementalrelayfee", args.GetArg("-incrementalrelayfee", "")));
incrementalRelayFee = CFeeRate(n);
}
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
- int64_t nPruneArg = gArgs.GetArg("-prune", 0);
+ int64_t nPruneArg = args.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
@@ -1125,20 +1130,20 @@ bool AppInitParameterInteraction()
fPruneMode = true;
}
- nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
+ nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
- peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
+ peer_connect_timeout = args.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
}
- if (gArgs.IsArgSet("-minrelaytxfee")) {
+ if (args.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
- return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
+ if (!ParseMoney(args.GetArg("-minrelaytxfee", ""), n)) {
+ return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in CWallet::CreateWalletFromFile()
::minRelayTxFee = CFeeRate(n);
@@ -1150,48 +1155,50 @@ bool AppInitParameterInteraction()
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that happens
- if (gArgs.IsArgSet("-blockmintxfee"))
- {
+ if (args.IsArgSet("-blockmintxfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
- return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
+ if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n))
+ return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions
- if (gArgs.IsArgSet("-dustrelayfee"))
- {
+ if (args.IsArgSet("-dustrelayfee")) {
CAmount n = 0;
- if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n))
- return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
+ if (!ParseMoney(args.GetArg("-dustrelayfee", ""), n))
+ return InitError(AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
- fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
+ fRequireStandard = !args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
}
- nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
+ nBytesPerSigOp = args.GetArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
- fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
- fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
- nMaxDatacarrierBytes = gArgs.GetArg("-datacarriersize", nMaxDatacarrierBytes);
+ fIsBareMultisigStd = args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
+ fAcceptDatacarrier = args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
+ nMaxDatacarrierBytes = args.GetArg("-datacarriersize", nMaxDatacarrierBytes);
// Option to startup with mocktime set (used for regression testing):
- SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+ SetMockTime(args.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
- if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
+ if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
+ if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError(Untranslated("rpcserialversion must be non-negative."));
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError(Untranslated("Unknown rpcserialversion requested."));
- nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
+ nMaxTipAge = args.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
+
+ if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
+ return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
+ }
return true;
}
@@ -1242,16 +1249,28 @@ bool AppInitLockDataDirectory()
return true;
}
-bool AppInitMain(const util::Ref& context, NodeContext& node)
+bool AppInitInterfaces(NodeContext& node)
{
+ node.chain = interfaces::MakeChain(node);
+ // Create client interfaces for wallets that are supposed to be loaded
+ // according to -wallet and -disablewallet options. This only constructs
+ // the interfaces, it doesn't load wallet data. Wallets actually get loaded
+ // when load() and start() interface methods are called below.
+ g_wallet_init_interface.Construct(node);
+ return true;
+}
+
+bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
+{
+ const ArgsManager& args = *Assert(node.args);
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
- if (!CreatePidFile()) {
+ if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
}
if (LogInstance().m_print_to_file) {
- if (gArgs.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
+ 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();
@@ -1268,10 +1287,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
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(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ 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 (gArgs.IsArgSet("-conf")) {
+ } 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\n"), config_file_path.string()));
} else {
@@ -1280,23 +1299,23 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
}
// Log the config arguments to debug.log
- gArgs.LogArgs();
+ args.LogArgs();
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
// Warn about relative -datadir path.
- if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) {
+ if (args.IsArgSet("-datadir") && !fs::path(args.GetArg("-datadir", "")).is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
"current working directory '%s'. This is fragile, because if bitcoin is started in the future "
"from a different location, it will be unable to locate the current data files. There could "
"also be data loss if bitcoin is started while in a temporary directory.\n",
- gArgs.GetArg("-datadir", ""), fs::current_path().string());
+ args.GetArg("-datadir", ""), fs::current_path().string());
}
InitSignatureCache();
InitScriptExecutionCache();
- int script_threads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
+ int script_threads = args.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script threads)
@@ -1330,12 +1349,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
- // Create client interfaces for wallets that are supposed to be loaded
- // according to -wallet and -disablewallet options. This only constructs
- // the interfaces, it doesn't load wallet data. Wallets actually get loaded
- // when load() and start() interface methods are called below.
- g_wallet_init_interface.Construct(node);
-
/* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
@@ -1352,8 +1365,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
* that the server is there and will be ready later). Warmup mode will
* be disabled when initialisation is finished.
*/
- if (gArgs.GetBoolArg("-server", false))
- {
+ if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers(context, node))
return InitError(_("Unable to start HTTP server. See debug log for details."));
@@ -1373,23 +1385,31 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
// need to reindex later.
assert(!node.banman);
- node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
- node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), gArgs.GetBoolArg("-networkactive", true));
+ node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true));
+
// Make mempool generally available in the node context. For example the connection manager, wallet, or RPC threads,
// which are all started after this, may use it from the node context.
assert(!node.mempool);
- node.mempool = &::mempool;
+ node.mempool = MakeUnique<CTxMemPool>(&::feeEstimator);
+ if (node.mempool) {
+ int ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
+ if (ratio != 0) {
+ node.mempool->setSanityCheck(1.0 / ratio);
+ }
+ }
+
assert(!node.chainman);
node.chainman = &g_chainman;
ChainstateManager& chainman = *Assert(node.chainman);
- node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, chainman, *node.mempool));
- RegisterValidationInterface(node.peer_logic.get());
+ node.peerman.reset(new PeerManager(chainparams, *node.connman, node.banman.get(), *node.scheduler, chainman, *node.mempool));
+ RegisterValidationInterface(node.peerman.get());
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
- for (const std::string& cmt : gArgs.GetArgs("-uacomment")) {
+ for (const std::string& cmt : args.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
uacomments.push_back(cmt);
@@ -1400,9 +1420,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
- if (gArgs.IsArgSet("-onlynet")) {
+ if (args.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
- for (const std::string& snet : gArgs.GetArgs("-onlynet")) {
+ for (const std::string& snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
@@ -1416,12 +1436,12 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
}
// Check for host lookup allowed before parsing any network related parameters
- fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
+ fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
- bool proxyRandomize = gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
+ bool proxyRandomize = args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
- std::string proxyArg = gArgs.GetArg("-proxy", "");
+ std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
@@ -1443,7 +1463,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
// An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none)
- std::string onionArg = gArgs.GetArg("-onion", "");
+ std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
@@ -1461,11 +1481,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
}
// see Step 2: parameter interactions for more information about these
- fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
- fDiscover = gArgs.GetBoolArg("-discover", true);
- g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
+ fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
+ fDiscover = args.GetBoolArg("-discover", true);
+ g_relay_txes = !args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
- for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
+ for (const std::string& strAddr : args.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
@@ -1474,8 +1494,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
}
// Read asmap file if configured
- if (gArgs.IsArgSet("-asmap")) {
- fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", ""));
+ if (args.IsArgSet("-asmap")) {
+ fs::path asmap_path = fs::path(args.GetArg("-asmap", ""));
if (asmap_path.empty()) {
asmap_path = DEFAULT_ASMAP_FILENAME;
}
@@ -1508,22 +1528,22 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set
uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
- if (gArgs.IsArgSet("-maxuploadtarget")) {
- nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024;
+ if (args.IsArgSet("-maxuploadtarget")) {
+ nMaxOutboundLimit = args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024;
}
// ********************************************************* Step 7: load block chain
- fReindex = gArgs.GetBoolArg("-reindex", false);
- bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
+ fReindex = args.GetBoolArg("-reindex", false);
+ bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
- int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
+ int64_t nTotalCache = (args.GetArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
nTotalCache -= nBlockTreeDBCache;
- int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
+ int64_t nTxIndexCache = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
nTotalCache -= nTxIndexCache;
int64_t filter_index_cache = 0;
if (!g_enabled_filter_types.empty()) {
@@ -1536,10 +1556,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
int64_t nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
- int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
@@ -1563,11 +1583,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
- chainman.InitializeChainstate();
+ chainman.InitializeChainstate(*Assert(node.mempool));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
- UnloadBlockIndex(node.mempool);
+ UnloadBlockIndex(node.mempool.get(), chainman);
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
@@ -1622,7 +1642,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
bool failed_chainstate_init = false;
for (CChainState* chainstate : chainman.GetAll()) {
- LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
chainstate->InitCoinsDB(
/* cache_size_bytes */ nCoinDBCache,
/* in_memory */ false,
@@ -1704,7 +1723,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
- if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
+ 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);
}
@@ -1722,10 +1741,10 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
// 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(
+ !CVerifyDB().VerifyDB(
chainparams, &chainstate->CoinsDB(),
- gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
- gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
+ args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
failed_verification = true;
break;
@@ -1781,7 +1800,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
fFeeEstimatesInitialized = true;
// ********************************************************* Step 8: start indexers
- if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex);
g_txindex->Start();
}
@@ -1841,16 +1860,26 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
}
#if HAVE_SYSTEM
- if (gArgs.IsArgSet("-blocknotify"))
- uiInterface.NotifyBlockTip_connect(BlockNotifyCallback);
+ const std::string block_notify = args.GetArg("-blocknotify", "");
+ if (!block_notify.empty()) {
+ uiInterface.NotifyBlockTip_connect([block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) {
+ if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) return;
+ std::string command = block_notify;
+ boost::replace_all(command, "%s", pBlockIndex->GetBlockHash().GetHex());
+ std::thread t(runCommand, command);
+ t.detach(); // thread runs free
+ });
+ }
#endif
std::vector<fs::path> vImportFiles;
- for (const std::string& strFile : gArgs.GetArgs("-loadblock")) {
+ for (const std::string& strFile : args.GetArgs("-loadblock")) {
vImportFiles.push_back(strFile);
}
- g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman]{ ThreadImport(chainman, vImportFiles); });
+ g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman, &args] {
+ ThreadImport(chainman, vImportFiles, args);
+ });
// Wait for genesis block to be processed
{
@@ -1877,16 +1906,22 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
LOCK(cs_main);
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
chain_active_height = chainman.ActiveChain().Height();
+ if (tip_info) {
+ tip_info->block_height = chain_active_height;
+ tip_info->block_time = chainman.ActiveChain().Tip() ? chainman.ActiveChain().Tip()->GetBlockTime() : Params().GenesisBlock().GetBlockTime();
+ tip_info->verification_progress = GuessVerificationProgress(Params().TxData(), chainman.ActiveChain().Tip());
+ }
+ if (tip_info && ::pindexBestHeader) {
+ tip_info->header_height = ::pindexBestHeader->nHeight;
+ tip_info->header_time = ::pindexBestHeader->GetBlockTime();
+ }
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
- if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
- StartTorControl();
-
Discover();
// Map ports with UPnP
- if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
+ if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) {
StartMapPort();
}
@@ -1900,42 +1935,68 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
connOptions.nBestHeight = chain_active_height;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
- connOptions.m_msgproc = node.peer_logic.get();
- connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
- connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
- connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
+ connOptions.m_msgproc = node.peerman.get();
+ connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
+ connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
connOptions.m_peer_connect_timeout = peer_connect_timeout;
- for (const std::string& strBind : gArgs.GetArgs("-bind")) {
- CService addrBind;
- if (!Lookup(strBind, addrBind, GetListenPort(), false)) {
- return InitError(ResolveErrMsg("bind", strBind));
+ for (const std::string& bind_arg : args.GetArgs("-bind")) {
+ CService bind_addr;
+ const size_t index = bind_arg.rfind('=');
+ if (index == std::string::npos) {
+ if (Lookup(bind_arg, bind_addr, GetListenPort(), false)) {
+ connOptions.vBinds.push_back(bind_addr);
+ continue;
+ }
+ } else {
+ const std::string network_type = bind_arg.substr(index + 1);
+ if (network_type == "onion") {
+ const std::string truncated_bind_arg = bind_arg.substr(0, index);
+ if (Lookup(truncated_bind_arg, bind_addr, BaseParams().OnionServiceTargetPort(), false)) {
+ connOptions.onion_binds.push_back(bind_addr);
+ continue;
+ }
+ }
+ }
+ return InitError(ResolveErrMsg("bind", bind_arg));
+ }
+
+ if (connOptions.onion_binds.empty()) {
+ connOptions.onion_binds.push_back(DefaultOnionServiceTarget());
+ }
+
+ if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
+ const auto bind_addr = connOptions.onion_binds.front();
+ if (connOptions.onion_binds.size() > 1) {
+ InitWarning(strprintf(_("More than one onion bind address is provided. Using %s for the automatically created Tor onion service."), bind_addr.ToStringIPPort()));
}
- connOptions.vBinds.push_back(addrBind);
+ StartTorControl(bind_addr);
}
- for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
+
+ for (const std::string& strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}
- for (const auto& net : gArgs.GetArgs("-whitelist")) {
+ for (const auto& net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
- connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
+ connOptions.vSeedNodes = args.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
- connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
+ connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
- const auto connect = gArgs.GetArgs("-connect");
+ const auto connect = args.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
@@ -1958,5 +2019,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
banman->DumpBanlist();
}, DUMP_BANS_INTERVAL);
+#if HAVE_SYSTEM
+ StartupNotify(args);
+#endif
+
return true;
}
diff --git a/src/init.h b/src/init.h
index 33fe96e8ea..679e875da1 100644
--- a/src/init.h
+++ b/src/init.h
@@ -8,9 +8,12 @@
#include <memory>
#include <string>
-#include <util/system.h>
+class ArgsManager;
struct NodeContext;
+namespace interfaces {
+struct BlockAndHeaderTipInfo;
+}
namespace boost {
class thread_group;
} // namespace boost
@@ -22,21 +25,21 @@ class Ref;
void Interrupt(NodeContext& node);
void Shutdown(NodeContext& node);
//!Initialize the logging infrastructure
-void InitLogging();
+void InitLogging(const ArgsManager& args);
//!Parameter interaction: change current parameters depending on various rules
-void InitParameterInteraction();
+void InitParameterInteraction(ArgsManager& args);
/** Initialize bitcoin core: Basic context setup.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
-bool AppInitBasicSetup();
+bool AppInitBasicSetup(ArgsManager& args);
/**
* Initialization: parameter interaction.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
*/
-bool AppInitParameterInteraction();
+bool AppInitParameterInteraction(const ArgsManager& args);
/**
* Initialization sanity checks: ecc init, sanity checks, dir lock.
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
@@ -50,11 +53,15 @@ bool AppInitSanityChecks();
*/
bool AppInitLockDataDirectory();
/**
+ * Initialize node and wallet interface pointers. Has no prerequisites or side effects besides allocating memory.
+ */
+bool AppInitInterfaces(NodeContext& node);
+/**
* Bitcoin core main initialization.
* @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 util::Ref& context, NodeContext& node);
+bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
/**
* Register all arguments with the ArgsManager
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index d49e4454af..4c5ebe66fc 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -59,13 +59,13 @@ public:
explicit NotificationsProxy(std::shared_ptr<Chain::Notifications> notifications)
: m_notifications(std::move(notifications)) {}
virtual ~NotificationsProxy() = default;
- void TransactionAddedToMempool(const CTransactionRef& tx) override
+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override
{
- m_notifications->transactionAddedToMempool(tx);
+ m_notifications->transactionAddedToMempool(tx, mempool_sequence);
}
- void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override
{
- m_notifications->transactionRemovedFromMempool(tx, reason);
+ m_notifications->transactionRemovedFromMempool(tx, reason, mempool_sequence);
}
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{
@@ -276,13 +276,15 @@ public:
}
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
{
- LOCK(::mempool.cs);
- return IsRBFOptIn(tx, ::mempool);
+ if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx);
+ LOCK(m_node.mempool->cs);
+ return IsRBFOptIn(tx, *m_node.mempool);
}
bool hasDescendantsInMempool(const uint256& txid) override
{
- LOCK(::mempool.cs);
- auto it = ::mempool.GetIter(txid);
+ if (!m_node.mempool) return false;
+ LOCK(m_node.mempool->cs);
+ auto it = m_node.mempool->GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
}
bool broadcastTransaction(const CTransactionRef& tx,
@@ -298,7 +300,9 @@ public:
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
- ::mempool.GetTransactionAncestry(txid, ancestors, descendants);
+ ancestors = descendants = 0;
+ if (!m_node.mempool) return;
+ m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
@@ -307,6 +311,7 @@ public:
}
bool checkChainLimits(const CTransactionRef& tx) override
{
+ if (!m_node.mempool) return true;
LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
CTxMemPool::setEntries ancestors;
@@ -315,8 +320,9 @@ public:
auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
std::string unused_error_string;
- LOCK(::mempool.cs);
- return ::mempool.CalculateMemPoolAncestors(entry, ancestors, limit_ancestor_count, limit_ancestor_size,
+ LOCK(m_node.mempool->cs);
+ return m_node.mempool->CalculateMemPoolAncestors(
+ entry, ancestors, limit_ancestor_count, limit_ancestor_size,
limit_descendant_count, limit_descendant_size, unused_error_string);
}
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
@@ -329,7 +335,8 @@ public:
}
CFeeRate mempoolMinFee() override
{
- return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ if (!m_node.mempool) return {};
+ return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
@@ -372,11 +379,33 @@ public:
RPCRunLater(name, std::move(fn), seconds);
}
int rpcSerializationFlags() override { return RPCSerializationFlags(); }
+ util::SettingsValue getRwSetting(const std::string& name) override
+ {
+ util::SettingsValue result;
+ gArgs.LockSettings([&](const util::Settings& settings) {
+ if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) {
+ result = *value;
+ }
+ });
+ return result;
+ }
+ bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override
+ {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ if (value.isNull()) {
+ settings.rw_settings.erase(name);
+ } else {
+ settings.rw_settings[name] = value;
+ }
+ });
+ return gArgs.WriteSettingsFile();
+ }
void requestMempoolTransactions(Notifications& notifications) override
{
- LOCK2(::cs_main, ::mempool.cs);
- for (const CTxMemPoolEntry& entry : ::mempool.mapTx) {
- notifications.transactionAddedToMempool(entry.GetSharedTx());
+ if (!m_node.mempool) return;
+ LOCK2(::cs_main, m_node.mempool->cs);
+ for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) {
+ notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index bbeb0fa801..85d09be0f3 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -7,6 +7,7 @@
#include <optional.h> // For Optional and nullopt
#include <primitives/transaction.h> // For CTransactionRef
+#include <util/settings.h> // For util::SettingsValue
#include <functional>
#include <memory>
@@ -241,8 +242,8 @@ public:
{
public:
virtual ~Notifications() {}
- virtual void transactionAddedToMempool(const CTransactionRef& tx) {}
- virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
+ virtual void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {}
+ virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {}
virtual void blockConnected(const CBlock& block, int height) {}
virtual void blockDisconnected(const CBlock& block, int height) {}
virtual void updatedBlockTip() {}
@@ -269,6 +270,12 @@ public:
//! Current RPC serialization flags.
virtual int rpcSerializationFlags() = 0;
+ //! Return <datadir>/settings.json setting value.
+ virtual util::SettingsValue getRwSetting(const std::string& name) = 0;
+
+ //! Write a setting to <datadir>/settings.json.
+ virtual bool updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0;
+
//! Synchronously send transactionAddedToMempool notifications about all
//! current mempool transactions to the specified handler and return after
//! the last one is sent. These notifications aren't coordinated with async
@@ -307,24 +314,11 @@ public:
//! Set mock time.
virtual void setMockTime(int64_t time) = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr<Chain> MakeChain(NodeContext& node);
-//! Return implementation of ChainClient interface for a wallet client. This
-//! function will be undefined in builds where ENABLE_WALLET is false.
-//!
-//! Currently, wallets are the only chain clients. But in the future, other
-//! types of chain clients could be added, such as tools for monitoring,
-//! analysis, or fee estimation. These clients need to expose their own
-//! MakeXXXClient functions returning their implementations of the ChainClient
-//! interface.
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
-
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 969767b90f..2c5f8627e6 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -27,6 +27,7 @@
#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
+#include <util/check.h>
#include <util/ref.h>
#include <util/system.h>
#include <util/translation.h>
@@ -41,49 +42,25 @@
#include <boost/signals2/signal.hpp>
-class CWallet;
-fs::path GetWalletDir();
-std::vector<fs::path> ListWalletDir();
-std::vector<std::shared_ptr<CWallet>> GetWallets();
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings);
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet);
-
namespace interfaces {
-
namespace {
class NodeImpl : public Node
{
public:
NodeImpl(NodeContext* context) { setContext(context); }
- void initError(const bilingual_str& message) override { InitError(message); }
- bool parseParameters(int argc, const char* const argv[], std::string& error) override
- {
- return gArgs.ParseParameters(argc, argv, error);
- }
- bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); }
- void forceSetArg(const std::string& arg, const std::string& value) override { gArgs.ForceSetArg(arg, value); }
- bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); }
- bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); }
- void selectParams(const std::string& network) override { SelectParams(network); }
- bool initSettings(std::string& error) override { return gArgs.InitSettings(error); }
- uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); }
- uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); }
- std::string getNetwork() override { return Params().NetworkIDString(); }
- void initLogging() override { InitLogging(); }
- void initParameterInteraction() override { InitParameterInteraction(); }
+ void initLogging() override { InitLogging(*Assert(m_context->args)); }
+ void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); }
bilingual_str getWarnings() override { return GetWarnings(true); }
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
- return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
- AppInitLockDataDirectory();
+ return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() &&
+ AppInitLockDataDirectory() && AppInitInterfaces(*m_context);
}
- bool appInitMain() override
+ bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
- m_context->chain = MakeChain(*m_context);
- return AppInitMain(m_context_ref, *m_context);
+ return AppInitMain(m_context_ref, *m_context, tip_info);
}
void appShutdown() override
{
@@ -109,7 +86,6 @@ public:
StopMapPort();
}
}
- void setupServerArgs() override { return SetupServerArgs(*m_context); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(CConnman::NumConnections flags) override
{
@@ -255,36 +231,9 @@ public:
LOCK(::cs_main);
return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
}
- std::string getWalletDir() override
- {
- return GetWalletDir().string();
- }
- std::vector<std::string> listWalletDir() override
- {
- std::vector<std::string> paths;
- for (auto& path : ListWalletDir()) {
- paths.push_back(path.string());
- }
- return paths;
- }
- std::vector<std::unique_ptr<Wallet>> getWallets() override
+ WalletClient& walletClient() override
{
- std::vector<std::unique_ptr<Wallet>> wallets;
- for (auto& client : m_context->chain_clients) {
- auto client_wallets = client->getWallets();
- std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets));
- }
- return wallets;
- }
- std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
- {
- return MakeWallet(LoadWallet(*m_context->chain, name, error, warnings));
- }
- std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) override
- {
- std::shared_ptr<CWallet> wallet;
- status = CreateWallet(*m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
- return MakeWallet(wallet);
+ return *Assert(m_context->wallet_client);
}
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
@@ -302,10 +251,6 @@ public:
{
return MakeHandler(::uiInterface.ShowProgress_connect(fn));
}
- std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
- {
- return HandleLoadWallet(std::move(fn));
- }
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn));
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index cd3cfe487d..5079be038e 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -29,57 +29,31 @@ class RPCTimerInterface;
class UniValue;
class proxyType;
enum class SynchronizationState;
-enum class WalletCreationStatus;
struct CNodeStateStats;
struct NodeContext;
struct bilingual_str;
namespace interfaces {
class Handler;
-class Wallet;
+class WalletClient;
struct BlockTip;
+//! Block and header tip information
+struct BlockAndHeaderTipInfo
+{
+ int block_height;
+ int64_t block_time;
+ int header_height;
+ int64_t header_time;
+ double verification_progress;
+};
+
//! Top-level interface for a bitcoin node (bitcoind process).
class Node
{
public:
virtual ~Node() {}
- //! Send init error.
- virtual void initError(const bilingual_str& message) = 0;
-
- //! Set command line arguments.
- virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
-
- //! Set a command line argument
- virtual void forceSetArg(const std::string& arg, const std::string& value) = 0;
-
- //! Set a command line argument if it doesn't already have a value
- virtual bool softSetArg(const std::string& arg, const std::string& value) = 0;
-
- //! Set a command line boolean argument if it doesn't already have a value
- virtual bool softSetBoolArg(const std::string& arg, bool value) = 0;
-
- //! Load settings from configuration file.
- virtual bool readConfigFiles(std::string& error) = 0;
-
- //! Choose network parameters.
- virtual void selectParams(const std::string& network) = 0;
-
- //! Read and update <datadir>/settings.json file with saved settings. This
- //! needs to be called after selectParams() because the settings file
- //! location is network-specific.
- virtual bool initSettings(std::string& error) = 0;
-
- //! Get the (assumed) blockchain size.
- virtual uint64_t getAssumedBlockchainSize() = 0;
-
- //! Get the (assumed) chain state size.
- virtual uint64_t getAssumedChainStateSize() = 0;
-
- //! Get network name.
- virtual std::string getNetwork() = 0;
-
//! Init logging.
virtual void initLogging() = 0;
@@ -96,7 +70,7 @@ public:
virtual bool baseInitialize() = 0;
//! Start node.
- virtual bool appInitMain() = 0;
+ virtual bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info = nullptr) = 0;
//! Stop node.
virtual void appShutdown() = 0;
@@ -107,9 +81,6 @@ public:
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
- //! Setup arguments
- virtual void setupServerArgs() = 0;
-
//! Map port.
virtual void mapPort(bool use_upnp) = 0;
@@ -201,22 +172,8 @@ public:
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
- //! Return default wallet directory.
- virtual std::string getWalletDir() = 0;
-
- //! Return available wallets in wallet directory.
- virtual std::vector<std::string> listWalletDir() = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
-
- //! Attempts to load a wallet from file or directory.
- //! The loaded wallet is also notified to handlers previously registered
- //! with handleLoadWallet.
- virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
-
- //! Create a wallet from file
- virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) = 0;
+ //! Get wallet client.
+ virtual WalletClient& walletClient() = 0;
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
@@ -238,10 +195,6 @@ public:
using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
- //! Register handler for load wallet messages.
- using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
- virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
-
//! Register handler for number of connections changed messages.
using NotifyNumConnectionsChangedFn = std::function<void(int new_num_connections)>;
virtual std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 7fd24425cf..f68016b557 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -37,6 +37,7 @@ namespace {
//! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{
+ LOCK(wallet.cs_wallet);
WalletTx result;
result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size());
@@ -132,7 +133,11 @@ public:
{
return m_wallet->SignMessage(message, pkhash, str_sig);
}
- bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
+ bool isSpendable(const CTxDestination& dest) override
+ {
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
+ }
bool haveWatchOnly() override
{
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();
@@ -226,8 +231,9 @@ public:
{
LOCK(m_wallet->cs_wallet);
CTransactionRef tx;
+ FeeCalculation fee_calc_out;
if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos,
- fail_reason, coin_control, sign)) {
+ fail_reason, coin_control, fee_calc_out, sign)) {
return {};
}
return tx;
@@ -441,7 +447,7 @@ public:
CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
void remove() override
{
- RemoveWallet(m_wallet);
+ RemoveWallet(m_wallet, false /* load_on_start */);
}
bool isLegacy() override { return m_wallet->IsLegacy(); }
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
@@ -480,15 +486,17 @@ public:
std::shared_ptr<CWallet> m_wallet;
};
-class WalletClientImpl : public ChainClient
+class WalletClientImpl : public WalletClient
{
public:
- WalletClientImpl(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
- : m_wallet_filenames(std::move(wallet_filenames))
+ WalletClientImpl(Chain& chain, ArgsManager& args)
{
m_context.chain = &chain;
m_context.args = &args;
}
+ ~WalletClientImpl() override { UnloadWallets(); }
+
+ //! ChainClient methods
void registerRpcs() override
{
for (const CRPCCommand& command : GetWalletRPCCommands()) {
@@ -498,12 +506,43 @@ public:
m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
}
}
- bool verify() override { return VerifyWallets(*m_context.chain, m_wallet_filenames); }
- bool load() override { return LoadWallets(*m_context.chain, m_wallet_filenames); }
+ bool verify() override { return VerifyWallets(*m_context.chain); }
+ bool load() override { return LoadWallets(*m_context.chain); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler, *Assert(m_context.args)); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
+
+ //! WalletClient methods
+ std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ std::shared_ptr<CWallet> wallet;
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_create = true;
+ options.create_flags = wallet_creation_flags;
+ options.create_passphrase = passphrase;
+ return MakeWallet(CreateWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
+ }
+ std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
+ return MakeWallet(LoadWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
+ }
+ std::string getWalletDir() override
+ {
+ return GetWalletDir().string();
+ }
+ std::vector<std::string> listWalletDir() override
+ {
+ std::vector<std::string> paths;
+ for (auto& path : ListWalletDir()) {
+ paths.push_back(path.string());
+ }
+ return paths;
+ }
std::vector<std::unique_ptr<Wallet>> getWallets() override
{
std::vector<std::unique_ptr<Wallet>> wallets;
@@ -512,7 +551,10 @@ public:
}
return wallets;
}
- ~WalletClientImpl() override { UnloadWallets(); }
+ std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
+ {
+ return HandleLoadWallet(std::move(fn));
+ }
WalletContext m_context;
const std::vector<std::string> m_wallet_filenames;
@@ -524,9 +566,9 @@ public:
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; }
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args)
{
- return MakeUnique<WalletClientImpl>(chain, args, std::move(wallet_filenames));
+ return MakeUnique<WalletClientImpl>(chain, args);
}
} // namespace interfaces
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 3cdadbc72e..6ccfd7fc20 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <amount.h> // For CAmount
+#include <interfaces/chain.h> // For ChainClient
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
@@ -31,6 +32,7 @@ enum class TransactionError;
enum isminetype : unsigned int;
struct CRecipient;
struct PartiallySignedTransaction;
+struct WalletContext;
struct bilingual_str;
typedef uint8_t isminefilter;
@@ -301,6 +303,34 @@ public:
virtual CWallet* wallet() { return nullptr; }
};
+//! Wallet chain client that in addition to having chain client methods for
+//! starting up, shutting down, and registering RPCs, also has additional
+//! methods (called by the GUI) to load and create wallets.
+class WalletClient : public ChainClient
+{
+public:
+ //! Create new wallet.
+ virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Load existing wallet.
+ virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Return default wallet directory.
+ virtual std::string getWalletDir() = 0;
+
+ //! Return available wallets in wallet directory.
+ virtual std::vector<std::string> listWalletDir() = 0;
+
+ //! Return interfaces for accessing wallets (if any).
+ virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+
+ //! Register handler for load wallet messages. This callback is triggered by
+ //! createWallet and loadWallet above, and also triggered when wallets are
+ //! loaded at startup or by RPC.
+ using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
+ virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
+};
+
//! Information about one wallet address.
struct WalletAddress
{
@@ -379,6 +409,10 @@ struct WalletTxOut
//! dummywallet.cpp and throws if the wallet component is not compiled.
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet);
+//! Return implementation of ChainClient interface for a wallet client. This
+//! function will be undefined in builds where ENABLE_WALLET is false.
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args);
+
} // namespace interfaces
#endif // BITCOIN_INTERFACES_WALLET_H
diff --git a/src/key.cpp b/src/key.cpp
index 4ed74a39b1..868a8b9b0e 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -31,7 +31,7 @@ static secp256k1_context* secp256k1_context_sign = nullptr;
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
-static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
const unsigned char *end = seckey + seckeylen;
memset(out32, 0, 32);
/* sequence header */
@@ -88,7 +88,7 @@ static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
+int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
assert(*seckeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
diff --git a/src/limitedmap.h b/src/limitedmap.h
deleted file mode 100644
index 7d66964e36..0000000000
--- a/src/limitedmap.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_LIMITEDMAP_H
-#define BITCOIN_LIMITEDMAP_H
-
-#include <assert.h>
-#include <map>
-
-/** STL-like map container that only keeps the N elements with the highest value. */
-template <typename K, typename V>
-class limitedmap
-{
-public:
- typedef K key_type;
- typedef V mapped_type;
- typedef std::pair<const key_type, mapped_type> value_type;
- typedef typename std::map<K, V>::const_iterator const_iterator;
- typedef typename std::map<K, V>::size_type size_type;
-
-protected:
- std::map<K, V> map;
- typedef typename std::map<K, V>::iterator iterator;
- std::multimap<V, iterator> rmap;
- typedef typename std::multimap<V, iterator>::iterator rmap_iterator;
- size_type nMaxSize;
-
-public:
- explicit limitedmap(size_type nMaxSizeIn)
- {
- assert(nMaxSizeIn > 0);
- nMaxSize = nMaxSizeIn;
- }
- const_iterator begin() const { return map.begin(); }
- const_iterator end() const { return map.end(); }
- size_type size() const { return map.size(); }
- bool empty() const { return map.empty(); }
- const_iterator find(const key_type& k) const { return map.find(k); }
- size_type count(const key_type& k) const { return map.count(k); }
- void insert(const value_type& x)
- {
- std::pair<iterator, bool> ret = map.insert(x);
- if (ret.second) {
- if (map.size() > nMaxSize) {
- map.erase(rmap.begin()->second);
- rmap.erase(rmap.begin());
- }
- rmap.insert(make_pair(x.second, ret.first));
- }
- }
- void erase(const key_type& k)
- {
- iterator itTarget = map.find(k);
- if (itTarget == map.end())
- return;
- std::pair<rmap_iterator, rmap_iterator> itPair = rmap.equal_range(itTarget->second);
- for (rmap_iterator it = itPair.first; it != itPair.second; ++it)
- if (it->second == itTarget) {
- rmap.erase(it);
- map.erase(itTarget);
- return;
- }
- // Shouldn't ever get here
- assert(0);
- }
- void update(const_iterator itIn, const mapped_type& v)
- {
- // Using map::erase() with empty range instead of map::find() to get a non-const iterator,
- // since it is a constant time operation in C++11. For more details, see
- // https://stackoverflow.com/questions/765148/how-to-remove-constness-of-const-iterator
- iterator itTarget = map.erase(itIn, itIn);
-
- if (itTarget == map.end())
- return;
- std::pair<rmap_iterator, rmap_iterator> itPair = rmap.equal_range(itTarget->second);
- for (rmap_iterator it = itPair.first; it != itPair.second; ++it)
- if (it->second == itTarget) {
- rmap.erase(it);
- itTarget->second = v;
- rmap.insert(make_pair(v, itTarget));
- return;
- }
- // Shouldn't ever get here
- assert(0);
- }
- size_type max_size() const { return nMaxSize; }
- size_type max_size(size_type s)
- {
- assert(s > 0);
- while (map.size() > s) {
- map.erase(rmap.begin()->second);
- rmap.erase(rmap.begin());
- }
- nMaxSize = s;
- return nMaxSize;
- }
-};
-
-#endif // BITCOIN_LIMITEDMAP_H
diff --git a/src/miner.h b/src/miner.h
index 096585dfe4..bb7a30b184 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -84,7 +84,7 @@ struct CompareTxIterByAncestorCount {
{
if (a->GetCountWithAncestors() != b->GetCountWithAncestors())
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
- return CTxMemPool::CompareIteratorByHash()(a, b);
+ return CompareIteratorByHash()(a, b);
}
};
diff --git a/src/net.cpp b/src/net.cpp
index 25fa4709b0..e8a27c3530 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -10,7 +10,6 @@
#include <net.h>
#include <banman.h>
-#include <chainparams.h>
#include <clientversion.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
@@ -42,11 +41,18 @@
static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
#endif
+#include <algorithm>
#include <cstdint>
#include <unordered_map>
#include <math.h>
+/** Maximum number of block-relay-only anchor connections */
+static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2;
+static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast<size_t>(MAX_BLOCK_RELAY_ONLY_CONNECTIONS), "MAX_BLOCK_RELAY_ONLY_ANCHORS must not exceed MAX_BLOCK_RELAY_ONLY_CONNECTIONS.");
+/** Anchor IP address database file name */
+const char* const ANCHORS_DATABASE_FILENAME = "anchors.dat";
+
// How often to dump addresses to peers.dat
static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15};
@@ -84,6 +90,11 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
+ /**
+ * Do not call AddLocal() for our special addresses, e.g., for incoming
+ * Tor connections, to prevent gossiping them over the network.
+ */
+ BF_DONT_ADVERTISE = (1U << 2),
};
// The set of sockets cannot be modified while waiting
@@ -94,6 +105,7 @@ const std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
+static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
//
// Global state variables
//
@@ -105,10 +117,10 @@ std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
std::string strSubVersion;
-void CConnman::AddOneShot(const std::string& strDest)
+void CConnman::AddAddrFetch(const std::string& strDest)
{
- LOCK(cs_vOneShots);
- vOneShots.push_back(strDest);
+ LOCK(m_addr_fetches_mutex);
+ m_addr_fetches.push_back(strDest);
}
uint16_t GetListenPort()
@@ -346,7 +358,7 @@ bool CConnman::CheckIncomingNonce(uint64_t nonce)
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce)
+ if (!pnode->fSuccessfullyConnected && !pnode->IsInboundConn() && pnode->GetLocalNonce() == nonce)
return false;
}
return true;
@@ -368,8 +380,10 @@ static CAddress GetBindAddress(SOCKET sock)
return addr_bind;
}
-CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only)
+CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
{
+ assert(conn_type != ConnectionType::INBOUND);
+
if (pszDest == nullptr) {
if (IsLocal(addrConnect))
return nullptr;
@@ -432,7 +446,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (hSocket == INVALID_SOCKET) {
return nullptr;
}
- connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection);
+ connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, conn_type == ConnectionType::MANUAL);
}
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
@@ -459,7 +473,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
+ CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type);
pnode->AddRef();
// We're making a new connection, harvest entropy from the time (and our peer count)
@@ -485,6 +499,26 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet
}
}
+std::string CNode::ConnectionTypeAsString() const
+{
+ switch (m_conn_type) {
+ case ConnectionType::INBOUND:
+ return "inbound";
+ case ConnectionType::MANUAL:
+ return "manual";
+ case ConnectionType::FEELER:
+ return "feeler";
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ return "outbound-full-relay";
+ case ConnectionType::BLOCK_RELAY:
+ return "block-relay-only";
+ case ConnectionType::ADDR_FETCH:
+ return "addr-fetch";
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+}
+
std::string CNode::GetAddrName() const {
LOCK(cs_addrName);
return addrName;
@@ -511,6 +545,11 @@ void CNode::SetAddrLocal(const CService& addrLocalIn) {
}
}
+Network CNode::ConnectedThroughNetwork() const
+{
+ return IsInboundConn() && m_inbound_onion ? NET_ONION : addr.GetNetClass();
+}
+
#undef X
#define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
@@ -519,6 +558,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
X(nServices);
X(addr);
X(addrBind);
+ stats.m_network = GetNetworkName(ConnectedThroughNetwork());
stats.m_mapped_as = addr.GetMappedAS(m_asmap);
if (m_tx_relay != nullptr) {
LOCK(m_tx_relay->cs_filter);
@@ -528,6 +568,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
}
X(nLastSend);
X(nLastRecv);
+ X(nLastTXTime);
+ X(nLastBlockTime);
X(nTimeConnected);
X(nTimeOffset);
stats.addrName = GetAddrName();
@@ -536,8 +578,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
LOCK(cs_SubVer);
X(cleanSubVer);
}
- X(fInbound);
- X(m_manual_connection);
+ stats.fInbound = IsInboundConn();
+ stats.m_manual_connection = IsManualConn();
X(nStartingHeight);
{
LOCK(cs_vSend);
@@ -577,9 +619,21 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
+
+ stats.m_conn_type_string = ConnectionTypeAsString();
}
#undef X
+/**
+ * Receive bytes from the buffer and deserialize them into messages.
+ *
+ * @param[in] pch A pointer to the raw data
+ * @param[in] nBytes Size of the data
+ * @param[out] complete Set True if at least one message has been
+ * deserialized and is ready to be processed
+ * @return True if the peer should stay connected,
+ * False if the peer should be disconnected from.
+ */
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
{
complete = false;
@@ -590,25 +644,35 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
while (nBytes > 0) {
// absorb network data
int handled = m_deserializer->Read(pch, nBytes);
- if (handled < 0) return false;
+ if (handled < 0) {
+ // Serious header problem, disconnect from the peer.
+ return false;
+ }
pch += handled;
nBytes -= handled;
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
- CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), time);
+ uint32_t out_err_raw_size{0};
+ Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
+ if (!result) {
+ // Message deserialization failed. Drop the message but don't disconnect the peer.
+ // store the size of the corrupt message
+ mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err_raw_size;
+ continue;
+ }
//store received bytes per message command
//to prevent a memory DOS, only allow valid commands
- mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.m_command);
+ mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(result->m_command);
if (i == mapRecvBytesPerMsgCmd.end())
i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
assert(i != mapRecvBytesPerMsgCmd.end());
- i->second += msg.m_raw_message_size;
+ i->second += result->m_raw_message_size;
// push the message to the process queue,
- vRecvMsg.push_back(std::move(msg));
+ vRecvMsg.push_back(std::move(*result));
complete = true;
}
@@ -617,32 +681,6 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
return true;
}
-void CNode::SetSendVersion(int nVersionIn)
-{
- // Send version may only be changed in the version message, and
- // only one version message is allowed per session. We can therefore
- // treat this value as const and even atomic as long as it's only used
- // once a version message has been successfully processed. Any attempt to
- // set this twice is an error.
- if (nSendVersion != 0) {
- error("Send version already set for node: %i. Refusing to change from %i to %i", id, nSendVersion, nVersionIn);
- } else {
- nSendVersion = nVersionIn;
- }
-}
-
-int CNode::GetSendVersion() const
-{
- // The send version should always be explicitly set to
- // INIT_PROTO_VERSION rather than using this value until SetSendVersion
- // has been called.
- if (nSendVersion == 0) {
- error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION);
- return INIT_PROTO_VERSION;
- }
- return nSendVersion;
-}
-
int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes)
{
// copy data to temporary parsing buffer
@@ -661,11 +699,19 @@ int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes)
hdrbuf >> hdr;
}
catch (const std::exception&) {
+ 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);
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);
return -1;
}
@@ -700,36 +746,39 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, const std::chrono::microseconds time)
+Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
{
// decompose a single CNetMessage from the TransportDeserializer
- CNetMessage msg(std::move(vRecv));
+ Optional<CNetMessage> msg(std::move(vRecv));
- // store state about valid header, netmagic and checksum
- msg.m_valid_header = hdr.IsValid(message_start);
- msg.m_valid_netmagic = (memcmp(hdr.pchMessageStart, message_start, CMessageHeader::MESSAGE_START_SIZE) == 0);
- uint256 hash = GetMessageHash();
+ // store command string, time, and sizes
+ msg->m_command = hdr.GetCommand();
+ msg->m_time = time;
+ msg->m_message_size = hdr.nMessageSize;
+ msg->m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
- // store command string, payload size
- msg.m_command = hdr.GetCommand();
- msg.m_message_size = hdr.nMessageSize;
- msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
+ uint256 hash = GetMessageHash();
// We just received a message off the wire, harvest entropy from the time (and the message checksum)
RandAddEvent(ReadLE32(hash.begin()));
- msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
- if (!msg.m_valid_checksum) {
- LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
- SanitizeString(msg.m_command), msg.m_message_size,
+ // 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",
+ SanitizeString(msg->m_command), msg->m_message_size,
HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
- HexStr(hdr.pchChecksum));
- }
-
- // store receive time
- msg.m_time = time;
-
- // reset the network deserializer (prepare for the next message)
+ HexStr(hdr.pchChecksum),
+ m_node_id);
+ out_err_raw_size = msg->m_raw_message_size;
+ msg = nullopt;
+ } else if (!hdr.IsCommandValid()) {
+ LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
+ hdr.GetCommand(), msg->m_message_size, m_node_id);
+ out_err_raw_size = msg->m_raw_message_size;
+ msg = nullopt;
+ }
+
+ // Always reset the network deserializer (prepare for the next message)
Reset();
return msg;
}
@@ -812,6 +861,7 @@ struct NodeEvictionCandidate
CAddress addr;
uint64_t nKeyedNetGroup;
bool prefer_evict;
+ bool m_is_local;
};
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -824,6 +874,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
+static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.m_is_local != b.m_is_local) return b.m_is_local;
+ return a.nTimeConnected > b.nTimeConnected;
+}
+
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -845,6 +901,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
return a.nTimeConnected > b.nTimeConnected;
}
+// Pick out the potential block-relay only peers, and sort them by last block time.
+static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
+ if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
+ if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
+ return a.nTimeConnected > b.nTimeConnected;
+}
//! Sort an array by the specified comparator, then erase the last K elements.
template<typename T, typename Comparator>
@@ -872,7 +936,7 @@ bool CConnman::AttemptToEvictConnection()
for (const CNode* node : vNodes) {
if (node->HasPermission(PF_NOBAN))
continue;
- if (!node->fInbound)
+ if (!node->IsInboundConn())
continue;
if (node->fDisconnect)
continue;
@@ -887,7 +951,7 @@ bool CConnman::AttemptToEvictConnection()
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
- node->m_prefer_evict};
+ node->m_prefer_evict, node->addr.IsLocal()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -900,15 +964,34 @@ bool CConnman::AttemptToEvictConnection()
// Protect the 8 nodes with the lowest minimum ping time.
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
- // Protect 4 nodes that most recently sent us transactions.
+ // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
- // Protect 4 nodes that most recently sent us blocks.
+ // Protect up to 8 non-tx-relay peers that have sent us novel blocks.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
+ size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
+
+ // Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
+
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
+ // Reserve half of these protected spots for localhost peers, even if
+ // they're not longest-uptime overall. This helps protect tor peers, which
+ // tend to be otherwise disadvantaged under our eviction criteria.
+ size_t initial_size = vEvictionCandidates.size();
+ size_t total_protect_size = initial_size / 2;
+
+ // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
+ size_t local_erase_size = total_protect_size / 2;
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
+ // Calculate how many we removed, and update our total number of peers that
+ // we want to protect based on uptime accordingly.
+ total_protect_size -= initial_size - vEvictionCandidates.size();
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
if (vEvictionCandidates.empty()) return false;
@@ -983,7 +1066,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (pnode->fInbound) nInbound++;
+ if (pnode->IsInboundConn()) nInbound++;
}
}
@@ -1048,7 +1131,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
}
- CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
+
+ const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
+ CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
pnode->AddRef();
pnode->m_permissionFlags = permissionFlags;
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
@@ -1155,7 +1240,7 @@ void CConnman::InactivityCheck(CNode *pnode)
LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
pnode->fDisconnect = true;
}
- else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
+ else if (nTime - pnode->nLastRecv > (pnode->GetCommonVersion() > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
{
LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
pnode->fDisconnect = true;
@@ -1646,7 +1731,7 @@ void CConnman::ThreadDNSAddressSeed()
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
+ if (pnode->fSuccessfullyConnected && pnode->IsOutboundOrBlockRelayConn()) ++nRelevant;
}
}
if (nRelevant >= 2) {
@@ -1674,7 +1759,7 @@ void CConnman::ThreadDNSAddressSeed()
LogPrintf("Loading addresses from DNS seed %s\n", seed);
if (HaveNameProxy()) {
- AddOneShot(seed);
+ AddAddrFetch(seed);
} else {
std::vector<CNetAddr> vIPs;
std::vector<CAddress> vAdd;
@@ -1696,8 +1781,8 @@ void CConnman::ThreadDNSAddressSeed()
addrman.Add(vAdd, resolveSource);
} else {
// We now avoid directly using results from DNS Seeds which do not support service bit filtering,
- // instead using them as a oneshot to get nodes with our desired service bits.
- AddOneShot(seed);
+ // instead using them as a addrfetch to get nodes with our desired service bits.
+ AddAddrFetch(seed);
}
}
--seeds_right_now;
@@ -1705,17 +1790,6 @@ void CConnman::ThreadDNSAddressSeed()
LogPrintf("%d addresses found from DNS seeds\n", found);
}
-
-
-
-
-
-
-
-
-
-
-
void CConnman::DumpAddresses()
{
int64_t nStart = GetTimeMillis();
@@ -1727,20 +1801,20 @@ void CConnman::DumpAddresses()
addrman.size(), GetTimeMillis() - nStart);
}
-void CConnman::ProcessOneShot()
+void CConnman::ProcessAddrFetch()
{
std::string strDest;
{
- LOCK(cs_vOneShots);
- if (vOneShots.empty())
+ LOCK(m_addr_fetches_mutex);
+ if (m_addr_fetches.empty())
return;
- strDest = vOneShots.front();
- vOneShots.pop_front();
+ strDest = m_addr_fetches.front();
+ m_addr_fetches.pop_front();
}
CAddress addr;
CSemaphoreGrant grant(*semOutbound, true);
if (grant) {
- OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true);
+ OpenNetworkConnection(addr, false, &grant, strDest.c_str(), ConnectionType::ADDR_FETCH);
}
}
@@ -1757,7 +1831,7 @@ void CConnman::SetTryNewOutboundPeer(bool flag)
// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
-// disconnected soon (eg one-shots and feelers)
+// disconnected soon (eg ADDR_FETCH and FEELER)
// 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)
@@ -1767,7 +1841,7 @@ int CConnman::GetExtraOutboundCount()
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) {
+ if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsOutboundOrBlockRelayConn()) {
++nOutbound;
}
}
@@ -1782,11 +1856,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
for (int64_t nLoop = 0;; nLoop++)
{
- ProcessOneShot();
+ ProcessAddrFetch();
for (const std::string& strAddr : connect)
{
CAddress addr(CService(), NODE_NONE);
- OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true);
+ OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), ConnectionType::MANUAL);
for (int i = 0; i < 10 && i < nLoop; i++)
{
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
@@ -1805,7 +1879,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
while (!interruptNet)
{
- ProcessOneShot();
+ ProcessAddrFetch();
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;
@@ -1838,47 +1912,62 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
int nOutboundFullRelay = 0;
int nOutboundBlockRelay = 0;
std::set<std::vector<unsigned char> > setConnected;
+
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (!pnode->fInbound && !pnode->m_manual_connection) {
- // Netgroups for inbound and addnode peers are not excluded because our goal here
- // is to not use multiple of our limited outbound slots on a single netgroup
- // but inbound and addnode peers do not use our outbound slots. Inbound peers
- // also have the added issue that they're attacker controlled and could be used
- // to prevent us from connecting to particular hosts if we used them here.
- setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
- if (pnode->m_tx_relay == nullptr) {
- nOutboundBlockRelay++;
- } else if (!pnode->fFeeler) {
- nOutboundFullRelay++;
- }
- }
+ if (pnode->IsFullOutboundConn()) nOutboundFullRelay++;
+ if (pnode->IsBlockOnlyConn()) nOutboundBlockRelay++;
+
+ // Netgroups for inbound and manual peers are not excluded because our goal here
+ // is to not use multiple of our limited outbound slots on a single netgroup
+ // but inbound and manual peers do not use our outbound slots. Inbound peers
+ // also have the added issue that they could be attacker controlled and used
+ // to prevent us from connecting to particular hosts if we used them here.
+ switch (pnode->m_conn_type) {
+ case ConnectionType::INBOUND:
+ case ConnectionType::MANUAL:
+ break;
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ case ConnectionType::BLOCK_RELAY:
+ case ConnectionType::ADDR_FETCH:
+ case ConnectionType::FEELER:
+ setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
+ } // no default case, so the compiler can warn about missing cases
}
}
- // Feeler Connections
- //
- // Design goals:
- // * Increase the number of connectable addresses in the tried table.
- //
- // Method:
- // * Choose a random address from new and attempt to connect to it if we can connect
- // successfully it is added to tried.
- // * Start attempting feeler connections only after node finishes making outbound
- // connections.
- // * Only make a feeler connection once every few minutes.
- //
+ ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
+ int64_t nTime = GetTimeMicros();
+ bool anchor = false;
bool fFeeler = false;
- if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
- int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
- if (nTime > nNextFeeler) {
- nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
- fFeeler = true;
- } else {
- continue;
- }
+ // Determine what type of connection to open. Opening
+ // BLOCK_RELAY connections to addresses from anchors.dat gets the highest
+ // priority. Then we open OUTBOUND_FULL_RELAY priority until we
+ // meet our full-relay capacity. Then we open BLOCK_RELAY connection
+ // until we hit our block-relay-only peer limit.
+ // GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
+ // try opening an additional OUTBOUND_FULL_RELAY connection. If none of
+ // these conditions are met, check the nNextFeeler timer to decide if
+ // we should open a FEELER.
+
+ if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) {
+ conn_type = ConnectionType::BLOCK_RELAY;
+ anchor = true;
+ } else if (nOutboundFullRelay < m_max_outbound_full_relay) {
+ // OUTBOUND_FULL_RELAY
+ } else if (nOutboundBlockRelay < m_max_outbound_block_relay) {
+ conn_type = ConnectionType::BLOCK_RELAY;
+ } else if (GetTryNewOutboundPeer()) {
+ // OUTBOUND_FULL_RELAY
+ } else if (nTime > nNextFeeler) {
+ nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ conn_type = ConnectionType::FEELER;
+ fFeeler = true;
+ } else {
+ // skip to next iteration of while loop
+ continue;
}
addrman.ResolveCollisions();
@@ -1887,6 +1976,24 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
int nTries = 0;
while (!interruptNet)
{
+ if (anchor && !m_anchors.empty()) {
+ const CAddress addr = m_anchors.back();
+ m_anchors.pop_back();
+ if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
+ !HasAllDesirableServiceFlags(addr.nServices) ||
+ setConnected.count(addr.GetGroup(addrman.m_asmap))) continue;
+ addrConnect = addr;
+ LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
+ break;
+ }
+
+ // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
+ // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
+ // already-connected network ranges, ...) before trying new addrman addresses.
+ nTries++;
+ if (nTries > 100)
+ break;
+
CAddrInfo addr = addrman.SelectTriedCollision();
// SelectTriedCollision returns an invalid address if it is empty.
@@ -1904,13 +2011,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
break;
}
- // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
- // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
- // already-connected network ranges, ...) before trying new addrman addresses.
- nTries++;
- if (nTries > 100)
- break;
-
if (!IsReachable(addr))
continue;
@@ -1945,16 +2045,22 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
}
- // Open this connection as block-relay-only if we're already at our
- // full-relay capacity, but not yet at our block-relay peer limit.
- // (It should not be possible for fFeeler to be set if we're not
- // also at our block-relay peer limit, but check against that as
- // well for sanity.)
- bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay && !fFeeler && nOutboundFullRelay >= m_max_outbound_full_relay;
+ OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, conn_type);
+ }
+ }
+}
- OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler, false, block_relay_only);
+std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
+{
+ std::vector<CAddress> ret;
+ LOCK(cs_vNodes);
+ for (const CNode* pnode : vNodes) {
+ if (pnode->IsBlockOnlyConn()) {
+ ret.push_back(pnode->addr);
}
}
+
+ return ret;
}
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
@@ -1976,11 +2082,11 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
if (pnode->addr.IsValid()) {
- mapConnected[pnode->addr] = pnode->fInbound;
+ mapConnected[pnode->addr] = pnode->IsInboundConn();
}
std::string addrName = pnode->GetAddrName();
if (!addrName.empty()) {
- mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr));
+ mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->IsInboundConn(), static_cast<const CService&>(pnode->addr));
}
}
}
@@ -2027,7 +2133,7 @@ void CConnman::ThreadOpenAddedConnections()
}
tried = true;
CAddress addr(CService(), NODE_NONE);
- OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true);
+ OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), ConnectionType::MANUAL);
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;
}
@@ -2039,8 +2145,10 @@ void CConnman::ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool block_relay_only)
+void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
{
+ assert(conn_type != ConnectionType::INBOUND);
+
//
// Initiate outbound network connection
//
@@ -2058,18 +2166,12 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
} else if (FindNode(std::string(pszDest)))
return;
- CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
+ CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type);
if (!pnode)
return;
if (grantOutbound)
grantOutbound->MoveTo(pnode->grantOutbound);
- if (fOneShot)
- pnode->fOneShot = true;
- if (fFeeler)
- pnode->fFeeler = true;
- if (manual_connection)
- pnode->m_manual_connection = true;
m_msgproc->InitializeNode(pnode);
{
@@ -2127,11 +2229,6 @@ void CConnman::ThreadMessageHandler()
}
}
-
-
-
-
-
bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions)
{
int nOne = 1;
@@ -2193,10 +2290,6 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
}
vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
-
- if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
- AddLocal(addrBind, LOCAL_BIND);
-
return true;
}
@@ -2290,10 +2383,18 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags
}
return false;
}
+
+ if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !(permissions & PF_NOBAN)) {
+ AddLocal(addr, LOCAL_BIND);
+ }
+
return true;
}
-bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
+bool CConnman::InitBinds(
+ const std::vector<CService>& binds,
+ const std::vector<NetWhitebindPermissions>& whiteBinds,
+ const std::vector<CService>& onion_binds)
{
bool fBound = false;
for (const auto& addrBind : binds) {
@@ -2304,11 +2405,16 @@ bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<N
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
- inaddr_any.s_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);
}
+
+ for (const auto& addr_bind : onion_binds) {
+ fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::PF_NONE);
+ }
+
return fBound;
}
@@ -2327,7 +2433,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
nMaxOutboundCycleStartTime = 0;
}
- if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) {
+ if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds, connOptions.onion_binds)) {
if (clientInterface) {
clientInterface->ThreadSafeMessageBox(
_("Failed to listen on any port. Use -listen=0 if you want this."),
@@ -2337,7 +2443,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
for (const auto& strDest : connOptions.vSeedNodes) {
- AddOneShot(strDest);
+ AddAddrFetch(strDest);
}
if (clientInterface) {
@@ -2356,6 +2462,15 @@ 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);
+ 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);
fAddressesInitialized = true;
@@ -2390,7 +2505,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
else
threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()> >, "dnsseed", std::function<void()>(std::bind(&CConnman::ThreadDNSAddressSeed, this)));
- // Initiate outbound connections from -addnode
+ // Initiate manual connections
threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));
if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
@@ -2471,6 +2586,15 @@ void CConnman::StopNodes()
if (fAddressesInitialized) {
DumpAddresses();
fAddressesInitialized = false;
+
+ if (m_use_addrman_outgoing) {
+ // Anchor connections are only dumped during clean shutdown.
+ std::vector<CAddress> anchors_to_dump = GetCurrentBlockRelayOnlyConns();
+ 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);
+ }
}
// Close sockets
@@ -2523,14 +2647,14 @@ void CConnman::MarkAddressGood(const CAddress& addr)
addrman.Good(addr);
}
-void CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
+bool CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
{
- addrman.Add(vAddr, addrFrom, nTimePenalty);
+ return addrman.Add(vAddr, addrFrom, nTimePenalty);
}
-std::vector<CAddress> CConnman::GetAddresses()
+std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct)
{
- std::vector<CAddress> addresses = addrman.GetAddr();
+ std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct);
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);}),
@@ -2539,15 +2663,47 @@ std::vector<CAddress> CConnman::GetAddresses()
return addresses;
}
-std::vector<CAddress> CConnman::GetAddresses(Network requestor_network)
+std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
+ SOCKET socket;
+ WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
+ auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
+ uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
+ .Write(requestor.addr.GetNetwork())
+ .Write(local_socket_bytes.data(), local_socket_bytes.size())
+ .Finalize();
const auto current_time = GetTime<std::chrono::microseconds>();
- if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() ||
- m_addr_response_caches[requestor_network].m_update_addr_response < current_time) {
- m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses();
- m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
+ 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);
+ // Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
+ // and the usefulness of ADDR responses to honest users.
+ //
+ // Longer cache lifetime makes it more difficult for an attacker to scrape
+ // enough AddrMan data to maliciously infer something useful.
+ // By the time an attacker scraped enough AddrMan records, most of
+ // the records should be old enough to not leak topology info by
+ // e.g. analyzing real-time changes in timestamps.
+ //
+ // It takes only several hundred requests to scrape everything from an AddrMan containing 100,000 nodes,
+ // so ~24 hours of cache lifetime indeed makes the data less inferable by the time
+ // most of it could be scraped (considering that timestamps are updated via
+ // ADDR self-announcements and when nodes communicate).
+ // We also should be robust to those attacks which may not require scraping *full* victim's AddrMan
+ // (because even several timestamps of the same handful of nodes may leak privacy).
+ //
+ // On the other hand, longer cache lifetime makes ADDR responses
+ // outdated and less useful for an honest requestor, e.g. if most nodes
+ // in the ADDR response are no longer active.
+ //
+ // However, the churn in the network is known to be rather low. Since we consider
+ // nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days,
+ // max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference
+ // in terms of the freshness of the response.
+ cache_entry.m_cache_entry_expiration = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
}
- return m_addr_response_caches[requestor_network].m_addrs_response_cache;
+ return cache_entry.m_addrs_response_cache;
}
bool CConnman::AddNode(const std::string& strNode)
@@ -2581,7 +2737,7 @@ size_t CConnman::GetNodeCount(NumConnections flags)
int nNum = 0;
for (const auto& pnode : vNodes) {
- if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
+ if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
nNum++;
}
}
@@ -2765,28 +2921,32 @@ int CConnman::GetBestHeight() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
-CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, bool fInboundIn, bool block_relay_only)
+CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion)
: nTimeConnected(GetSystemTimeInSeconds()),
addr(addrIn),
addrBind(addrBindIn),
- fInbound(fInboundIn),
nKeyedNetGroup(nKeyedNetGroupIn),
// Don't relay addr messages to peers that we connect to as block-relay-only
// peers (to prevent adversaries from inferring these links from addr
// traffic).
- m_addr_known{block_relay_only ? nullptr : MakeUnique<CRollingBloomFilter>(5000, 0.001)},
id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
+ m_conn_type(conn_type_in),
nLocalServices(nLocalServicesIn),
- nMyStartingHeight(nMyStartingHeightIn)
+ nMyStartingHeight(nMyStartingHeightIn),
+ m_inbound_onion(inbound_onion)
{
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
hashContinue = uint256();
- if (!block_relay_only) {
+ if (conn_type_in != ConnectionType::BLOCK_RELAY) {
m_tx_relay = MakeUnique<TxRelay>();
}
+ if (RelayAddrsWithConn()) {
+ m_addr_known = MakeUnique<CRollingBloomFilter>(5000, 0.001);
+ }
+
for (const std::string &msg : getAllNetMessageTypes())
mapRecvBytesPerMsgCmd[msg] = 0;
mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
@@ -2797,7 +2957,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer());
}
diff --git a/src/net.h b/src/net.h
index 1c558ee810..2652d82ea0 100644
--- a/src/net.h
+++ b/src/net.h
@@ -10,12 +10,13 @@
#include <addrman.h>
#include <amount.h>
#include <bloom.h>
+#include <chainparams.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <hash.h>
-#include <limitedmap.h>
-#include <netaddress.h>
#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -51,11 +52,8 @@ static const bool DEFAULT_WHITELISTFORCERELAY = false;
static const int TIMEOUT_INTERVAL = 20 * 60;
/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
static const int FEELER_INTERVAL = 120;
-/** The maximum number of new addresses to accumulate before announcing. */
-static const unsigned int MAX_ADDR_TO_SEND = 1000;
-// TODO: remove ADDRMAN_GETADDR_MAX and let the caller specify this limit with MAX_ADDR_TO_SEND.
-static_assert(MAX_ADDR_TO_SEND == ADDRMAN_GETADDR_MAX,
- "Max allowed ADDR message size should be equal to the max number of records returned from AddrMan.");
+/** 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 */
@@ -117,6 +115,73 @@ struct CSerializedNetMsg
std::string m_type;
};
+/** Different types of connections to a peer. This enum encapsulates the
+ * information we have available at the time of opening or accepting the
+ * connection. Aside from INBOUND, all types are initiated by us.
+ *
+ * If adding or removing types, please update CONNECTION_TYPE_DOC in
+ * src/rpc/net.cpp. */
+enum class ConnectionType {
+ /**
+ * Inbound connections are those initiated by a peer. This is the only
+ * property we know at the time of connection, until P2P messages are
+ * exchanged.
+ */
+ INBOUND,
+
+ /**
+ * These are the default connections that we use to connect with the
+ * network. There is no restriction on what is relayed- by default we relay
+ * blocks, addresses & transactions. We automatically attempt to open
+ * MAX_OUTBOUND_FULL_RELAY_CONNECTIONS using addresses from our AddrMan.
+ */
+ OUTBOUND_FULL_RELAY,
+
+
+ /**
+ * We open manual connections to addresses that users explicitly inputted
+ * via the addnode RPC, or the -connect command line argument. Even if a
+ * manual connection is misbehaving, we do not automatically disconnect or
+ * add it to our discouragement filter.
+ */
+ MANUAL,
+
+ /**
+ * Feeler connections are short-lived connections made to check that a node
+ * is alive. They can be useful for:
+ * - test-before-evict: if one of the peers is considered for eviction from
+ * our AddrMan because another peer is mapped to the same slot in the tried table,
+ * evict only if this longer-known peer is offline.
+ * - move node addresses from New to Tried table, so that we have more
+ * connectable addresses in our AddrMan.
+ * Note that in the literature ("Eclipse Attacks on Bitcoin’s Peer-to-Peer Network")
+ * only the latter feature is referred to as "feeler connections",
+ * although in our codebase feeler connections encompass test-before-evict as well.
+ * We make these connections approximately every FEELER_INTERVAL:
+ * first we resolve previously found collisions if they exist (test-before-evict),
+ * otherwise connect to a node from the new table.
+ */
+ FEELER,
+
+ /**
+ * We use block-relay-only connections to help prevent against partition
+ * attacks. By not relaying transactions or addresses, these connections
+ * are harder to detect by a third party, thus helping obfuscate the
+ * network topology. We automatically attempt to open
+ * MAX_BLOCK_RELAY_ONLY_ANCHORS using addresses from our anchors.dat. Then
+ * addresses from our AddrMan if MAX_BLOCK_RELAY_ONLY_CONNECTIONS
+ * isn't reached yet.
+ */
+ BLOCK_RELAY,
+
+ /**
+ * AddrFetch connections are short lived connections used to solicit
+ * addresses from peers. These are initiated to addresses submitted via the
+ * -seednode command line argument, or under certain conditions when the
+ * AddrMan is empty.
+ */
+ ADDR_FETCH,
+};
class NetEventsInterface;
class CConnman
@@ -151,6 +216,7 @@ public:
std::vector<NetWhitelistPermissions> vWhitelistedRange;
std::vector<NetWhitebindPermissions> vWhiteBinds;
std::vector<CService> vBinds;
+ std::vector<CService> onion_binds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -183,6 +249,7 @@ public:
LOCK(cs_vAddedNodes);
vAddedNodes = connOptions.m_added_nodes;
}
+ m_onion_binds = connOptions.onion_binds;
}
CConnman(uint64_t seed0, uint64_t seed1, bool network_active = true);
@@ -201,15 +268,15 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false, bool block_relay_only = false);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
- template<typename Callable>
- void ForEachNode(Callable&& func)
+ using NodeFn = std::function<void(CNode*)>;
+ void ForEachNode(const NodeFn& func)
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
@@ -218,8 +285,7 @@ public:
}
};
- template<typename Callable>
- void ForEachNode(Callable&& func) const
+ void ForEachNode(const NodeFn& func) const
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
@@ -253,15 +319,15 @@ public:
// Addrman functions
void SetServices(const CService &addr, ServiceFlags nServices);
void MarkAddressGood(const CAddress& addr);
- void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
- std::vector<CAddress> GetAddresses();
+ bool AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
+ std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct);
/**
* Cache is used to minimize topology leaks, so it should
* be used for all non-trusted calls, for example, p2p.
* A non-malicious call (from RPC or a peer with addr permission) should
* call the function without a parameter to avoid using the cache.
*/
- std::vector<CAddress> GetAddresses(Network requestor_network);
+ std::vector<CAddress> GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct);
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
@@ -349,10 +415,14 @@ private:
bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
+ bool InitBinds(
+ const std::vector<CService>& binds,
+ const std::vector<NetWhitebindPermissions>& whiteBinds,
+ const std::vector<CService>& onion_binds);
+
void ThreadOpenAddedConnections();
- void AddOneShot(const std::string& strDest);
- void ProcessOneShot();
+ void AddAddrFetch(const std::string& strDest);
+ void ProcessAddrFetch();
void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -373,7 +443,7 @@ private:
CNode* FindNode(const CService& addr);
bool AttemptToEvictConnection();
- CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only);
+ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void DeleteNode(CNode* pnode);
@@ -387,6 +457,11 @@ private:
void RecordBytesRecv(uint64_t bytes);
void RecordBytesSent(uint64_t bytes);
+ /**
+ * Return vector of current BLOCK_RELAY peers.
+ */
+ std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
+
// Whether the node should be passed out in ForEach* callbacks
static bool NodeFullyConnected(const CNode* pnode);
@@ -416,8 +491,8 @@ private:
std::atomic<bool> fNetworkActive{true};
bool fAddressesInitialized{false};
CAddrMan addrman;
- std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots);
- RecursiveMutex cs_vOneShots;
+ 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;
std::vector<CNode*> vNodes GUARDED_BY(cs_vNodes);
@@ -434,20 +509,24 @@ private:
*/
struct CachedAddrResponse {
std::vector<CAddress> m_addrs_response_cache;
- std::chrono::microseconds m_update_addr_response{0};
+ std::chrono::microseconds m_cache_entry_expiration{0};
};
/**
* Addr responses stored in different caches
- * per network prevent cross-network node identification.
+ * per (network, local socket) prevent cross-network node identification.
* If a node for example is multi-homed under Tor and IPv6,
* a single cache (or no cache at all) would let an attacker
* to easily detect that it is the same node by comparing responses.
- * The used memory equals to 1000 CAddress records (or around 32 bytes) per
+ * Indexing by local socket prevents leakage when a node has multiple
+ * listening addresses on the same network.
+ *
+ * The used memory equals to 1000 CAddress records (or around 40 bytes) per
* distinct Network (up to 5) we have/had an inbound peer from,
- * resulting in at most ~160 KB.
+ * resulting in at most ~196 KB. Every separate local socket may
+ * add up to ~196 KB extra.
*/
- std::map<Network, CachedAddrResponse> m_addr_response_caches;
+ std::map<uint64_t, CachedAddrResponse> m_addr_response_caches;
/**
* Services this instance offers.
@@ -484,6 +563,12 @@ private:
/** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
BanMan* m_banman;
+ /**
+ * Addresses that were saved during the previous clean shutdown. We'll
+ * attempt to make block-relay-only connections to them.
+ */
+ std::vector<CAddress> m_anchors;
+
/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;
@@ -509,6 +594,12 @@ private:
std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+ /**
+ * A vector of -bind=<address>:<port>=onion arguments each of which is
+ * an address and port that are designated for incoming Tor connections.
+ */
+ std::vector<CService> m_onion_binds;
+
friend struct CConnmanTest;
friend struct ConnmanTestMsg;
};
@@ -518,21 +609,6 @@ void InterruptMapPort();
void StopMapPort();
uint16_t GetListenPort();
-struct CombinerAll
-{
- typedef bool result_type;
-
- template<typename I>
- bool operator()(I first, I last) const
- {
- while (first != last) {
- if (!(*first)) return false;
- ++first;
- }
- return true;
- }
-};
-
/**
* Interface for message handling
*/
@@ -611,6 +687,8 @@ public:
bool fRelayTxes;
int64_t nLastSend;
int64_t nLastRecv;
+ int64_t nLastTXTime;
+ int64_t nLastBlockTime;
int64_t nTimeConnected;
int64_t nTimeOffset;
std::string addrName;
@@ -635,7 +713,10 @@ public:
CAddress addr;
// Bind address of our side of the connection
CAddress addrBind;
+ // Name of the network the peer connected through
+ std::string m_network;
uint32_t m_mapped_as;
+ std::string m_conn_type_string;
};
@@ -648,11 +729,8 @@ class CNetMessage {
public:
CDataStream m_recv; //!< received message data
std::chrono::microseconds m_time{0}; //!< time of message receipt
- bool m_valid_netmagic = false;
- bool m_valid_header = false;
- bool m_valid_checksum = false;
- uint32_t m_message_size{0}; //!< size of the payload
- uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
+ uint32_t m_message_size{0}; //!< size of the payload
+ uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
std::string m_command;
CNetMessage(CDataStream&& recv_in) : m_recv(std::move(recv_in)) {}
@@ -676,13 +754,15 @@ public:
// read and deserialize data
virtual int Read(const char *data, unsigned int bytes) = 0;
// decomposes a message from the context
- virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) = 0;
+ virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
virtual ~TransportDeserializer() {}
};
class V1TransportDeserializer final : public TransportDeserializer
{
private:
+ const CChainParams& m_chain_params;
+ const NodeId m_node_id; // Only for logging
mutable CHash256 hasher;
mutable uint256 data_hash;
bool in_data; // parsing header (false) or data (true)
@@ -708,8 +788,12 @@ private:
}
public:
-
- V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
+ V1TransportDeserializer(const CChainParams& chain_params, const NodeId node_id, int nTypeIn, int nVersionIn)
+ : m_chain_params(chain_params),
+ m_node_id(node_id),
+ hdrbuf(nTypeIn, nVersionIn),
+ vRecv(nTypeIn, nVersionIn)
+ {
Reset();
}
@@ -729,7 +813,7 @@ public:
if (ret < 0) Reset();
return ret;
}
- CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) override;
+ Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -773,9 +857,7 @@ public:
RecursiveMutex cs_sendProcessing;
- std::deque<CInv> vRecvGetData;
uint64_t nRecvBytes GUARDED_BY(cs_vRecv){0};
- std::atomic<int> nRecvVersion{INIT_PROTO_VERSION};
std::atomic<int64_t> nLastSend{0};
std::atomic<int64_t> nLastRecv{0};
@@ -798,12 +880,13 @@ public:
}
// This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
bool m_legacyWhitelisted{false};
- bool fFeeler{false}; // If true this node is being used as a short lived feeler.
- bool fOneShot{false};
- bool m_manual_connection{false};
bool fClient{false}; // set by version message
bool m_limited_node{false}; //after BIP159, set by version message
- const bool fInbound;
+ /**
+ * 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};
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
@@ -816,6 +899,78 @@ public:
std::atomic_bool fPauseRecv{false};
std::atomic_bool fPauseSend{false};
+ bool IsOutboundOrBlockRelayConn() const {
+ switch (m_conn_type) {
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ case ConnectionType::BLOCK_RELAY:
+ return true;
+ case ConnectionType::INBOUND:
+ case ConnectionType::MANUAL:
+ case ConnectionType::ADDR_FETCH:
+ case ConnectionType::FEELER:
+ return false;
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+ }
+
+ bool IsFullOutboundConn() const {
+ return m_conn_type == ConnectionType::OUTBOUND_FULL_RELAY;
+ }
+
+ bool IsManualConn() const {
+ return m_conn_type == ConnectionType::MANUAL;
+ }
+
+ bool IsBlockOnlyConn() const {
+ return m_conn_type == ConnectionType::BLOCK_RELAY;
+ }
+
+ bool IsFeelerConn() const {
+ return m_conn_type == ConnectionType::FEELER;
+ }
+
+ bool IsAddrFetchConn() const {
+ return m_conn_type == ConnectionType::ADDR_FETCH;
+ }
+
+ bool IsInboundConn() const {
+ return m_conn_type == ConnectionType::INBOUND;
+ }
+
+ /* Whether we send addr messages over this connection */
+ bool RelayAddrsWithConn() const
+ {
+ return m_conn_type != ConnectionType::BLOCK_RELAY;
+ }
+
+ bool ExpectServicesFromConn() const {
+ switch (m_conn_type) {
+ case ConnectionType::INBOUND:
+ case ConnectionType::MANUAL:
+ case ConnectionType::FEELER:
+ return false;
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ case ConnectionType::BLOCK_RELAY:
+ case ConnectionType::ADDR_FETCH:
+ return true;
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+ }
+
+ /**
+ * Get network the peer connected through.
+ *
+ * Returns Network::NET_ONION for *inbound* onion connections,
+ * and CNetAddr::GetNetClass() otherwise. The latter cannot be used directly
+ * because it doesn't detect the former, and it's not the responsibility of
+ * the CNetAddr class to know the actual network a peer is connected through.
+ *
+ * @return network the peer connected through.
+ */
+ Network ConnectedThroughNetwork() const;
+
protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
@@ -826,13 +981,11 @@ public:
// flood relay
std::vector<CAddress> vAddrToSend;
- const std::unique_ptr<CRollingBloomFilter> m_addr_known;
+ std::unique_ptr<CRollingBloomFilter> m_addr_known{nullptr};
bool fGetAddr{false};
std::chrono::microseconds m_next_addr_send GUARDED_BY(cs_sendProcessing){0};
std::chrono::microseconds m_next_local_addr_send GUARDED_BY(cs_sendProcessing){0};
- bool IsAddrRelayPeer() const { return m_addr_known != nullptr; }
-
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
@@ -872,8 +1025,17 @@ public:
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
- // Block and TXN accept times
+ /** UNIX epoch time of the last block received from this peer that we had
+ * not yet seen (e.g. not already received from another peer), that passed
+ * preliminary validity checks and was saved to disk, even if we don't
+ * connect the block or it eventually fails connection. Used as an inbound
+ * peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastBlockTime{0};
+
+ /** UNIX epoch time of the last transaction received from this peer that we
+ * had not yet seen (e.g. not already received from another peer) and that
+ * was accepted into our mempool. Used as an inbound peer eviction criterium
+ * in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
// Ping time measurement:
@@ -888,9 +1050,7 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued{false};
- std::set<uint256> orphan_work_set;
-
- CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool block_relay_only = false);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -898,6 +1058,8 @@ public:
private:
const NodeId id;
const uint64_t nLocalHostNonce;
+ const ConnectionType m_conn_type;
+ std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION};
//! Services offered to this peer.
//!
@@ -917,7 +1079,6 @@ private:
const ServiceFlags nLocalServices;
const int nMyStartingHeight;
- int nSendVersion{0};
NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
@@ -927,6 +1088,10 @@ private:
// Our address, as reported by the peer
CService addrLocal GUARDED_BY(cs_addrLocal);
mutable RecursiveMutex cs_addrLocal;
+
+ //! Whether this peer connected via our Tor onion service.
+ const bool m_inbound_onion{false};
+
public:
NodeId GetId() const {
@@ -949,16 +1114,14 @@ public:
bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete);
- void SetRecvVersion(int nVersionIn)
+ void SetCommonVersion(int greatest_common_version)
{
- nRecvVersion = nVersionIn;
+ m_greatest_common_version = greatest_common_version;
}
- int GetRecvVersion() const
+ int GetCommonVersion() const
{
- return nRecvVersion;
+ return m_greatest_common_version;
}
- void SetSendVersion(int nVersionIn);
- int GetSendVersion() const;
CService GetAddrLocal() const;
//! May not be called more than once
@@ -985,11 +1148,16 @@ public:
void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
{
+ // Whether the peer supports the address in `_addr`. For example,
+ // nodes that do not implement BIP155 cannot receive Tor v3 addresses
+ // because they require ADDRv2 (BIP155) encoding.
+ const bool addr_format_supported = m_wants_addrv2 || _addr.IsAddrV1Compatible();
+
// 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())) {
+ if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && addr_format_supported) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
@@ -1028,6 +1196,8 @@ public:
std::string GetAddrName() const;
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);
+
+ std::string ConnectionTypeAsString() const;
};
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
index 53648deb40..d40fdfb113 100644
--- a/src/net_permissions.cpp
+++ b/src/net_permissions.cpp
@@ -12,7 +12,7 @@ const std::vector<std::string> NET_PERMISSIONS_DOC{
"bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
"noban (do not ban for misbehavior; implies download)",
"forcerelay (relay transactions that are already in the mempool; implies relay)",
- "relay (relay even in -blocksonly mode)",
+ "relay (relay even in -blocksonly mode, and unlimited transaction announcements)",
"mempool (allow requesting BIP35 mempool contents)",
"download (allow getheaders during IBD, no disconnect after maxuploadtarget limit)",
"addr (responses to GETADDR avoid hitting the cache and contain random records with the most up-to-date info)"
diff --git a/src/net_permissions.h b/src/net_permissions.h
index 5b68f635a7..bba0ea1695 100644
--- a/src/net_permissions.h
+++ b/src/net_permissions.h
@@ -19,6 +19,7 @@ enum NetPermissionFlags {
// Can query bloomfilter even if -peerbloomfilters is false
PF_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),
// Always relay transactions from this peer, even if already in mempool
// Keep parameter interaction: forcerelay implies relay
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 7c2d7335a0..94d4052fa1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -23,6 +23,7 @@
#include <random.h>
#include <reverse_iterator.h>
#include <scheduler.h>
+#include <streams.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <util/check.h> // For NDEBUG compile time check
@@ -71,22 +72,22 @@ static constexpr std::chrono::minutes PING_INTERVAL{2};
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of entries in an 'inv' protocol message */
static const unsigned int MAX_INV_SZ = 50000;
-/** Maximum number of in-flight transactions from a peer */
-static constexpr int32_t MAX_PEER_TX_IN_FLIGHT = 100;
-/** Maximum number of announced transactions from a peer */
-static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 2 * MAX_INV_SZ;
-/** How many microseconds to delay requesting transactions via txids, if we have wtxid-relaying peers */
-static constexpr std::chrono::microseconds TXID_RELAY_DELAY{std::chrono::seconds{2}};
-/** How many microseconds to delay requesting transactions from inbound peers */
-static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}};
+/** Maximum number of in-flight transaction requests from a peer. It is not a hard limit, but the threshold at which
+ * point the OVERLOADED_PEER_TX_DELAY kicks in. */
+static constexpr int32_t MAX_PEER_TX_REQUEST_IN_FLIGHT = 100;
+/** Maximum number of transactions to consider for requesting, per peer. It provides a reasonable DoS limit to
+ * per-peer memory usage spent on announcements, while covering peers continuously sending INVs at the maximum
+ * rate (by our own policy, see INVENTORY_BROADCAST_PER_SECOND) for several minutes, while not receiving
+ * the actual transaction (from any peer) in response to requests for them. */
+static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS = 5000;
+/** How long to delay requesting transactions via txids, if we have wtxid-relaying peers */
+static constexpr auto TXID_RELAY_DELAY = std::chrono::seconds{2};
+/** How long to delay requesting transactions from non-preferred peers */
+static constexpr auto NONPREF_PEER_TX_DELAY = std::chrono::seconds{2};
+/** How long to delay requesting transactions from overloaded peers (see MAX_PEER_TX_REQUEST_IN_FLIGHT). */
+static constexpr auto OVERLOADED_PEER_TX_DELAY = std::chrono::seconds{2};
/** How long to wait (in microseconds) before downloading a transaction from an additional peer */
static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}};
-/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */
-static constexpr std::chrono::microseconds MAX_GETDATA_RANDOM_DELAY{std::chrono::seconds{2}};
-/** How long to wait (in microseconds) before expiring an in-flight getdata request to a peer */
-static constexpr std::chrono::microseconds TX_EXPIRY_INTERVAL{GETDATA_TX_INTERVAL * 10};
-static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY,
-"To preserve security, MAX_GETDATA_RANDOM_DELAY should not exceed INBOUND_PEER_DELAY");
/** Limit to avoid sending big packets. Not used in processing incoming GETDATA for compatibility */
static const unsigned int MAX_GETDATA_SZ = 1000;
/** Number of blocks that can be requested at any given time from a single peer. */
@@ -143,6 +144,8 @@ static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
static constexpr uint32_t MAX_GETCFILTERS_SIZE = 1000;
/** Maximum number of cf hashes that may be requested with one getcfheaders. See BIP 157. */
static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000;
+/** the maximum percentage of addresses from our addrman to return in response to a getaddr message. */
+static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -151,8 +154,14 @@ struct COrphanTx {
int64_t nTimeExpire;
size_t list_pos;
};
+
+/** Guards orphan transactions and extra txs for compact blocks */
RecursiveMutex g_cs_orphans;
+/** Map from txid to orphan transaction record. Limited by
+ * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
+/** Index from wtxid into the mapOrphanTransactions to lookup orphan
+ * transactions using their witness ids. */
std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans);
void EraseOrphansFor(NodeId peer);
@@ -256,12 +265,19 @@ namespace {
return &(*a) < &(*b);
}
};
- std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
- std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); //! For random eviction
+ /** Index from the parents' COutPoint into the mapOrphanTransactions. Used
+ * to remove orphan transactions from the mapOrphanTransactions */
+ std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+ /** Orphan transactions in vector for quick random eviction */
+ std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans);
- static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ /** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
+ * The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
+ * these are kept in a ring buffer */
static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
+ /** Offset into vExtraTxnForCompact to insert the next tx */
+ static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
} // namespace
namespace {
@@ -276,12 +292,6 @@ struct CNodeState {
const CService address;
//! Whether we have a fully established connection.
bool fCurrentlyConnected;
- //! Accumulated misbehaviour score for this peer.
- int nMisbehavior;
- //! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
- bool m_should_discourage;
- //! String name of this peer (debugging/logging purposes).
- const std::string name;
//! The best known block we know this peer has announced.
const CBlockIndex *pindexBestKnownBlock;
//! The hash of the last unknown block this peer has announced.
@@ -325,10 +335,17 @@ struct CNodeState {
*/
bool fSupportsDesiredCmpctVersion;
- /** State used to enforce CHAIN_SYNC_TIMEOUT
- * Only in effect for outbound, non-manual, full-relay connections, with
- * m_protect == false
- * Algorithm: if a peer's best known block has less work than our tip,
+ /** State used to enforce CHAIN_SYNC_TIMEOUT and EXTRA_PEER_CHECK_INTERVAL logic.
+ *
+ * Both are only in effect for outbound, non-manual, non-protected connections.
+ * Any peer protected (m_protect = true) is not chosen for eviction. A peer is
+ * marked as protected if all of these are true:
+ * - its connection type is IsBlockOnlyConn() == false
+ * - it gave us a valid connecting header
+ * - we haven't reached MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT yet
+ * - it has a better chain than we have
+ *
+ * CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip,
* set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
* - If at timeout their best known block now has more work than our tip
* when the timeout was set, then either reset the timeout or clear it
@@ -338,6 +355,9 @@ struct CNodeState {
* and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future.
* If their best known block is still behind when that new timeout is
* reached, disconnect.
+ *
+ * EXTRA_PEER_CHECK_INTERVAL: after each interval, if we have too many outbound peers,
+ * drop the outbound one that least recently announced us a new block.
*/
struct ChainSyncTimeoutState {
//! A timeout used for checking whether our peer has sufficiently synced
@@ -355,69 +375,6 @@ struct CNodeState {
//! Time of last new block announcement
int64_t m_last_block_announcement;
- /*
- * State associated with transaction download.
- *
- * Tx download algorithm:
- *
- * When inv comes in, queue up (process_time, txid) inside the peer's
- * CNodeState (m_tx_process_time) as long as m_tx_announced for the peer
- * isn't too big (MAX_PEER_TX_ANNOUNCEMENTS).
- *
- * The process_time for a transaction is set to nNow for outbound peers,
- * nNow + 2 seconds for inbound peers. This is the time at which we'll
- * consider trying to request the transaction from the peer in
- * SendMessages(). The delay for inbound peers is to allow outbound peers
- * a chance to announce before we request from inbound peers, to prevent
- * an adversary from using inbound connections to blind us to a
- * transaction (InvBlock).
- *
- * When we call SendMessages() for a given peer,
- * we will loop over the transactions in m_tx_process_time, looking
- * at the transactions whose process_time <= nNow. We'll request each
- * such transaction that we don't have already and that hasn't been
- * requested from another peer recently, up until we hit the
- * MAX_PEER_TX_IN_FLIGHT limit for the peer. Then we'll update
- * g_already_asked_for for each requested txid, storing the time of the
- * GETDATA request. We use g_already_asked_for to coordinate transaction
- * requests amongst our peers.
- *
- * For transactions that we still need but we have already recently
- * requested from some other peer, we'll reinsert (process_time, txid)
- * back into the peer's m_tx_process_time at the point in the future at
- * which the most recent GETDATA request would time out (ie
- * GETDATA_TX_INTERVAL + the request time stored in g_already_asked_for).
- * We add an additional delay for inbound peers, again to prefer
- * attempting download from outbound peers first.
- * We also add an extra small random delay up to 2 seconds
- * to avoid biasing some peers over others. (e.g., due to fixed ordering
- * of peer processing in ThreadMessageHandler).
- *
- * When we receive a transaction from a peer, we remove the txid from the
- * peer's m_tx_in_flight set and from their recently announced set
- * (m_tx_announced). We also clear g_already_asked_for for that entry, so
- * that if somehow the transaction is not accepted but also not added to
- * the reject filter, then we will eventually redownload from other
- * peers.
- */
- struct TxDownloadState {
- /* Track when to attempt download of announced transactions (process
- * time in micros -> txid)
- */
- std::multimap<std::chrono::microseconds, GenTxid> m_tx_process_time;
-
- //! Store all the transactions a peer has recently announced
- std::set<uint256> m_tx_announced;
-
- //! Store transactions which were requested by us, with timestamp
- std::map<uint256, std::chrono::microseconds> m_tx_in_flight;
-
- //! Periodically check for stuck getdata requests
- std::chrono::microseconds m_check_expiry_timer{0};
- };
-
- TxDownloadState m_tx_download;
-
//! Whether this peer is an inbound connection
bool m_is_inbound;
@@ -430,13 +387,10 @@ struct CNodeState {
//! Whether this peer relays txs via wtxid
bool m_wtxid_relay{false};
- CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
- address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
- m_is_manual_connection (is_manual)
+ CNodeState(CAddress addrIn, bool is_inbound, bool is_manual)
+ : address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual)
{
fCurrentlyConnected = false;
- nMisbehavior = 0;
- m_should_discourage = false;
pindexBestKnownBlock = nullptr;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = nullptr;
@@ -461,9 +415,6 @@ struct CNodeState {
}
};
-// Keeps track of the time (in microseconds) when transactions were requested last time
-limitedmap<uint256, std::chrono::microseconds> g_already_asked_for GUARDED_BY(cs_main)(MAX_INV_SZ);
-
/** Map maintaining per-node state. */
static std::map<NodeId, CNodeState> mapNodeState GUARDED_BY(cs_main);
@@ -474,12 +425,64 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
+/**
+ * Data structure for an individual peer. This struct is not protected by
+ * cs_main since it does not contain validation-critical data.
+ *
+ * Memory is owned by shared pointers and this object is destructed when
+ * the refcount drops to zero.
+ *
+ * TODO: move most members from CNodeState to this structure.
+ * TODO: move remaining application-layer data members from CNode to this structure.
+ */
+struct Peer {
+ /** Same id as the CNode object for this peer */
+ const NodeId m_id{0};
+
+ /** Protects misbehavior data members */
+ 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). */
+ bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){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);
+
+ /** Protects m_getdata_requests **/
+ Mutex m_getdata_requests_mutex;
+ /** Work queue of items requested by this peer **/
+ std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
+
+ Peer(NodeId id) : m_id(id) {}
+};
+
+using PeerRef = std::shared_ptr<Peer>;
+
+/**
+ * Map of all Peer objects, keyed by peer id. This map is protected
+ * by the global g_peer_mutex. Once a shared pointer reference is
+ * taken, the lock may be released. Individual fields are protected by
+ * their own locks.
+ */
+Mutex g_peer_mutex;
+static std::map<NodeId, PeerRef> g_peer_map GUARDED_BY(g_peer_mutex);
+
+/** Get a shared pointer to the Peer object.
+ * May return nullptr if the Peer object can't be found. */
+static PeerRef GetPeerRef(NodeId id)
+{
+ LOCK(g_peer_mutex);
+ auto it = g_peer_map.find(id);
+ return it != g_peer_map.end() ? it->second : nullptr;
+}
+
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.fInbound || node.HasPermission(PF_NOBAN)) && !node.fOneShot && !node.fClient;
+ state->fPreferredDownload = (!node.IsInboundConn() || node.HasPermission(PF_NOBAN)) && !node.IsAddrFetchConn() && !node.fClient;
nPreferredDownload += state->fPreferredDownload;
}
@@ -522,7 +525,7 @@ static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs
}
if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
// First block on the queue was received, update the start download time for the next one
- state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros());
+ state->nDownloadingSince = std::max(state->nDownloadingSince, count_microseconds(GetTime<std::chrono::microseconds>()));
}
state->vBlocksInFlight.erase(itInFlight->second.second);
state->nBlocksInFlight--;
@@ -557,7 +560,7 @@ static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint25
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
- state->nDownloadingSince = GetTimeMicros();
+ state->nDownloadingSince = GetTime<std::chrono::microseconds>().count();
}
if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
nPeersWithValidatedDownloads++;
@@ -625,20 +628,19 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
return;
}
}
- connman.ForNode(nodeid, [&connman](CNode* pfrom){
- AssertLockHeld(cs_main);
+ connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
- AssertLockHeld(cs_main);
- connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
+ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
+ connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
return true;
});
@@ -757,73 +759,35 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
}
}
-void EraseTxRequest(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- g_already_asked_for.erase(gtxid.GetHash());
-}
-
-std::chrono::microseconds GetTxRequestTime(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- auto it = g_already_asked_for.find(gtxid.GetHash());
- if (it != g_already_asked_for.end()) {
- return it->second;
- }
- return {};
-}
-
-void UpdateTxRequestTime(const GenTxid& gtxid, std::chrono::microseconds request_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- auto it = g_already_asked_for.find(gtxid.GetHash());
- if (it == g_already_asked_for.end()) {
- g_already_asked_for.insert(std::make_pair(gtxid.GetHash(), request_time));
- } else {
- g_already_asked_for.update(it, request_time);
- }
-}
-
-std::chrono::microseconds CalculateTxGetDataTime(const GenTxid& gtxid, std::chrono::microseconds current_time, bool use_inbound_delay, bool use_txid_delay) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- std::chrono::microseconds process_time;
- const auto last_request_time = GetTxRequestTime(gtxid);
- // First time requesting this tx
- if (last_request_time.count() == 0) {
- process_time = current_time;
- } else {
- // Randomize the delay to avoid biasing some peers over others (such as due to
- // fixed ordering of peer processing in ThreadMessageHandler)
- process_time = last_request_time + GETDATA_TX_INTERVAL + GetRandMicros(MAX_GETDATA_RANDOM_DELAY);
- }
-
- // We delay processing announcements from inbound peers
- if (use_inbound_delay) process_time += INBOUND_PEER_TX_DELAY;
-
- // We delay processing announcements from peers that use txid-relay (instead of wtxid)
- if (use_txid_delay) process_time += TXID_RELAY_DELAY;
-
- return process_time;
-}
+} // namespace
-void RequestTx(CNodeState* state, const GenTxid& gtxid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
{
- CNodeState::TxDownloadState& peer_download_state = state->m_tx_download;
- if (peer_download_state.m_tx_announced.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
- peer_download_state.m_tx_process_time.size() >= MAX_PEER_TX_ANNOUNCEMENTS ||
- peer_download_state.m_tx_announced.count(gtxid.GetHash())) {
- // Too many queued announcements from this peer, or we already have
- // this announcement
+ AssertLockHeld(::cs_main); // For m_txrequest
+ NodeId nodeid = node.GetId();
+ if (!node.HasPermission(PF_RELAY) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
+ // Too many queued announcements from this peer
return;
}
- peer_download_state.m_tx_announced.insert(gtxid.GetHash());
-
- // Calculate the time to try requesting this transaction. Use
- // fPreferredDownload as a proxy for outbound peers.
- const auto process_time = CalculateTxGetDataTime(gtxid, current_time, !state->fPreferredDownload, !state->m_wtxid_relay && g_wtxid_relay_peers > 0);
-
- peer_download_state.m_tx_process_time.emplace(process_time, gtxid);
+ const CNodeState* state = State(nodeid);
+
+ // Decide the TxRequestTracker parameters for this announcement:
+ // - "preferred": if fPreferredDownload is set (= outbound, or PF_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).
+ auto delay = std::chrono::microseconds{0};
+ const bool preferred = state->fPreferredDownload;
+ if (!preferred) delay += NONPREF_PEER_TX_DELAY;
+ if (!gtxid.IsWtxid() && g_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
+ const bool overloaded = !node.HasPermission(PF_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);
}
-} // namespace
-
// This function is used for testing the stale tip eviction logic, see
// denialofservice_tests.cpp
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
@@ -833,36 +797,37 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
if (state) state->m_last_block_announcement = time_in_seconds;
}
-// Returns true for outbound peers, excluding manual connections, feelers, and
-// one-shots.
-static bool IsOutboundDisconnectionCandidate(const CNode& node)
-{
- return !(node.fInbound || node.m_manual_connection || node.fFeeler || node.fOneShot);
-}
-
-void PeerLogicValidation::InitializeNode(CNode *pnode) {
+void PeerManager::InitializeNode(CNode *pnode) {
CAddress addr = pnode->addr;
std::string addrName = pnode->GetAddrName();
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn(), pnode->IsManualConn()));
+ assert(m_txrequest.Count(nodeid) == 0);
+ }
+ {
+ PeerRef peer = std::make_shared<Peer>(nodeid);
+ LOCK(g_peer_mutex);
+ g_peer_map.emplace_hint(g_peer_map.end(), nodeid, std::move(peer));
+ }
+ if (!pnode->IsInboundConn()) {
+ PushNodeVersion(*pnode, m_connman, GetTime());
}
- if(!pnode->fInbound)
- PushNodeVersion(*pnode, *connman, GetTime());
}
-void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
+void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const
{
- std::map<uint256, uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
+ std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
+
+ for (const auto& txid : unbroadcast_txids) {
+ CTransactionRef tx = m_mempool.get(txid);
- for (const auto& elem : unbroadcast_txids) {
- // Sanity check: all unbroadcast txns should exist in the mempool
- if (m_mempool.exists(elem.first)) {
+ if (tx != nullptr) {
LOCK(cs_main);
- RelayTransaction(elem.first, elem.second, *connman);
+ RelayTransaction(txid, tx->GetWitnessHash(), m_connman);
} else {
- m_mempool.RemoveUnbroadcastTx(elem.first, true);
+ m_mempool.RemoveUnbroadcastTx(txid, true);
}
}
@@ -872,16 +837,24 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
-void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
fUpdateConnectionTime = false;
LOCK(cs_main);
+ int misbehavior{0};
+ {
+ PeerRef peer = GetPeerRef(nodeid);
+ assert(peer != nullptr);
+ misbehavior = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+ LOCK(g_peer_mutex);
+ g_peer_map.erase(nodeid);
+ }
CNodeState *state = State(nodeid);
assert(state != nullptr);
if (state->fSyncStarted)
nSyncStarted--;
- if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ if (misbehavior == 0 && state->fCurrentlyConnected) {
fUpdateConnectionTime = true;
}
@@ -889,6 +862,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
mapBlocksInFlight.erase(entry.hash);
}
EraseOrphansFor(nodeid);
+ m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
assert(nPeersWithValidatedDownloads >= 0);
@@ -906,22 +880,29 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
assert(nPeersWithValidatedDownloads == 0);
assert(g_outbound_peers_with_protect_from_disconnect == 0);
assert(g_wtxid_relay_peers == 0);
+ assert(m_txrequest.Size() == 0);
}
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
}
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
- if (state == nullptr)
- return false;
- stats.nMisbehavior = state->nMisbehavior;
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
- for (const QueuedBlock& queue : state->vBlocksInFlight) {
- if (queue.pindex)
- stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ {
+ LOCK(cs_main);
+ CNodeState* state = State(nodeid);
+ if (state == nullptr)
+ return false;
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
+ for (const QueuedBlock& queue : state->vBlocksInFlight) {
+ if (queue.pindex)
+ stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ }
}
+
+ PeerRef peer = GetPeerRef(nodeid);
+ if (peer == nullptr) return false;
+ stats.m_misbehavior_score = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+
return true;
}
@@ -1061,39 +1042,27 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
return nEvicted;
}
-/**
- * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
- * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
- */
-void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
{
assert(howmuch > 0);
- CNodeState* const state = State(pnode);
- if (state == nullptr) return;
+ PeerRef peer = GetPeerRef(pnode);
+ if (peer == nullptr) return;
- state->nMisbehavior += howmuch;
+ LOCK(peer->m_misbehavior_mutex);
+ peer->m_misbehavior_score += howmuch;
const std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
- {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
- state->m_should_discourage = true;
+ if (peer->m_misbehavior_score >= DISCOURAGEMENT_THRESHOLD && peer->m_misbehavior_score - howmuch < DISCOURAGEMENT_THRESHOLD) {
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
+ peer->m_should_discourage = true;
} else {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
}
}
-/**
- * Potentially mark a node discouraged based on the contents of a BlockValidationState object
- *
- * @param[in] via_compact_block this bool is passed in because net_processing should
- * punish peers differently depending on whether the data was provided in a compact
- * block message or not. If the compact block had a valid header, but contained invalid
- * txs, the peer should not be punished. See BIP 152.
- *
- * @return Returns true if the peer was punished (probably disconnected)
- */
-static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") {
+bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
+ bool via_compact_block, const std::string& message)
+{
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
@@ -1101,7 +1070,6 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
- LOCK(cs_main);
Misbehaving(nodeid, 100, message);
return true;
}
@@ -1125,18 +1093,12 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_INVALID_HEADER:
case BlockValidationResult::BLOCK_CHECKPOINT:
case BlockValidationResult::BLOCK_INVALID_PREV:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- }
+ Misbehaving(nodeid, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case BlockValidationResult::BLOCK_MISSING_PREV:
- {
- // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
- LOCK(cs_main);
- Misbehaving(nodeid, 10, message);
- }
+ // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
+ Misbehaving(nodeid, 10, message);
return true;
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1148,23 +1110,15 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
return false;
}
-/**
- * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
- *
- * @return Returns true if the peer was punished (probably disconnected)
- */
-static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "")
+bool PeerManager::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message)
{
switch (state.GetResult()) {
case TxValidationResult::TX_RESULT_UNSET:
break;
// The node is providing invalid data:
case TxValidationResult::TX_CONSENSUS:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- return true;
- }
+ Misbehaving(nodeid, 100, message);
+ return true;
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
@@ -1202,8 +1156,10 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
- : connman(connmanIn),
+PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
+ CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
+ : m_chainparams(chainparams),
+ m_connman(connman),
m_banman(banman),
m_chainman(chainman),
m_mempool(pool),
@@ -1223,13 +1179,12 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CS
// same probability that we have in the reject filter).
g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
- const Consensus::Params& consensusParams = Params().GetConsensus();
// Stale tip checking and peer eviction are on two different timers, but we
// don't want them to get out of sync due to drift in the scheduler, so we
// combine them in one function and schedule at the quicker (peer-eviction)
// timer.
static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer");
- scheduler.scheduleEvery([this, consensusParams] { this->CheckForStaleTipAndEvictPeers(consensusParams); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
+ scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
// schedule next run for 10-15 minutes in the future
const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
@@ -1238,9 +1193,10 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CS
/**
* Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
- * block. Also save the time of the last tip update.
+ * block, remember the recently confirmed transactions, and delete tracked
+ * announcements for them. Also save the time of the last tip update.
*/
-void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
+void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
{
LOCK(g_cs_orphans);
@@ -1282,9 +1238,16 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
}
}
+ {
+ LOCK(cs_main);
+ for (const auto& ptx : pblock->vtx) {
+ m_txrequest.ForgetTxHash(ptx->GetHash());
+ m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
+ }
+ }
}
-void PeerLogicValidation::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex)
+void PeerManager::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex)
{
// To avoid relay problems with transactions that were previously
// confirmed, clear our filter of recently confirmed transactions whenever
@@ -1309,7 +1272,7 @@ static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_
* Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
-void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
+void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true);
const CNetMsgMaker msgMaker(PROTOCOL_VERSION);
@@ -1320,7 +1283,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
return;
nHighestFastAnnounce = pindex->nHeight;
- bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, Params().GetConsensus());
+ bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus());
uint256 hashBlock(pblock->GetHash());
{
@@ -1331,11 +1294,11 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
}
- connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
- AssertLockHeld(cs_main);
+ m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// TODO: Avoid the repeated-serialization here
- if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
+ if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
return;
ProcessBlockAvailability(pnode->GetId());
CNodeState &state = *State(pnode->GetId());
@@ -1344,9 +1307,9 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) &&
!PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
- LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock",
+ LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock",
hashBlock.ToString(), pnode->GetId());
- connman->PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
+ m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
state.pindexBestHeaderSent = pindex;
}
});
@@ -1356,9 +1319,9 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
* Update our best height and announce any block hashes which weren't previously
* in ::ChainActive() to our peers.
*/
-void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+void PeerManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
- connman->SetBestHeight(nNewHeight);
+ m_connman.SetBestHeight(nNewHeight);
SetServiceFlagsIBDCache(!fInitialDownload);
if (!fInitialDownload) {
@@ -1375,7 +1338,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
}
// Relay inventory, but don't relay old inventory during initial block download.
- connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
+ m_connman.ForEachNode([nNewHeight, &vHashes](CNode* pnode) {
LOCK(pnode->cs_inventory);
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) {
for (const uint256& hash : reverse_iterate(vHashes)) {
@@ -1383,7 +1346,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
}
});
- connman->WakeMessageHandler();
+ m_connman.WakeMessageHandler();
}
}
@@ -1391,7 +1354,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
* Handle invalid block rejection and consequent peer discouragement, maintain which
* peers announce compact blocks.
*/
-void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) {
+void PeerManager::BlockChecked(const CBlock& block, const BlockValidationState& state) {
LOCK(cs_main);
const uint256 hash(block.GetHash());
@@ -1414,7 +1377,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
!::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman);
}
}
if (it != mapBlockSource.end())
@@ -1427,56 +1390,50 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
//
-bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- switch (inv.type)
- {
- case MSG_TX:
- case MSG_WITNESS_TX:
- case MSG_WTX:
- {
- assert(recentRejects);
- if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
- {
- // If the chain tip has changed previously rejected transactions
- // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
- // or a double-spend. Reset the rejects filter and give those
- // txs a second chance.
- hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
- recentRejects->reset();
- }
-
- {
- LOCK(g_cs_orphans);
- if (!inv.IsMsgWtx() && mapOrphanTransactions.count(inv.hash)) {
- return true;
- } else if (inv.IsMsgWtx() && g_orphans_by_wtxid.count(inv.hash)) {
- return true;
- }
- }
+ assert(recentRejects);
+ if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
+ // If the chain tip has changed previously rejected transactions
+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
+ // or a double-spend. Reset the rejects filter and give those
+ // txs a second chance.
+ hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
+ recentRejects->reset();
+ }
- {
- LOCK(g_cs_recent_confirmed_transactions);
- if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
- }
+ const uint256& hash = gtxid.GetHash();
- return recentRejects->contains(inv.hash) || mempool.exists(ToGenTxid(inv));
+ {
+ LOCK(g_cs_orphans);
+ if (!gtxid.IsWtxid() && mapOrphanTransactions.count(hash)) {
+ return true;
+ } else if (gtxid.IsWtxid() && g_orphans_by_wtxid.count(hash)) {
+ return true;
}
- case MSG_BLOCK:
- case MSG_WITNESS_BLOCK:
- return LookupBlockIndex(inv.hash) != nullptr;
}
- // Don't know what it is, just say we already got one
- return true;
+
+ {
+ LOCK(g_cs_recent_confirmed_transactions);
+ if (g_recent_confirmed_transactions->contains(hash)) return true;
+ }
+
+ return recentRejects->contains(hash) || mempool.exists(gtxid);
+}
+
+bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ return LookupBlockIndex(block_hash) != nullptr;
}
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
- connman.ForEachNode([&txid, &wtxid](CNode* pnode)
- {
- AssertLockHeld(cs_main);
- CNodeState &state = *State(pnode->GetId());
- if (state.m_wtxid_relay) {
+ connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
+
+ CNodeState* state = State(pnode->GetId());
+ if (state == nullptr) return;
+ if (state->m_wtxid_relay) {
pnode->PushTxInventory(wtxid);
} else {
pnode->PushTxInventory(txid);
@@ -1486,7 +1443,7 @@ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman&
static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& connman)
{
- unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
+ 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
@@ -1495,11 +1452,14 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
const CSipHasher hasher = 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}}};
assert(nRelayNodes <= best.size());
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->IsAddrRelayPeer()) {
+ if (pnode->RelayAddrsWithConn()) {
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -1552,7 +1512,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
BlockValidationState state;
- if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ if (!ActivateBestChain(state, chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -1565,11 +1525,11 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
}
}
- const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
if (send &&
connman.OutboundTargetReached(true) &&
- (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
@@ -1595,7 +1555,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
std::shared_ptr<const CBlock> pblock;
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
pblock = a_recent_block;
- } else if (inv.type == MSG_WITNESS_BLOCK) {
+ } else if (inv.IsMsgWitnessBlk()) {
// Fast-path: in this case it is possible to serve the block directly from disk,
// as the network format matches the format on disk
std::vector<uint8_t> block_data;
@@ -1612,12 +1572,11 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
pblock = pblockRead;
}
if (pblock) {
- if (inv.type == MSG_BLOCK)
+ if (inv.IsMsgBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_WITNESS_BLOCK)
+ } else if (inv.IsMsgWitnessBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_FILTERED_BLOCK)
- {
+ } else if (inv.IsMsgFilteredBlk()) {
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
if (pfrom.m_tx_relay != nullptr) {
@@ -1641,9 +1600,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
// else
// no response
- }
- else if (inv.type == MSG_CMPCT_BLOCK)
- {
+ } else if (inv.IsMsgCmpctBlk()) {
// If a peer is asking for old blocks, we're almost guaranteed
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
@@ -1678,7 +1635,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-CTransactionRef static FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
{
auto txinfo = mempool.info(gtxid);
if (txinfo.tx) {
@@ -1705,13 +1662,13 @@ CTransactionRef static FindTxForGetData(const CNode& peer, const GenTxid& gtxid,
return {};
}
-void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
+void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex)
{
AssertLockNotHeld(cs_main);
- std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin();
+ std::deque<CInv>::iterator it = peer.m_getdata_requests.begin();
std::vector<CInv> vNotFound;
- const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
const std::chrono::seconds now = GetTime<std::chrono::seconds>();
// Get last mempool request time
@@ -1721,7 +1678,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
// them.
- while (it != pfrom.vRecvGetData.end() && it->IsGenTxMsg()) {
+ while (it != peer.m_getdata_requests.end() && it->IsGenTxMsg()) {
if (interruptMsgProc) return;
// The send buffer provides backpressure. If there's no space in
// the buffer, pause processing until the next call.
@@ -1734,7 +1691,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
continue;
}
- CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now);
+ CTransactionRef tx = FindTxForGetData(mempool, pfrom, ToGenTxid(inv), mempool_req, now);
if (tx) {
// WTX and WITNESS_TX imply we serialize with witness
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
@@ -1746,11 +1703,11 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
LOCK(mempool.cs);
auto txiter = mempool.GetIter(tx->GetHash());
if (txiter) {
- const CTxMemPool::setEntries& parents = mempool.GetMemPoolParents(*txiter);
+ const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst();
parent_ids_to_add.reserve(parents.size());
- for (CTxMemPool::txiter parent_iter : parents) {
- if (parent_iter->GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
- parent_ids_to_add.push_back(parent_iter->GetTx().GetHash());
+ for (const CTxMemPoolEntry& parent : parents) {
+ if (parent.GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
+ parent_ids_to_add.push_back(parent.GetTx().GetHash());
}
}
}
@@ -1769,16 +1726,16 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// Only process one BLOCK item per call, since they're uncommon and can be
// expensive to process.
- if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
+ if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
+ if (inv.IsGenBlkMsg()) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
}
// else: If the first item on the queue is an unknown type, we erase it
// and continue processing the queue on the next call.
}
- pfrom.vRecvGetData.erase(pfrom.vRecvGetData.begin(), it);
+ peer.m_getdata_requests.erase(peer.m_getdata_requests.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
@@ -1807,25 +1764,24 @@ static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_ma
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman& connman) {
+void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) {
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
return;
}
resp.txn[i] = block.vtx[req.indexes[i]];
}
LOCK(cs_main);
- const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
-static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
+void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block)
{
- const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
size_t nCount = headers.size();
if (nCount == 0) {
@@ -1849,7 +1805,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -1883,7 +1839,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
}
BlockValidationState state;
- if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
+ if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
return;
@@ -1914,10 +1870,10 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight);
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
- bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) {
@@ -1927,7 +1883,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
+ (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -1951,7 +1907,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -1964,7 +1920,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
@@ -1982,18 +1938,19 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// until we have a headers chain that has at least
// nMinimumChainWork, even if a peer has a chain past our tip,
// as an anti-DoS measure.
- if (IsOutboundDisconnectionCandidate(pfrom)) {
+ if (pfrom.IsOutboundOrBlockRelayConn()) {
LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom.GetId());
pfrom.fDisconnect = true;
}
}
}
- if (!pfrom.fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
- // If this is an outbound full-relay peer, check to see if we should protect
- // it from the bad/lagging chain logic.
- // Note that block-relay-only peers are already implicitly protected, so we
- // only consider setting m_protect for the full-relay peers.
+ // If this is an outbound full-relay peer, check to see if we should protect
+ // it from the bad/lagging chain logic.
+ // Note that outbound block-relay peers are excluded from this protection, and
+ // thus always subject to eviction under the bad/lagging chain logic.
+ // See ChainSyncTimeoutState.
+ if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
@@ -2005,13 +1962,20 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
return;
}
-void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+/**
+ * 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
+ * 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.
+ */
+void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
{
AssertLockHeld(cs_main);
AssertLockHeld(g_cs_orphans);
- std::set<NodeId> setMisbehaving;
- bool done = false;
- while (!done && !orphan_work_set.empty()) {
+
+ while (!orphan_work_set.empty()) {
const uint256 orphanHash = *orphan_work_set.begin();
orphan_work_set.erase(orphan_work_set.begin());
@@ -2019,18 +1983,13 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
if (orphan_it == mapOrphanTransactions.end()) continue;
const CTransactionRef porphanTx = orphan_it->second.tx;
- const CTransaction& orphanTx = *porphanTx;
- NodeId fromPeer = orphan_it->second.fromPeer;
- // Use a new TxValidationState because orphans come from different peers (and we call
- // MaybePunishNodeForTx based on the source peer from the orphan map, not based on the peer
- // that relayed the previous transaction).
- TxValidationState orphan_state;
-
- if (setMisbehaving.count(fromPeer)) continue;
- if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ TxValidationState state;
+ std::list<CTransactionRef> removed_txn;
+
+ if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), connman);
- for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
+ RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
+ for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
@@ -2039,22 +1998,23 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
}
}
EraseOrphanTx(orphanHash);
- done = true;
- } else if (orphan_state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
- if (orphan_state.IsInvalid()) {
- // Punish peer that gave us an invalid orphan tx
- if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
- setMisbehaving.insert(fromPeer);
- }
+ for (const CTransactionRef& removedTx : removed_txn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
+ break;
+ } else if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
+ if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
orphanHash.ToString(),
- fromPeer,
- orphan_state.ToString());
+ orphan_it->second.fromPeer,
+ state.ToString());
+ // Maybe punish peer that gave us an invalid orphan tx
+ MaybePunishNodeForTx(orphan_it->second.fromPeer, state);
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- if (orphan_state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
+ if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
// We can add the wtxid of this transaction to our reject filter.
// Do not add txids of witness transactions or witness-stripped
// transactions to the filter, as they can have been malleated;
@@ -2069,7 +2029,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
// for concerns around weakening security of unupgraded nodes
// if we start doing this too early.
assert(recentRejects);
- recentRejects->insert(orphanTx.GetWitnessHash());
+ recentRejects->insert(porphanTx->GetWitnessHash());
// If the transaction failed for TX_INPUTS_NOT_STANDARD,
// then we know that the witness was irrelevant to the policy
// failure, since this check depends only on the txid
@@ -2078,17 +2038,17 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
// processing of this transaction in the event that child
// transactions are later received (resulting in
// parent-fetching by txid via the orphan-handling logic).
- if (orphan_state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && orphanTx.GetWitnessHash() != orphanTx.GetHash()) {
+ if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && porphanTx->GetWitnessHash() != porphanTx->GetHash()) {
// We only add the txid if it differs from the wtxid, to
// avoid wasting entries in the rolling bloom filter.
- recentRejects->insert(orphanTx.GetHash());
+ recentRejects->insert(porphanTx->GetHash());
}
}
EraseOrphanTx(orphanHash);
- done = true;
+ break;
}
- mempool.check(&::ChainstateActive().CoinsTip());
}
+ m_mempool.check(&::ChainstateActive().CoinsTip());
}
/**
@@ -2096,7 +2056,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] chain_params Chain parameters
* @param[in] filter_type The filter type the request is for. Must be basic filters.
* @param[in] start_height The start height for the request
@@ -2106,7 +2066,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
* @param[out] filter_index The filter index, if the request can be serviced.
* @return True if the request can be serviced.
*/
-static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_params,
+static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params,
BlockFilterType filter_type, uint32_t start_height,
const uint256& stop_hash, uint32_t max_height_diff,
const CBlockIndex*& stop_index,
@@ -2114,11 +2074,11 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
{
const bool supported_filter_type =
(filter_type == BlockFilterType::BASIC &&
- gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS));
+ (peer.GetLocalServices() & NODE_COMPACT_FILTERS));
if (!supported_filter_type) {
LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n",
- pfrom.GetId(), static_cast<uint8_t>(filter_type));
- pfrom.fDisconnect = true;
+ peer.GetId(), static_cast<uint8_t>(filter_type));
+ peer.fDisconnect = true;
return false;
}
@@ -2129,8 +2089,8 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
// Check that the stop block exists and the peer would be allowed to fetch it.
if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n",
- pfrom.GetId(), stop_hash.ToString());
- pfrom.fDisconnect = true;
+ peer.GetId(), stop_hash.ToString());
+ peer.fDisconnect = true;
return false;
}
}
@@ -2139,14 +2099,14 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
if (start_height > stop_height) {
LogPrint(BCLog::NET, "peer %d sent invalid getcfilters/getcfheaders with " /* Continued */
"start height %d and stop height %d\n",
- pfrom.GetId(), start_height, stop_height);
- pfrom.fDisconnect = true;
+ peer.GetId(), start_height, stop_height);
+ peer.fDisconnect = true;
return false;
}
if (stop_height - start_height >= max_height_diff) {
LogPrint(BCLog::NET, "peer %d requested too many cfilters/cfheaders: %d / %d\n",
- pfrom.GetId(), stop_height - start_height + 1, max_height_diff);
- pfrom.fDisconnect = true;
+ peer.GetId(), stop_height - start_height + 1, max_height_diff);
+ peer.fDisconnect = true;
return false;
}
@@ -2164,12 +2124,12 @@ static bool PrepareBlockFilterRequest(CNode& pfrom, const CChainParams& chain_pa
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2182,13 +2142,12 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
MAX_GETCFILTERS_SIZE, stop_index, filter_index)) {
return;
}
std::vector<BlockFilter> filters;
-
if (!filter_index->LookupFilterRange(start_height, stop_index, filters)) {
LogPrint(BCLog::NET, "Failed to find block filter in index: filter_type=%s, start_height=%d, stop_hash=%s\n",
BlockFilterTypeName(filter_type), start_height, stop_hash.ToString());
@@ -2196,9 +2155,9 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
}
for (const auto& filter : filters) {
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion())
.Make(NetMsgType::CFILTER, filter);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
}
@@ -2207,12 +2166,12 @@ static void ProcessGetCFilters(CNode& pfrom, CDataStream& vRecv, const CChainPar
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2225,7 +2184,7 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, start_height, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash,
MAX_GETCFHEADERS_SIZE, stop_index, filter_index)) {
return;
}
@@ -2248,13 +2207,13 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
return;
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion())
.Make(NetMsgType::CFHEADERS,
filter_type_ser,
stop_index->GetBlockHash(),
prev_header,
filter_hashes);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
/**
@@ -2262,12 +2221,12 @@ static void ProcessGetCFHeaders(CNode& pfrom, CDataStream& vRecv, const CChainPa
*
* May disconnect from the peer in the case of a bad request.
*
- * @param[in] pfrom The peer that we received the request from
+ * @param[in] peer The peer that we received the request from
* @param[in] vRecv The raw message received
* @param[in] chain_params Chain parameters
* @param[in] connman Pointer to the connection manager
*/
-static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainParams& chain_params,
+static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params,
CConnman& connman)
{
uint8_t filter_type_ser;
@@ -2279,7 +2238,7 @@ static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainPa
const CBlockIndex* stop_index;
BlockFilterIndex* filter_index;
- if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, /*start_height=*/0, stop_hash,
+ if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, /*start_height=*/0, stop_hash,
/*max_height_diff=*/std::numeric_limits<uint32_t>::max(),
stop_index, filter_index)) {
return;
@@ -2300,25 +2259,17 @@ static void ProcessGetCFCheckPt(CNode& pfrom, CDataStream& vRecv, const CChainPa
}
}
- CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion())
+ CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion())
.Make(NetMsgType::CFCHECKPT,
filter_type_ser,
stop_index->GetBlockHash(),
headers);
- connman.PushMessage(&pfrom, std::move(msg));
+ connman.PushMessage(&peer, std::move(msg));
}
-void ProcessMessage(
- CNode& pfrom,
- const std::string& msg_type,
- CDataStream& vRecv,
- const std::chrono::microseconds time_received,
- const CChainParams& chainparams,
- ChainstateManager& chainman,
- CTxMemPool& mempool,
- CConnman& connman,
- BanMan* banman,
- const std::atomic<bool>& interruptMsgProc)
+void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
+ const std::chrono::microseconds time_received,
+ const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -2327,12 +2278,13 @@ void ProcessMessage(
return;
}
+ PeerRef peer = GetPeerRef(pfrom.GetId());
+ if (peer == nullptr) return;
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom.nVersion != 0)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "redundant version message");
return;
}
@@ -2344,19 +2296,17 @@ void ProcessMessage(
uint64_t nServiceInt;
ServiceFlags nServices;
int nVersion;
- int nSendVersion;
std::string cleanSubVer;
int nStartingHeight = -1;
bool fRelay = true;
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
- nSendVersion = std::min(nVersion, PROTOCOL_VERSION);
nServices = ServiceFlags(nServiceInt);
- if (!pfrom.fInbound)
+ if (!pfrom.IsInboundConn())
{
- connman.SetServices(pfrom.addr, nServices);
+ m_connman.SetServices(pfrom.addr, nServices);
}
- if (!pfrom.fInbound && !pfrom.fFeeler && !pfrom.m_manual_connection && !HasAllDesirableServiceFlags(nServices))
+ if (pfrom.ExpectServicesFromConn() && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom.GetId(), nServices, GetDesirableServiceFlags(nServices));
pfrom.fDisconnect = true;
@@ -2383,27 +2333,37 @@ void ProcessMessage(
if (!vRecv.empty())
vRecv >> fRelay;
// Disconnect if we connected to ourself
- if (pfrom.fInbound && !connman.CheckIncomingNonce(nNonce))
+ if (pfrom.IsInboundConn() && !m_connman.CheckIncomingNonce(nNonce))
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom.addr.ToString());
pfrom.fDisconnect = true;
return;
}
- if (pfrom.fInbound && addrMe.IsRoutable())
+ if (pfrom.IsInboundConn() && addrMe.IsRoutable())
{
SeenLocal(addrMe);
}
// Be shy and don't send version until we hear
- if (pfrom.fInbound)
- PushNodeVersion(pfrom, connman, GetAdjustedTime());
+ if (pfrom.IsInboundConn())
+ PushNodeVersion(pfrom, m_connman, GetAdjustedTime());
+
+ // Change version
+ const int greatest_common_version = std::min(nVersion, PROTOCOL_VERSION);
+ pfrom.SetCommonVersion(greatest_common_version);
+ pfrom.nVersion = nVersion;
+
+ const CNetMsgMaker msg_maker(greatest_common_version);
- if (nVersion >= WTXID_RELAY_VERSION) {
- connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY));
+ if (greatest_common_version >= WTXID_RELAY_VERSION) {
+ m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY));
}
- connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
+ m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
+
+ // Signal ADDRv2 support (BIP155).
+ m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
pfrom.nServices = nServices;
pfrom.SetAddrLocal(addrMe);
@@ -2424,10 +2384,6 @@ void ProcessMessage(
pfrom.m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
}
- // Change version
- pfrom.SetSendVersion(nSendVersion);
- pfrom.nVersion = nVersion;
-
if((nServices & NODE_WITNESS))
{
LOCK(cs_main);
@@ -2440,9 +2396,23 @@ void ProcessMessage(
UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom.fInbound && pfrom.IsAddrRelayPeer())
- {
- // Advertise our address
+ if (!pfrom.IsInboundConn() && !pfrom.IsBlockOnlyConn()) {
+ // For outbound peers, we try to relay our address (so that other
+ // nodes can try to find us more quickly, as we have no guarantee
+ // that an outbound peer is even aware of how to reach us) and do a
+ // one-time address fetch (to help populate/update our addrman). If
+ // we're starting up for the first time, our addrman may be pretty
+ // empty and no one will know who we are, so these mechanisms are
+ // important to help us connect to the network.
+ //
+ // We also update the addrman to record connection success for
+ // these peers (which include OUTBOUND_FULL_RELAY and FEELER
+ // connections) so that addrman will have an up-to-date notion of
+ // which peers are online and available.
+ //
+ // We skip these operations for BLOCK_RELAY peers to avoid
+ // potentially leaking information about our BLOCK_RELAY
+ // connections via the addrman or address relay.
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@@ -2459,9 +2429,12 @@ void ProcessMessage(
}
// Get recent addresses
- connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
pfrom.fGetAddr = true;
- connman.MarkAddressGood(pfrom.addr);
+
+ // Moves address from New to Tried table in Addrman, resolves
+ // tried-table collisions, etc.
+ m_connman.MarkAddressGood(pfrom.addr);
}
std::string remoteAddr;
@@ -2478,14 +2451,13 @@ void ProcessMessage(
AddTimeData(pfrom.addr, nTimeOffset);
// If the peer is old enough to have the old alert system, send it the final alert.
- if (pfrom.nVersion <= 70012) {
+ if (greatest_common_version <= 70012) {
CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION);
- connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert));
+ m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make("alert", finalAlert));
}
// Feeler connections exist only to verify if address is online.
- if (pfrom.fFeeler) {
- assert(pfrom.fInbound == false);
+ if (pfrom.IsFeelerConn()) {
pfrom.fDisconnect = true;
}
return;
@@ -2493,19 +2465,17 @@ void ProcessMessage(
if (pfrom.nVersion == 0) {
// Must have a version message before anything else
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
return;
}
// At this point, the outgoing message serialization version can't change.
- const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
+ const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
- if (msg_type == NetMsgType::VERACK)
- {
- pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION));
+ if (msg_type == NetMsgType::VERACK) {
+ if (pfrom.fSuccessfullyConnected) return;
- if (!pfrom.fInbound) {
+ if (!pfrom.IsInboundConn()) {
// Mark this node as currently connected, so we update its timestamp later.
LOCK(cs_main);
State(pfrom.GetId())->fCurrentlyConnected = true;
@@ -2515,14 +2485,14 @@ void ProcessMessage(
pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay");
}
- if (pfrom.nVersion >= SENDHEADERS_VERSION) {
+ if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) {
// Tell our peer we prefer to receive headers rather than inv's
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
// nodes)
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS));
}
- if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) {
+ if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) {
// Tell our peer we are willing to provide version 1 or 2 cmpctblocks
// However, we do not request new block announcements using
// cmpctblock messages.
@@ -2531,9 +2501,9 @@ void ProcessMessage(
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 2;
if (pfrom.GetLocalServices() & NODE_WITNESS)
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
nCMPCTBLOCKVersion = 1;
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
}
pfrom.fSuccessfullyConnected = true;
return;
@@ -2548,7 +2518,7 @@ void ProcessMessage(
pfrom.fDisconnect = true;
return;
}
- if (pfrom.nVersion >= WTXID_RELAY_VERSION) {
+ if (pfrom.GetCommonVersion() >= WTXID_RELAY_VERSION) {
LOCK(cs_main);
if (!State(pfrom.GetId())->m_wtxid_relay) {
State(pfrom.GetId())->m_wtxid_relay = true;
@@ -2559,23 +2529,29 @@ void ProcessMessage(
}
if (!pfrom.fSuccessfullyConnected) {
- // Must have a verack message before anything else
- LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
+ LogPrint(BCLog::NET, "Unsupported message \"%s\" prior to verack from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
return;
}
- if (msg_type == NetMsgType::ADDR) {
+ if (msg_type == NetMsgType::ADDR || msg_type == NetMsgType::ADDRV2) {
+ int stream_version = vRecv.GetVersion();
+ if (msg_type == NetMsgType::ADDRV2) {
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
+ // unserialize methods know that an address in v2 format is coming.
+ stream_version |= ADDRV2_FORMAT;
+ }
+
+ OverrideStream<CDataStream> s(&vRecv, vRecv.GetType(), stream_version);
std::vector<CAddress> vAddr;
- vRecv >> vAddr;
- if (!pfrom.IsAddrRelayPeer()) {
+ s >> vAddr;
+
+ if (!pfrom.RelayAddrsWithConn()) {
return;
}
if (vAddr.size() > MAX_ADDR_TO_SEND)
{
- LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
return;
}
@@ -2597,7 +2573,7 @@ void ProcessMessage(
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom.AddAddressKnown(addr);
- if (banman && (banman->IsDiscouraged(addr) || banman->IsBanned(addr))) {
+ if (m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr))) {
// Do not process banned/discouraged addresses beyond remembering we received them
continue;
}
@@ -2605,20 +2581,25 @@ void ProcessMessage(
if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
- RelayAddress(addr, fReachable, connman);
+ RelayAddress(addr, fReachable, m_connman);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
- connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
+ m_connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom.fGetAddr = false;
- if (pfrom.fOneShot)
+ if (pfrom.IsAddrFetchConn())
pfrom.fDisconnect = true;
return;
}
+ if (msg_type == NetMsgType::SENDADDRV2) {
+ pfrom.m_wants_addrv2 = true;
+ return;
+ }
+
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom.GetId())->fPreferHeaders = true;
@@ -2653,7 +2634,6 @@ void ProcessMessage(
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
return;
}
@@ -2669,14 +2649,11 @@ void ProcessMessage(
LOCK(cs_main);
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
uint256* best_block{nullptr};
- for (CInv &inv : vInv)
- {
- if (interruptMsgProc)
- return;
+ for (CInv& inv : vInv) {
+ if (interruptMsgProc) return;
// Ignore INVs that don't match wtxidrelay setting.
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
@@ -2687,14 +2664,10 @@ void ProcessMessage(
if (inv.IsMsgWtx()) continue;
}
- bool fAlreadyHave = AlreadyHave(inv, mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
-
- if (inv.IsMsgTx()) {
- inv.type |= nFetchFlags;
- }
+ if (inv.IsMsgBlk()) {
+ const bool fAlreadyHave = AlreadyHaveBlock(inv.hash);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
- if (inv.type == MSG_BLOCK) {
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
@@ -2704,20 +2677,26 @@ void ProcessMessage(
// then fetch the blocks we need to catch up.
best_block = &inv.hash;
}
- } else {
+ } else if (inv.IsGenTxMsg()) {
+ const GenTxid gtxid = ToGenTxid(inv);
+ const bool fAlreadyHave = AlreadyHaveTx(gtxid, m_mempool);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
+
pfrom.AddKnownTx(inv.hash);
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
return;
- } else if (!fAlreadyHave && !chainman.ActiveChainstate().IsInitialBlockDownload()) {
- RequestTx(State(pfrom.GetId()), ToGenTxid(inv), current_time);
+ } else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ AddTxAnnouncement(pfrom, gtxid, current_time);
}
+ } else {
+ LogPrint(BCLog::NET, "Unknown inv type \"%s\" received from peer=%d\n", inv.ToString(), pfrom.GetId());
}
}
if (best_block != nullptr) {
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), *best_block));
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}
@@ -2729,7 +2708,6 @@ void ProcessMessage(
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
return;
}
@@ -2740,8 +2718,12 @@ void ProcessMessage(
LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom.GetId());
}
- pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams, connman, mempool, interruptMsgProc);
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ peer->m_getdata_requests.insert(peer->m_getdata_requests.end(), vInv.begin(), vInv.end());
+ ProcessGetData(pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ }
+
return;
}
@@ -2770,7 +2752,7 @@ void ProcessMessage(
a_recent_block = most_recent_block;
}
BlockValidationState state;
- if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ if (!ActivateBestChain(state, m_chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -2794,7 +2776,7 @@ void ProcessMessage(
}
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
- const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
+ const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
@@ -2825,40 +2807,42 @@ void ProcessMessage(
// Unlock cs_most_recent_block to avoid cs_main lock inversion
}
if (recent_block) {
- SendBlockTransactions(*recent_block, req, pfrom, connman);
+ SendBlockTransactions(pfrom, *recent_block, req);
return;
}
- LOCK(cs_main);
+ {
+ LOCK(cs_main);
- const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
- if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
- return;
- }
+ const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
+ if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
+ return;
+ }
- if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
- // If an older block is requested (should never happen in practice,
- // but can happen in tests) send a block response instead of a
- // blocktxn response. Sending a full block response instead of a
- // small blocktxn response is preferable in the case where a peer
- // might maliciously send lots of getblocktxn requests to trigger
- // expensive disk reads, because it will require the peer to
- // actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
- CInv inv;
- inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
- inv.hash = req.blockhash;
- pfrom.vRecvGetData.push_back(inv);
- // The message processing loop will go around again (without pausing) and we'll respond then (without cs_main)
- return;
- }
+ if (pindex->nHeight >= ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
+ CBlock block;
+ bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
+ assert(ret);
- CBlock block;
- bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus());
- assert(ret);
+ SendBlockTransactions(pfrom, block, req);
+ return;
+ }
+ }
- SendBlockTransactions(block, req, pfrom, connman);
+ // If an older block is requested (should never happen in practice,
+ // but can happen in tests) send a block response instead of a
+ // blocktxn response. Sending a full block response instead of a
+ // small blocktxn response is preferable in the case where a peer
+ // might maliciously send lots of getblocktxn requests to trigger
+ // expensive disk reads, because it will require the peer to
+ // actually receive all the data read from disk over the network.
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
+ CInv inv;
+ WITH_LOCK(cs_main, inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK);
+ inv.hash = req.blockhash;
+ WITH_LOCK(peer->m_getdata_requests_mutex, peer->m_getdata_requests.push_back(inv));
+ // The message processing loop will go around again (without pausing) and we'll respond then
return;
}
@@ -2889,7 +2873,7 @@ void ProcessMessage(
return;
}
- if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
+ if (!BlockRequestAllowed(pindex, m_chainparams.GetConsensus())) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
return;
}
@@ -2925,7 +2909,7 @@ void ProcessMessage(
// will re-announce the new block via headers (or compact blocks again)
// in the SendMessages logic.
nodestate->pindexBestHeaderSent = pindex ? pindex : ::ChainActive().Tip();
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
return;
}
@@ -2964,15 +2948,12 @@ void ProcessMessage(
TxValidationState state;
- for (const GenTxid& gtxid : {GenTxid(false, txid), GenTxid(true, wtxid)}) {
- nodestate->m_tx_download.m_tx_announced.erase(gtxid.GetHash());
- nodestate->m_tx_download.m_tx_in_flight.erase(gtxid.GetHash());
- EraseTxRequest(gtxid);
- }
+ m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
+ if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
std::list<CTransactionRef> lRemovedTxn;
- // We do the AlreadyHave() check using wtxid, rather than txid - in the
+ // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
// absence of witness malleation, this is strictly better, because the
// recent rejects filter may contain the wtxid but rarely contains
// the txid of a segwit transaction that has been rejected.
@@ -2984,15 +2965,19 @@ void ProcessMessage(
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (!AlreadyHave(CInv(MSG_WTX, wtxid), mempool) &&
- AcceptToMemoryPool(mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
- mempool.check(&::ChainstateActive().CoinsTip());
- RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), connman);
+ if (!AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool) &&
+ AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
+ m_mempool.check(&::ChainstateActive().CoinsTip());
+ // As this version of the transaction was acceptable, we can forget about any
+ // requests for it.
+ m_txrequest.ForgetTxHash(tx.GetHash());
+ m_txrequest.ForgetTxHash(tx.GetWitnessHash());
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
- pfrom.orphan_work_set.insert(elem->first);
+ peer->m_orphan_work_set.insert(elem->first);
}
}
}
@@ -3002,10 +2987,14 @@ void ProcessMessage(
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
pfrom.GetId(),
tx.GetHash().ToString(),
- mempool.size(), mempool.DynamicMemoryUsage() / 1000);
+ m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
+
+ for (const CTransactionRef& removedTx : lRemovedTxn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(connman, mempool, pfrom.orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(peer->m_orphan_work_set);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -3028,7 +3017,6 @@ void ProcessMessage(
}
}
if (!fRejectedParents) {
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
for (const uint256& parent_txid : unique_parents) {
@@ -3037,12 +3025,16 @@ void ProcessMessage(
// wtxidrelay peers.
// Eventually we should replace this with an improved
// protocol for getting all unconfirmed parents.
- CInv _inv(MSG_TX | nFetchFlags, parent_txid);
+ const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
pfrom.AddKnownTx(parent_txid);
- if (!AlreadyHave(_inv, mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time);
+ if (!AlreadyHaveTx(gtxid, m_mempool)) AddTxAnnouncement(pfrom, gtxid, current_time);
}
AddOrphanTx(ptx, pfrom.GetId());
+ // Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
+ m_txrequest.ForgetTxHash(tx.GetHash());
+ m_txrequest.ForgetTxHash(tx.GetWitnessHash());
+
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
@@ -3059,6 +3051,8 @@ void ProcessMessage(
// from any of our non-wtxidrelay peers.
recentRejects->insert(tx.GetHash());
recentRejects->insert(tx.GetWitnessHash());
+ m_txrequest.ForgetTxHash(tx.GetHash());
+ m_txrequest.ForgetTxHash(tx.GetWitnessHash());
}
} else {
if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
@@ -3077,6 +3071,7 @@ void ProcessMessage(
// if we start doing this too early.
assert(recentRejects);
recentRejects->insert(tx.GetWitnessHash());
+ m_txrequest.ForgetTxHash(tx.GetWitnessHash());
// If the transaction failed for TX_INPUTS_NOT_STANDARD,
// then we know that the witness was irrelevant to the policy
// failure, since this check depends only on the txid
@@ -3087,12 +3082,11 @@ void ProcessMessage(
// parent-fetching by txid via the orphan-handling logic).
if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && tx.GetWitnessHash() != tx.GetHash()) {
recentRejects->insert(tx.GetHash());
+ m_txrequest.ForgetTxHash(tx.GetHash());
}
if (RecursiveDynamicUsage(*ptx) < 100000) {
AddToCompactExtraTransactions(ptx);
}
- } else if (tx.HasWitness() && RecursiveDynamicUsage(*ptx) < 100000) {
- AddToCompactExtraTransactions(ptx);
}
if (pfrom.HasPermission(PF_FORCERELAY)) {
@@ -3100,18 +3094,15 @@ void ProcessMessage(
// if they were already in the mempool,
// allowing the node to function as a gateway for
// nodes hidden behind it.
- if (!mempool.exists(tx.GetHash())) {
+ if (!m_mempool.exists(tx.GetHash())) {
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(), connman);
+ RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
}
}
}
- for (const CTransactionRef& removedTx : lRemovedTxn)
- AddToCompactExtraTransactions(removedTx);
-
// If a tx has been detected by recentRejects, we will have reached
// this point and the tx will have been ignored. Because we haven't run
// the tx through AcceptToMemoryPool, we won't have computed a DoS
@@ -3157,7 +3148,7 @@ void ProcessMessage(
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!::ChainstateActive().IsInitialBlockDownload())
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return;
}
@@ -3168,7 +3159,7 @@ void ProcessMessage(
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return;
@@ -3218,16 +3209,16 @@ void ProcessMessage(
// so we just grab the block via normal getdata
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
}
return;
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
- if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
+ if (!fAlreadyInFlight && !CanDirectFetch(m_chainparams.GetConsensus()))
return;
- if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
+ if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
return;
@@ -3239,9 +3230,9 @@ void ProcessMessage(
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
- (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&mempool));
+ (*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
// The block was already in flight using compact blocks from the same peer
LogPrint(BCLog::NET, "Peer sent us compact block we were already syncing!\n");
@@ -3259,7 +3250,7 @@ void ProcessMessage(
// Duplicate txindexes, the block is now in-flight, so just request it
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return;
}
@@ -3276,7 +3267,7 @@ void ProcessMessage(
fProcessBLOCKTXN = true;
} else {
req.blockhash = pindex->GetBlockHash();
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req));
}
} else {
// This block is either already in flight from a different
@@ -3284,7 +3275,7 @@ void ProcessMessage(
// download from.
// Optimistically try to reconstruct anyway since we might be
// able to without any round trips.
- PartiallyDownloadedBlock tempBlock(&mempool);
+ PartiallyDownloadedBlock tempBlock(&m_mempool);
ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status != READ_STATUS_OK) {
// TODO: don't ignore failures
@@ -3302,7 +3293,7 @@ void ProcessMessage(
// mempool will probably be useless - request the block normally
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash());
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv));
return;
} else {
// If this was an announce-cmpctblock, we want the same treatment as a header message
@@ -3311,8 +3302,9 @@ void ProcessMessage(
}
} // cs_main
- if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, chainman, mempool, connman, banman, interruptMsgProc);
+ if (fProcessBLOCKTXN) {
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc);
+ }
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -3320,7 +3312,7 @@ void ProcessMessage(
// the peer if the header turns out to be for an invalid block.
// Note that if a peer tries to build on an invalid chain, that
// will be detected and the peer will be disconnected/discouraged.
- return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
+ return ProcessHeadersMessage(pfrom, {cmpctblock.header}, /*via_compact_block=*/true);
}
if (fBlockReconstructed) {
@@ -3340,7 +3332,7 @@ void ProcessMessage(
// 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.
- chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3392,7 +3384,7 @@ void ProcessMessage(
// Might have collided, fall back to getdata now :(
std::vector<CInv> invs;
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
} else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
@@ -3430,7 +3422,7 @@ void ProcessMessage(
// 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.
- chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3454,7 +3446,6 @@ void ProcessMessage(
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
return;
}
@@ -3464,7 +3455,7 @@ void ProcessMessage(
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, /*via_compact_block=*/false);
+ return ProcessHeadersMessage(pfrom, headers, /*via_compact_block=*/false);
}
if (msg_type == NetMsgType::BLOCK)
@@ -3493,7 +3484,7 @@ void ProcessMessage(
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
- chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3509,12 +3500,8 @@ void ProcessMessage(
// to users' AddrMan and later request them by sending getaddr messages.
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
- if (!pfrom.fInbound) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
- return;
- }
- if (!pfrom.IsAddrRelayPeer()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
+ if (!pfrom.IsInboundConn()) {
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from %s connection. peer=%d\n", pfrom.ConnectionTypeAsString(), pfrom.GetId());
return;
}
@@ -3529,9 +3516,9 @@ void ProcessMessage(
pfrom.vAddrToSend.clear();
std::vector<CAddress> vAddr;
if (pfrom.HasPermission(PF_ADDR)) {
- vAddr = connman.GetAddresses();
+ vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
} else {
- vAddr = connman.GetAddresses(pfrom.addr.GetNetwork());
+ vAddr = m_connman.GetAddresses(pfrom, MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND);
}
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
@@ -3551,7 +3538,7 @@ void ProcessMessage(
return;
}
- if (connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
+ if (m_connman.OutboundTargetReached(false) && !pfrom.HasPermission(PF_MEMPOOL))
{
if (!pfrom.HasPermission(PF_NOBAN))
{
@@ -3569,8 +3556,7 @@ void ProcessMessage(
}
if (msg_type == NetMsgType::PING) {
- if (pfrom.nVersion > BIP0031_VERSION)
- {
+ if (pfrom.GetCommonVersion() > BIP0031_VERSION) {
uint64_t nonce = 0;
vRecv >> nonce;
// Echo the message back with the nonce. This allows for two useful features:
@@ -3584,7 +3570,7 @@ void ProcessMessage(
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::PONG, nonce));
}
return;
}
@@ -3605,7 +3591,7 @@ void ProcessMessage(
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
const auto ping_time = ping_end - pfrom.m_ping_start.load();
- if (ping_time.count() > 0) {
+ if (ping_time.count() >= 0) {
// Successful ping time measurement, replace previous
pfrom.nPingUsecTime = count_microseconds(ping_time);
pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), count_microseconds(ping_time));
@@ -3656,7 +3642,6 @@ void ProcessMessage(
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
}
else if (pfrom.m_tx_relay != nullptr)
@@ -3690,7 +3675,6 @@ void ProcessMessage(
}
}
if (bad) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
}
return;
@@ -3724,39 +3708,30 @@ void ProcessMessage(
}
if (msg_type == NetMsgType::GETCFILTERS) {
- ProcessGetCFilters(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFilters(pfrom, vRecv, m_chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFHeaders(pfrom, vRecv, m_chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman);
+ ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::NOTFOUND) {
- // Remove the NOTFOUND transactions from the peer
- LOCK(cs_main);
- CNodeState *state = State(pfrom.GetId());
std::vector<CInv> vInv;
vRecv >> vInv;
- if (vInv.size() <= MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (vInv.size() <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ LOCK(::cs_main);
for (CInv &inv : vInv) {
if (inv.IsGenTxMsg()) {
- // If we receive a NOTFOUND message for a txid we requested, erase
- // it from our data structures for this peer.
- auto in_flight_it = state->m_tx_download.m_tx_in_flight.find(inv.hash);
- if (in_flight_it == state->m_tx_download.m_tx_in_flight.end()) {
- // Skip any further work if this is a spurious NOTFOUND
- // message.
- continue;
- }
- state->m_tx_download.m_tx_in_flight.erase(in_flight_it);
- state->m_tx_download.m_tx_announced.erase(inv.hash);
+ // If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
+ // completed in TxRequestTracker.
+ m_txrequest.ReceivedResponse(pfrom.GetId(), inv.hash);
}
}
}
@@ -3768,23 +3743,20 @@ void ProcessMessage(
return;
}
-/** Maybe disconnect a peer and discourage future connections from its address.
- *
- * @param[in] pnode The node to check.
- * @return True if the peer was marked for disconnection in this function
- */
-bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
+bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode)
{
const NodeId peer_id{pnode.GetId()};
+ PeerRef peer = GetPeerRef(peer_id);
+ if (peer == nullptr) return false;
+
{
- LOCK(cs_main);
- CNodeState& state = *State(peer_id);
+ LOCK(peer->m_misbehavior_mutex);
// There's nothing to do if the m_should_discourage flag isn't set
- if (!state.m_should_discourage) return false;
+ if (!peer->m_should_discourage) return false;
- state.m_should_discourage = false;
- } // cs_main
+ 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
@@ -3792,7 +3764,7 @@ bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
return false;
}
- if (pnode.m_manual_connection) {
+ if (pnode.IsManualConn()) {
// We never disconnect or discourage manual peers for bad behavior
LogPrintf("Warning: not punishing manually connected peer %d!\n", peer_id);
return false;
@@ -3809,32 +3781,28 @@ bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
// Normal case: Disconnect the peer and discourage all nodes sharing the address
LogPrintf("Disconnecting and discouraging peer %d!\n", peer_id);
if (m_banman) m_banman->Discourage(pnode.addr);
- connman->DisconnectNode(pnode.addr);
+ m_connman.DisconnectNode(pnode.addr);
return true;
}
-bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
+bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
{
- const CChainParams& chainparams = Params();
- //
- // Message format
- // (4) message start
- // (12) command
- // (4) size
- // (4) checksum
- // (x) data
- //
bool fMoreWork = false;
- if (!pfrom->vRecvGetData.empty())
- ProcessGetData(*pfrom, chainparams, *connman, m_mempool, interruptMsgProc);
+ PeerRef peer = GetPeerRef(pfrom->GetId());
+ if (peer == nullptr) return false;
- if (!pfrom->orphan_work_set.empty()) {
- std::list<CTransactionRef> removed_txn;
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) {
+ ProcessGetData(*pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ }
+ }
+
+ {
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(*connman, m_mempool, pfrom->orphan_work_set, removed_txn);
- for (const CTransactionRef& removedTx : removed_txn) {
- AddToCompactExtraTransactions(removedTx);
+ if (!peer->m_orphan_work_set.empty()) {
+ ProcessOrphanTx(peer->m_orphan_work_set);
}
}
@@ -3842,9 +3810,16 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return false;
// this maintains the order of responses
- // and prevents vRecvGetData to grow unbounded
- if (!pfrom->vRecvGetData.empty()) return true;
- if (!pfrom->orphan_work_set.empty()) return true;
+ // and prevents m_getdata_requests to grow unbounded
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) return true;
+ }
+
+ {
+ LOCK(g_cs_orphans);
+ if (!peer->m_orphan_work_set.empty()) return true;
+ }
// Don't bother if send buffer is too full to respond anyway
if (pfrom->fPauseSend)
@@ -3858,45 +3833,24 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
// Just take one message
msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size;
- pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize();
+ pfrom->fPauseRecv = pfrom->nProcessQueueSize > m_connman.GetReceiveFloodSize();
fMoreWork = !pfrom->vProcessMsg.empty();
}
CNetMessage& msg(msgs.front());
- msg.SetVersion(pfrom->GetRecvVersion());
- // Check network magic
- if (!msg.m_valid_netmagic) {
- LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
- pfrom->fDisconnect = true;
- return false;
- }
-
- // Check header
- if (!msg.m_valid_header)
- {
- LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
- return fMoreWork;
- }
+ msg.SetVersion(pfrom->GetCommonVersion());
const std::string& msg_type = msg.m_command;
// Message size
unsigned int nMessageSize = msg.m_message_size;
- // Checksum
- CDataStream& vRecv = msg.m_recv;
- if (!msg.m_valid_checksum)
- {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__,
- SanitizeString(msg_type), nMessageSize, pfrom->GetId());
- return fMoreWork;
- }
-
try {
- ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, m_chainman, m_mempool, *connman, m_banman, interruptMsgProc);
- if (interruptMsgProc)
- return false;
- if (!pfrom->vRecvGetData.empty())
- fMoreWork = true;
+ ProcessMessage(*pfrom, msg_type, msg.m_recv, msg.m_time, interruptMsgProc);
+ if (interruptMsgProc) return false;
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) fMoreWork = true;
+ }
} catch (const std::exception& e) {
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg_type), nMessageSize, e.what(), typeid(e).name());
} catch (...) {
@@ -3906,14 +3860,14 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return fMoreWork;
}
-void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
+void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pto.GetId());
- const CNetMsgMaker msgMaker(pto.GetSendVersion());
+ const CNetMsgMaker msgMaker(pto.GetCommonVersion());
- if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) {
+ if (!state.m_chain_sync.m_protect && pto.IsOutboundOrBlockRelayConn() && state.fSyncStarted) {
// This is an outbound peer subject to disconnection if they don't
// announce a block with as much work as the current tip within
// CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if
@@ -3945,7 +3899,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
} else {
assert(state.m_chain_sync.m_work_header);
LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto.GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString());
- connman->PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
+ m_connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(state.m_chain_sync.m_work_header->pprev), uint256()));
state.m_chain_sync.m_sent_getheaders = true;
constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes
// Bump the timeout to allow a response, which could clear the timeout
@@ -3959,10 +3913,10 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
}
}
-void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
+void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds)
{
// Check whether we have too many outbound peers
- int extra_peers = connman->GetExtraOutboundCount();
+ int extra_peers = m_connman.GetExtraOutboundCount();
if (extra_peers > 0) {
// If we have more outbound peers than we target, disconnect one.
// Pick the outbound peer that least recently announced
@@ -3971,11 +3925,11 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
NodeId worst_peer = -1;
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
- connman->ForEachNode([&](CNode* pnode) {
- AssertLockHeld(cs_main);
+ m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
- if (!IsOutboundDisconnectionCandidate(*pnode) || pnode->fDisconnect) return;
+ if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return;
CNodeState *state = State(pnode->GetId());
if (state == nullptr) return; // shouldn't be possible, but just in case
// Don't evict our protected peers
@@ -3988,8 +3942,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
}
});
if (worst_peer != -1) {
- bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
- AssertLockHeld(cs_main);
+ bool disconnected = m_connman.ForNode(worst_peer, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
@@ -4012,18 +3966,16 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
// detected a stale tip. Don't try any more extra peers until
// we next detect a stale tip, to limit the load we put on the
// network from these extra connections.
- connman->SetTryNewOutboundPeer(false);
+ m_connman.SetTryNewOutboundPeer(false);
}
}
}
}
-void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
+void PeerManager::CheckForStaleTipAndEvictPeers()
{
LOCK(cs_main);
- if (connman == nullptr) return;
-
int64_t time_in_seconds = GetTime();
EvictExtraOutboundPeers(time_in_seconds);
@@ -4031,11 +3983,11 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
if (time_in_seconds > m_stale_tip_check_time) {
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (!fImporting && !fReindex && connman->GetNetworkActive() && connman->GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) {
+ if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(m_chainparams.GetConsensus())) {
LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
- connman->SetTryNewOutboundPeer(true);
- } else if (connman->GetTryNewOutboundPeer()) {
- connman->SetTryNewOutboundPeer(false);
+ m_connman.SetTryNewOutboundPeer(true);
+ } else if (m_connman.GetTryNewOutboundPeer()) {
+ m_connman.SetTryNewOutboundPeer(false);
}
m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
}
@@ -4062,9 +4014,9 @@ public:
};
}
-bool PeerLogicValidation::SendMessages(CNode* pto)
+bool PeerManager::SendMessages(CNode* pto)
{
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
// We must call MaybeDiscourageAndDisconnect first, to ensure that we'll
// disconnect misbehaving peers even before the version handshake is complete.
@@ -4075,7 +4027,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
return true;
// If we get here, the outgoing message serialization version is set and can't change.
- const CNetMsgMaker msgMaker(pto->GetSendVersion());
+ const CNetMsgMaker msgMaker(pto->GetCommonVersion());
//
// Message: ping
@@ -4096,13 +4048,13 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
pto->fPingQueued = false;
pto->m_ping_start = GetTime<std::chrono::microseconds>();
- if (pto->nVersion > BIP0031_VERSION) {
+ if (pto->GetCommonVersion() > BIP0031_VERSION) {
pto->nPingNonceSent = nonce;
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
} else {
// Peer is too old to support ping command with nonce, pong will never arrive.
pto->nPingNonceSent = 0;
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
}
}
@@ -4112,10 +4064,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
CNodeState &state = *State(pto->GetId());
// Address refresh broadcast
- int64_t nNow = GetTimeMicros();
auto current_time = GetTime<std::chrono::microseconds>();
- if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) {
+ if (pto->RelayAddrsWithConn() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) {
AdvertiseLocal(pto);
pto->m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -4123,11 +4074,22 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: addr
//
- if (pto->IsAddrRelayPeer() && pto->m_next_addr_send < current_time) {
+ if (pto->RelayAddrsWithConn() && pto->m_next_addr_send < current_time) {
pto->m_next_addr_send = PoissonNextSend(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
assert(pto->m_addr_known);
+
+ const char* msg_type;
+ int make_flags;
+ if (pto->m_wants_addrv2) {
+ msg_type = NetMsgType::ADDRV2;
+ make_flags = ADDRV2_FORMAT;
+ } else {
+ msg_type = NetMsgType::ADDR;
+ make_flags = 0;
+ }
+
for (const CAddress& addr : pto->vAddrToSend)
{
if (!pto->m_addr_known->contains(addr.GetKey()))
@@ -4137,14 +4099,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// receiver rejects addr messages larger than MAX_ADDR_TO_SEND
if (vAddr.size() >= MAX_ADDR_TO_SEND)
{
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ m_connman.PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr));
+ m_connman.PushMessage(pto, msgMaker.Make(make_flags, msg_type, vAddr));
// we only send the big addr message once
if (pto->vAddrToSend.capacity() > 40)
pto->vAddrToSend.shrink_to_fit();
@@ -4153,12 +4115,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Start block sync
if (pindexBestHeader == nullptr)
pindexBestHeader = ::ChainActive().Tip();
- bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
+ bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->IsAddrFetchConn()); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
- state.nHeadersSyncTimeout = GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
+ state.nHeadersSyncTimeout = count_microseconds(current_time) + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime())/(consensusParams.nPowTargetSpacing);
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
/* If possible, start at the block preceding the currently
@@ -4171,7 +4133,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight);
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256()));
}
}
@@ -4255,10 +4217,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LOCK(cs_most_recent_block);
if (most_recent_block_hash == pBestIndex->GetBlockHash()) {
if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock)
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block));
else {
CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness);
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
fGotBlockFromCache = true;
}
@@ -4268,7 +4230,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams);
assert(ret);
CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness);
- connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
+ m_connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock));
}
state.pindexBestHeaderSent = pBestIndex;
} else if (state.fPreferHeaders) {
@@ -4281,7 +4243,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__,
vHeaders.front().GetHash().ToString(), pto->GetId());
}
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders));
state.pindexBestHeaderSent = pBestIndex;
} else
fRevertToInv = true;
@@ -4326,7 +4288,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
for (const uint256& hash : pto->vInventoryBlockToSend) {
vInv.push_back(CInv(MSG_BLOCK, hash));
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -4338,8 +4300,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
- if (pto->fInbound) {
- pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
+ if (pto->IsInboundConn()) {
+ pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{m_connman.PoissonNextSendInbound(count_microseconds(current_time), INVENTORY_BROADCAST_INTERVAL)};
} else {
// Use half the delay for outbound peers, as there is less privacy concern for them.
pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
@@ -4379,7 +4341,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
@@ -4438,7 +4400,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
nRelayedTransactions++;
{
// Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow)
+ while (!vRelayExpiration.empty() && vRelayExpiration.front().first < count_microseconds(current_time))
{
mapRelay.erase(vRelayExpiration.front().second);
vRelayExpiration.pop_front();
@@ -4446,16 +4408,16 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
auto ret = mapRelay.emplace(txid, std::move(txinfo.tx));
if (ret.second) {
- vRelayExpiration.emplace_back(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret.first);
+ vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret.first);
}
// Add wtxid-based lookup into mapRelay as well, so that peers can request by wtxid
auto ret2 = mapRelay.emplace(wtxid, ret.first->second);
if (ret2.second) {
- vRelayExpiration.emplace_back(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret2.first);
+ vRelayExpiration.emplace_back(count_microseconds(current_time + std::chrono::microseconds{RELAY_TX_CACHE_TIME}), ret2.first);
}
}
if (vInv.size() == MAX_INV_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
pto->m_tx_relay->filterInventoryKnown.insert(hash);
@@ -4472,14 +4434,11 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
}
if (!vInv.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
current_time = GetTime<std::chrono::microseconds>();
- // nNow is the current system time (GetTimeMicros is not mockable) and
- // should be replaced by the mockable current_time eventually
- nNow = GetTimeMicros();
- if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) {
+ if (state.nStallingSince && state.nStallingSince < count_microseconds(current_time) - 1000000 * BLOCK_STALLING_TIMEOUT) {
// Stalling only triggers when the block download window cannot move. During normal steady state,
// the download window should be much larger than the to-be-downloaded set of blocks, so disconnection
// should only happen during initial block download.
@@ -4495,7 +4454,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
- if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
+ if (count_microseconds(current_time) > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
@@ -4505,7 +4464,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits<int64_t>::max()) {
// Detect whether this is a stalling initial-headers-sync peer
if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) {
- if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
+ if (count_microseconds(current_time) > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) {
// Disconnect a peer (without the noban permission) if it is our only sync peer,
// and we have others we could be using instead.
// Note: If all our peers are inbound, then we won't
@@ -4555,7 +4514,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
if (state.nBlocksInFlight == 0 && staller != -1) {
if (State(staller)->nStallingSince == 0) {
- State(staller)->nStallingSince = nNow;
+ State(staller)->nStallingSince = count_microseconds(current_time);
LogPrint(BCLog::NET, "Stall started peer=%d\n", staller);
}
}
@@ -4564,82 +4523,40 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: getdata (non-blocks)
//
-
- // For robustness, expire old requests after a long timeout, so that
- // we can resume downloading transactions from a peer even if they
- // were unresponsive in the past.
- // Eventually we should consider disconnecting peers, but this is
- // conservative.
- if (state.m_tx_download.m_check_expiry_timer <= current_time) {
- for (auto it=state.m_tx_download.m_tx_in_flight.begin(); it != state.m_tx_download.m_tx_in_flight.end();) {
- if (it->second <= current_time - TX_EXPIRY_INTERVAL) {
- LogPrint(BCLog::NET, "timeout of inflight tx %s from peer=%d\n", it->first.ToString(), pto->GetId());
- state.m_tx_download.m_tx_announced.erase(it->first);
- state.m_tx_download.m_tx_in_flight.erase(it++);
- } else {
- ++it;
- }
- }
- // On average, we do this check every TX_EXPIRY_INTERVAL. Randomize
- // so that we're not doing this for all peers at the same time.
- state.m_tx_download.m_check_expiry_timer = current_time + TX_EXPIRY_INTERVAL / 2 + GetRandMicros(TX_EXPIRY_INTERVAL);
- }
-
- auto& tx_process_time = state.m_tx_download.m_tx_process_time;
- while (!tx_process_time.empty() && tx_process_time.begin()->first <= current_time && state.m_tx_download.m_tx_in_flight.size() < MAX_PEER_TX_IN_FLIGHT) {
- const GenTxid gtxid = tx_process_time.begin()->second;
- // Erase this entry from tx_process_time (it may be added back for
- // processing at a later time, see below)
- tx_process_time.erase(tx_process_time.begin());
- CInv inv(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
- if (!AlreadyHave(inv, m_mempool)) {
- // If this transaction was last requested more than 1 minute ago,
- // then request.
- const auto last_request_time = GetTxRequestTime(gtxid);
- if (last_request_time <= current_time - GETDATA_TX_INTERVAL) {
- LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId());
- vGetData.push_back(inv);
- if (vGetData.size() >= MAX_GETDATA_SZ) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
- vGetData.clear();
- }
- UpdateTxRequestTime(gtxid, current_time);
- state.m_tx_download.m_tx_in_flight.emplace(gtxid.GetHash(), current_time);
- } else {
- // This transaction is in flight from someone else; queue
- // up processing to happen after the download times out
- // (with a slight delay for inbound peers, to prefer
- // requests to outbound peers).
- // Don't apply the txid-delay to re-requests of a
- // transaction; the heuristic of delaying requests to
- // txid-relay peers is to save bandwidth on initial
- // announcement of a transaction, and doesn't make sense
- // for a followup request if our first peer times out (and
- // would open us up to an attacker using inbound
- // wtxid-relay to prevent us from requesting transactions
- // from outbound txid-relay peers).
- const auto next_process_time = CalculateTxGetDataTime(gtxid, current_time, !state.fPreferredDownload, false);
- tx_process_time.emplace(next_process_time, gtxid);
+ std::vector<std::pair<NodeId, GenTxid>> expired;
+ auto requestable = m_txrequest.GetRequestable(pto->GetId(), current_time, &expired);
+ for (const auto& entry : expired) {
+ LogPrint(BCLog::NET, "timeout of inflight %s %s from peer=%d\n", entry.second.IsWtxid() ? "wtx" : "tx",
+ entry.second.GetHash().ToString(), entry.first);
+ }
+ for (const GenTxid& gtxid : requestable) {
+ if (!AlreadyHaveTx(gtxid, m_mempool)) {
+ LogPrint(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
+ gtxid.GetHash().ToString(), pto->GetId());
+ vGetData.emplace_back(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
+ if (vGetData.size() >= MAX_GETDATA_SZ) {
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ vGetData.clear();
}
+ m_txrequest.RequestedTx(pto->GetId(), gtxid.GetHash(), current_time + GETDATA_TX_INTERVAL);
} else {
- // We have already seen this transaction, no need to download.
- state.m_tx_download.m_tx_announced.erase(gtxid.GetHash());
- state.m_tx_download.m_tx_in_flight.erase(gtxid.GetHash());
+ // We have already seen this transaction, no need to download. This is just a belt-and-suspenders, as
+ // this should already be called whenever a transaction becomes AlreadyHaveTx().
+ m_txrequest.ForgetTxHash(gtxid.GetHash());
}
}
if (!vGetData.empty())
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
//
// Message: feefilter
//
- if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
+ if (pto->m_tx_relay != nullptr && 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();
- int64_t timeNow = GetTimeMicros();
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
@@ -4650,24 +4567,24 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
- pto->m_tx_relay->nextSendTimeFeeFilter = timeNow - 1;
+ pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) - 1;
}
}
- if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
+ if (count_microseconds(current_time) > pto->m_tx_relay->nextSendTimeFeeFilter) {
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) {
- connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
+ m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->m_tx_relay->lastSentFeeFilter = filterToSend;
}
- pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(count_microseconds(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 (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
+ else if (count_microseconds(current_time) + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
(currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
- pto->m_tx_relay->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
+ pto->m_tx_relay->nextSendTimeFeeFilter = count_microseconds(current_time) + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
}
}
} // release cs_main
diff --git a/src/net_processing.h b/src/net_processing.h
index 2d98714122..578660355a 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -9,10 +9,16 @@
#include <consensus/params.h>
#include <net.h>
#include <sync.h>
+#include <txrequest.h>
#include <validationinterface.h>
+class BlockTransactionsRequest;
+class BlockValidationState;
+class CBlockHeader;
+class CChainParams;
class CTxMemPool;
class ChainstateManager;
+class TxValidationState;
extern RecursiveMutex cs_main;
extern RecursiveMutex g_cs_orphans;
@@ -26,18 +32,10 @@ static const bool DEFAULT_PEERBLOCKFILTERS = false;
/** Threshold for marking a node to be discouraged, e.g. disconnected and added to the discouragement filter. */
static const int DISCOURAGEMENT_THRESHOLD{100};
-class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
-private:
- CConnman* const connman;
- /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
- BanMan* const m_banman;
- ChainstateManager& m_chainman;
- CTxMemPool& m_mempool;
-
- bool MaybeDiscourageAndDisconnect(CNode& pnode);
-
+class PeerManager final : public CValidationInterface, public NetEventsInterface {
public:
- PeerLogicValidation(CConnman* connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
+ PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
+ CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
/**
* Overridden from CValidationInterface.
@@ -79,18 +77,76 @@ public:
/** 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);
/** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
- void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
+ void CheckForStaleTipAndEvictPeers();
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */
void ReattemptInitialBroadcast(CScheduler& scheduler) const;
+ /** Process a single message from a peer. Public for fuzz testing */
+ void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
+ const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc);
+
+ /**
+ * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
+ * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
+ * Public for unit testing.
+ */
+ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message);
+
private:
+ /**
+ * Potentially mark a node discouraged based on the contents of a BlockValidationState object
+ *
+ * @param[in] via_compact_block this bool is passed in because net_processing should
+ * punish peers differently depending on whether the data was provided in a compact
+ * block message or not. If the compact block had a valid header, but contained invalid
+ * txs, the peer should not be punished. See BIP 152.
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ */
+ bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
+ bool via_compact_block, const std::string& message = "");
+
+ /**
+ * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ */
+ bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "");
+
+ /** Maybe disconnect a peer and discourage future connections from its address.
+ *
+ * @param[in] pnode The node to check.
+ * @return True if the peer was marked for disconnection in this function
+ */
+ bool MaybeDiscourageAndDisconnect(CNode& pnode);
+
+ void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
+ /** Process a single headers message from a peer. */
+ void ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block);
+
+ void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req);
+
+ /** Register with TxRequestTracker that an INV has been received from a
+ * peer. The announcement parameters are decided in PeerManager and then
+ * passed to TxRequestTracker. */
+ void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
+ const CChainParams& m_chainparams;
+ CConnman& m_connman;
+ /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
+ BanMan* const m_banman;
+ ChainstateManager& m_chainman;
+ CTxMemPool& m_mempool;
+ TxRequestTracker m_txrequest GUARDED_BY(::cs_main);
+
int64_t m_stale_tip_check_time; //!< Next time to check for stale tip
};
struct CNodeStateStats {
- int nMisbehavior = 0;
+ int m_misbehavior_score = 0;
int nSyncHeight = -1;
int nCommonHeight = -1;
std::vector<int> vHeightInFlight;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index d29aed6c8b..c0193fa2e9 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -3,79 +3,184 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <cstdint>
#include <netaddress.h>
+
+#include <crypto/common.h>
+#include <crypto/sha3.h>
#include <hash.h>
-#include <util/strencodings.h>
-#include <util/asmap.h>
+#include <prevector.h>
#include <tinyformat.h>
+#include <util/asmap.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <ios>
+#include <iterator>
+#include <tuple>
+
+constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE;
+constexpr size_t CNetAddr::MAX_ADDRV2_SIZE;
-static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
-static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
+CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
+{
+ switch (m_net) {
+ case NET_IPV4:
+ return BIP155Network::IPV4;
+ 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);
+ }
+ case NET_I2P:
+ return BIP155Network::I2P;
+ case NET_CJDNS:
+ return BIP155Network::CJDNS;
+ case NET_INTERNAL: // should have been handled before calling this function
+ case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
+ case NET_MAX: // m_net is never and should not be set to NET_MAX
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+}
+
+bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size)
+{
+ switch (possible_bip155_net) {
+ case BIP155Network::IPV4:
+ if (address_size == ADDR_IPV4_SIZE) {
+ m_net = NET_IPV4;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 IPv4 address with length %u (should be %u)", address_size,
+ ADDR_IPV4_SIZE));
+ case BIP155Network::IPV6:
+ if (address_size == ADDR_IPV6_SIZE) {
+ m_net = NET_IPV6;
+ return true;
+ }
+ 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;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 TORv3 address with length %u (should be %u)", address_size,
+ ADDR_TORV3_SIZE));
+ case BIP155Network::I2P:
+ if (address_size == ADDR_I2P_SIZE) {
+ m_net = NET_I2P;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 I2P address with length %u (should be %u)", address_size,
+ ADDR_I2P_SIZE));
+ case BIP155Network::CJDNS:
+ if (address_size == ADDR_CJDNS_SIZE) {
+ m_net = NET_CJDNS;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 CJDNS address with length %u (should be %u)", address_size,
+ ADDR_CJDNS_SIZE));
+ }
-// 0xFD + sha256("bitcoin")[0:5]
-static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
+ // Don't throw on addresses with unknown network ids (maybe from the future).
+ // Instead silently drop them and have the unserialization code consume
+ // subsequent ones which may be known to us.
+ return false;
+}
/**
* Construct an unspecified IPv6 network address (::/128).
*
* @note This address is considered invalid by CNetAddr::IsValid()
*/
-CNetAddr::CNetAddr()
-{
- memset(ip, 0, sizeof(ip));
-}
+CNetAddr::CNetAddr() {}
void CNetAddr::SetIP(const CNetAddr& ipIn)
{
+ // Size check.
+ switch (ipIn.m_net) {
+ case NET_IPV4:
+ assert(ipIn.m_addr.size() == ADDR_IPV4_SIZE);
+ break;
+ case NET_IPV6:
+ 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);
+ break;
+ case NET_I2P:
+ assert(ipIn.m_addr.size() == ADDR_I2P_SIZE);
+ break;
+ case NET_CJDNS:
+ assert(ipIn.m_addr.size() == ADDR_CJDNS_SIZE);
+ break;
+ case NET_INTERNAL:
+ assert(ipIn.m_addr.size() == ADDR_INTERNAL_SIZE);
+ break;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
m_net = ipIn.m_net;
- memcpy(ip, ipIn.ip, sizeof(ip));
+ m_addr = ipIn.m_addr;
}
-void CNetAddr::SetLegacyIPv6(const uint8_t ipv6[16])
+void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
{
- if (memcmp(ipv6, pchIPv4, sizeof(pchIPv4)) == 0) {
+ assert(ipv6.size() == ADDR_IPV6_SIZE);
+
+ size_t skip{0};
+
+ if (HasPrefix(ipv6, IPV4_IN_IPV6_PREFIX)) {
+ // IPv4-in-IPv6
m_net = NET_IPV4;
- } else if (memcmp(ipv6, pchOnionCat, sizeof(pchOnionCat)) == 0) {
+ skip = sizeof(IPV4_IN_IPV6_PREFIX);
+ } else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) {
+ // TORv2-in-IPv6
m_net = NET_ONION;
- } else if (memcmp(ipv6, g_internal_prefix, sizeof(g_internal_prefix)) == 0) {
+ skip = sizeof(TORV2_IN_IPV6_PREFIX);
+ } else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) {
+ // Internal-in-IPv6
m_net = NET_INTERNAL;
+ skip = sizeof(INTERNAL_IN_IPV6_PREFIX);
} else {
+ // IPv6
m_net = NET_IPV6;
}
- memcpy(ip, ipv6, 16);
-}
-void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
-{
- switch(network)
- {
- case NET_IPV4:
- m_net = NET_IPV4;
- memcpy(ip, pchIPv4, 12);
- memcpy(ip+12, ip_in, 4);
- break;
- case NET_IPV6:
- SetLegacyIPv6(ip_in);
- break;
- default:
- assert(!"invalid network");
- }
+ m_addr.assign(ipv6.begin() + skip, ipv6.end());
}
/**
- * Try to make this a dummy address that maps the specified name into IPv6 like
- * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy
- * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be
- * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to
- * unique-local addresses.
- *
- * CAddrMan uses these fake addresses to keep track of which DNS seeds were
- * used.
- *
+ * Create an "internal" address that represents a name or FQDN. CAddrMan uses
+ * these fake addresses to keep track of which DNS seeds were used.
* @returns Whether or not the operation was successful.
- *
- * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
+ * @see NET_INTERNAL, INTERNAL_IN_IPV6_PREFIX, CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
*/
bool CNetAddr::SetInternal(const std::string &name)
{
@@ -85,60 +190,106 @@ bool CNetAddr::SetInternal(const std::string &name)
m_net = NET_INTERNAL;
unsigned char hash[32] = {};
CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);
- memcpy(ip, g_internal_prefix, sizeof(g_internal_prefix));
- memcpy(ip + sizeof(g_internal_prefix), hash, sizeof(ip) - sizeof(g_internal_prefix));
+ m_addr.assign(hash, hash + ADDR_INTERNAL_SIZE);
return true;
}
+namespace torv3 {
+// https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt#n2135
+static constexpr size_t CHECKSUM_LEN = 2;
+static const unsigned char VERSION[] = {3};
+static constexpr size_t TOTAL_LEN = ADDR_TORV3_SIZE + CHECKSUM_LEN + sizeof(VERSION);
+
+static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKSUM_LEN])
+{
+ // TORv3 CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
+ static const unsigned char prefix[] = ".onion checksum";
+ static constexpr size_t prefix_len = 15;
+
+ SHA3_256 hasher;
+
+ hasher.Write(MakeSpan(prefix).first(prefix_len));
+ hasher.Write(addr_pubkey);
+ hasher.Write(VERSION);
+
+ uint8_t checksum_full[SHA3_256::OUTPUT_SIZE];
+
+ hasher.Finalize(checksum_full);
+
+ memcpy(checksum, checksum_full, sizeof(checksum));
+}
+
+}; // namespace torv3
+
/**
- * Try to make this a dummy address that maps the specified onion address into
- * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix
- * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they
- * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+ * Parse a TOR address and set this object to it.
*
* @returns Whether or not the operation was successful.
*
- * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193()
+ * @see CNetAddr::IsTor()
*/
-bool CNetAddr::SetSpecial(const std::string &strName)
+bool CNetAddr::SetSpecial(const std::string& str)
{
- if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
- std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
- if (vchAddr.size() != 16-sizeof(pchOnionCat))
+ static const char* suffix{".onion"};
+ static constexpr size_t suffix_len{6};
+
+ if (!ValidAsCString(str) || str.size() <= suffix_len ||
+ str.substr(str.size() - suffix_len) != suffix) {
+ return false;
+ }
+
+ bool invalid;
+ const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
+
+ if (invalid) {
+ 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: {
+ 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)};
+
+ uint8_t calculated_checksum[torv3::CHECKSUM_LEN];
+ torv3::Checksum(input_pubkey, calculated_checksum);
+
+ if (input_checksum != calculated_checksum || input_version != torv3::VERSION) {
return false;
+ }
+
m_net = NET_ONION;
- memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
- for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
- ip[i + sizeof(pchOnionCat)] = vchAddr[i];
+ m_addr.assign(input_pubkey.begin(), input_pubkey.end());
return true;
}
+ }
+
return false;
}
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{
- SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
+ m_net = NET_IPV4;
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&ipv4Addr);
+ m_addr.assign(ptr, ptr + ADDR_IPV4_SIZE);
}
CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope)
{
- SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr);
- scopeId = scope;
-}
-
-unsigned int CNetAddr::GetByte(int n) const
-{
- return ip[15-n];
+ SetLegacyIPv6(Span<const uint8_t>(reinterpret_cast<const uint8_t*>(&ipv6Addr), sizeof(ipv6Addr)));
+ m_scope_id = scope;
}
bool CNetAddr::IsBindAny() const
{
- const int cmplen = IsIPv4() ? 4 : 16;
- for (int i = 0; i < cmplen; ++i) {
- if (GetByte(i)) return false;
+ if (!IsIPv4() && !IsIPv6()) {
+ return false;
}
-
- return true;
+ return std::all_of(m_addr.begin(), m_addr.end(), [](uint8_t b) { return b == 0; });
}
bool CNetAddr::IsIPv4() const { return m_net == NET_IPV4; }
@@ -148,108 +299,118 @@ bool CNetAddr::IsIPv6() const { return m_net == NET_IPV6; }
bool CNetAddr::IsRFC1918() const
{
return IsIPv4() && (
- GetByte(3) == 10 ||
- (GetByte(3) == 192 && GetByte(2) == 168) ||
- (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31)));
+ m_addr[0] == 10 ||
+ (m_addr[0] == 192 && m_addr[1] == 168) ||
+ (m_addr[0] == 172 && m_addr[1] >= 16 && m_addr[1] <= 31));
}
bool CNetAddr::IsRFC2544() const
{
- return IsIPv4() && GetByte(3) == 198 && (GetByte(2) == 18 || GetByte(2) == 19);
+ return IsIPv4() && m_addr[0] == 198 && (m_addr[1] == 18 || m_addr[1] == 19);
}
bool CNetAddr::IsRFC3927() const
{
- return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254);
+ return IsIPv4() && HasPrefix(m_addr, std::array<uint8_t, 2>{169, 254});
}
bool CNetAddr::IsRFC6598() const
{
- return IsIPv4() && GetByte(3) == 100 && GetByte(2) >= 64 && GetByte(2) <= 127;
+ return IsIPv4() && m_addr[0] == 100 && m_addr[1] >= 64 && m_addr[1] <= 127;
}
bool CNetAddr::IsRFC5737() const
{
- return IsIPv4() && ((GetByte(3) == 192 && GetByte(2) == 0 && GetByte(1) == 2) ||
- (GetByte(3) == 198 && GetByte(2) == 51 && GetByte(1) == 100) ||
- (GetByte(3) == 203 && GetByte(2) == 0 && GetByte(1) == 113));
+ return IsIPv4() && (HasPrefix(m_addr, std::array<uint8_t, 3>{192, 0, 2}) ||
+ HasPrefix(m_addr, std::array<uint8_t, 3>{198, 51, 100}) ||
+ HasPrefix(m_addr, std::array<uint8_t, 3>{203, 0, 113}));
}
bool CNetAddr::IsRFC3849() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x0D && GetByte(12) == 0xB8;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x0D, 0xB8});
}
bool CNetAddr::IsRFC3964() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x02;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 2>{0x20, 0x02});
}
bool CNetAddr::IsRFC6052() const
{
- static const unsigned char pchRFC6052[] = {0,0x64,0xFF,0x9B,0,0,0,0,0,0,0,0};
- return IsIPv6() && memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0;
+ return IsIPv6() &&
+ HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x64, 0xFF, 0x9B, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
}
bool CNetAddr::IsRFC4380() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 &&
- GetByte(12) == 0;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x00, 0x00});
}
bool CNetAddr::IsRFC4862() const
{
- static const unsigned char pchRFC4862[] = {0xFE,0x80,0,0,0,0,0,0};
- return IsIPv6() && memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 8>{0xFE, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00});
}
bool CNetAddr::IsRFC4193() const
{
- return IsIPv6() && (GetByte(15) & 0xFE) == 0xFC;
+ return IsIPv6() && (m_addr[0] & 0xFE) == 0xFC;
}
bool CNetAddr::IsRFC6145() const
{
- static const unsigned char pchRFC6145[] = {0,0,0,0,0,0,0,0,0xFF,0xFF,0,0};
- return IsIPv6() && memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0;
+ return IsIPv6() &&
+ HasPrefix(m_addr, std::array<uint8_t, 12>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00});
}
bool CNetAddr::IsRFC4843() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) &&
+ (m_addr[3] & 0xF0) == 0x10;
}
bool CNetAddr::IsRFC7343() const
{
- return IsIPv6() && GetByte(15) == 0x20 && GetByte(14) == 0x01 &&
- GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20;
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 3>{0x20, 0x01, 0x00}) &&
+ (m_addr[3] & 0xF0) == 0x20;
}
bool CNetAddr::IsHeNet() const
{
- return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70);
+ return IsIPv6() && HasPrefix(m_addr, std::array<uint8_t, 4>{0x20, 0x01, 0x04, 0x70});
}
/**
- * @returns Whether or not this is a dummy address that maps an onion address
- * into IPv6.
- *
+ * Check whether this object represents a TOR address.
* @see CNetAddr::SetSpecial(const std::string &)
*/
bool CNetAddr::IsTor() const { return m_net == NET_ONION; }
+/**
+ * Check whether this object represents an I2P address.
+ */
+bool CNetAddr::IsI2P() const { return m_net == NET_I2P; }
+
+/**
+ * Check whether this object represents a CJDNS address.
+ */
+bool CNetAddr::IsCJDNS() const { return m_net == NET_CJDNS; }
+
bool CNetAddr::IsLocal() const
{
// IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8)
- if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0))
+ if (IsIPv4() && (m_addr[0] == 127 || m_addr[0] == 0)) {
return true;
+ }
// IPv6 loopback (::1/128)
static const unsigned char pchLocal[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
- if (IsIPv6() && memcmp(ip, pchLocal, 16) == 0)
+ if (IsIPv6() && memcmp(m_addr.data(), pchLocal, sizeof(pchLocal)) == 0) {
return true;
+ }
return false;
}
@@ -266,19 +427,11 @@ bool CNetAddr::IsLocal() const
*/
bool CNetAddr::IsValid() const
{
- // Cleanup 3-byte shifted addresses caused by garbage in size field
- // of addr messages from versions before 0.2.9 checksum.
- // Two consecutive addr messages look like this:
- // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
- // so if the first length field is garbled, it reads the second batch
- // of addr misaligned by 3 bytes.
- if (IsIPv6() && memcmp(ip, pchIPv4+3, sizeof(pchIPv4)-3) == 0)
- return false;
-
// unspecified IPv6 address (::/128)
unsigned char ipNone6[16] = {};
- if (IsIPv6() && memcmp(ip, ipNone6, 16) == 0)
+ if (IsIPv6() && memcmp(m_addr.data(), ipNone6, sizeof(ipNone6)) == 0) {
return false;
+ }
// documentation IPv6 address
if (IsRFC3849())
@@ -287,17 +440,11 @@ bool CNetAddr::IsValid() const
if (IsInternal())
return false;
- if (IsIPv4())
- {
- // INADDR_NONE
- uint32_t ipNone = INADDR_NONE;
- if (memcmp(ip+12, &ipNone, 4) == 0)
- return false;
-
- // 0
- ipNone = 0;
- if (memcmp(ip+12, &ipNone, 4) == 0)
+ if (IsIPv4()) {
+ const uint32_t addr = ReadBE32(m_addr.data());
+ if (addr == INADDR_ANY || addr == INADDR_NONE) {
return false;
+ }
}
return true;
@@ -318,7 +465,7 @@ bool CNetAddr::IsRoutable() const
}
/**
- * @returns Whether or not this is a dummy address that maps a name into IPv6.
+ * @returns Whether or not this is a dummy address that represents a name.
*
* @see CNetAddr::SetInternal(const std::string &)
*/
@@ -327,6 +474,26 @@ bool CNetAddr::IsInternal() const
return m_net == NET_INTERNAL;
}
+bool CNetAddr::IsAddrV1Compatible() const
+{
+ switch (m_net) {
+ case NET_IPV4:
+ case NET_IPV6:
+ case NET_INTERNAL:
+ return true;
+ case NET_ONION:
+ return m_addr.size() == ADDR_TORV2_SIZE;
+ case NET_I2P:
+ case NET_CJDNS:
+ return false;
+ case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
+ case NET_MAX: // m_net is never and should not be set to NET_MAX
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+}
+
enum Network CNetAddr::GetNetwork() const
{
if (IsInternal())
@@ -338,28 +505,72 @@ enum Network CNetAddr::GetNetwork() const
return m_net;
}
+static std::string IPv6ToString(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
+}
+
std::string CNetAddr::ToStringIP() const
{
- if (IsTor())
- return EncodeBase32(&ip[6], 10) + ".onion";
- if (IsInternal())
- return EncodeBase32(ip + sizeof(g_internal_prefix), sizeof(ip) - sizeof(g_internal_prefix)) + ".internal";
- 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);
- }
- if (IsIPv4())
- return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
- else
- return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- GetByte(15) << 8 | GetByte(14), GetByte(13) << 8 | GetByte(12),
- GetByte(11) << 8 | GetByte(10), GetByte(9) << 8 | GetByte(8),
- GetByte(7) << 8 | GetByte(6), GetByte(5) << 8 | GetByte(4),
- GetByte(3) << 8 | GetByte(2), GetByte(1) << 8 | GetByte(0));
+ 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);
+ }
+ if (m_net == NET_IPV4) {
+ return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]);
+ }
+ 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);
+
+ // 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 EncodeBase32(address) + ".onion";
+ }
+ default:
+ assert(false);
+ }
+ case NET_I2P:
+ return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p";
+ case NET_CJDNS:
+ return IPv6ToString(m_addr);
+ case NET_INTERNAL:
+ return EncodeBase32(m_addr) + ".internal";
+ case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
+ case NET_MAX: // m_net is never and should not be set to NET_MAX
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
}
std::string CNetAddr::ToString() const
@@ -369,12 +580,12 @@ std::string CNetAddr::ToString() const
bool operator==(const CNetAddr& a, const CNetAddr& b)
{
- return a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) == 0;
+ return a.m_net == b.m_net && a.m_addr == b.m_addr;
}
bool operator<(const CNetAddr& a, const CNetAddr& b)
{
- return a.m_net < b.m_net || (a.m_net == b.m_net && memcmp(a.ip, b.ip, 16) < 0);
+ return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr);
}
/**
@@ -391,7 +602,8 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
{
if (!IsIPv4())
return false;
- memcpy(pipv4Addr, ip+12, 4);
+ assert(sizeof(*pipv4Addr) == m_addr.size());
+ memcpy(pipv4Addr, m_addr.data(), m_addr.size());
return true;
}
@@ -410,7 +622,8 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
if (!IsIPv6()) {
return false;
}
- memcpy(pipv6Addr, ip, 16);
+ assert(sizeof(*pipv6Addr) == m_addr.size());
+ memcpy(pipv6Addr, m_addr.data(), m_addr.size());
return true;
}
@@ -421,34 +634,37 @@ bool CNetAddr::HasLinkedIPv4() const
uint32_t CNetAddr::GetLinkedIPv4() const
{
- if (IsIPv4() || IsRFC6145() || IsRFC6052()) {
- // IPv4, mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
- return ReadBE32(ip + 12);
+ if (IsIPv4()) {
+ return ReadBE32(m_addr.data());
+ } else if (IsRFC6052() || IsRFC6145()) {
+ // mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address
+ return ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data());
} else if (IsRFC3964()) {
// 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6
- return ReadBE32(ip + 2);
+ return ReadBE32(MakeSpan(m_addr).subspan(2, ADDR_IPV4_SIZE).data());
} else if (IsRFC4380()) {
// Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped
- return ~ReadBE32(ip + 12);
+ return ~ReadBE32(MakeSpan(m_addr).last(ADDR_IPV4_SIZE).data());
}
assert(false);
}
-uint32_t CNetAddr::GetNetClass() const {
- uint32_t net_class = NET_IPV6;
- if (IsLocal()) {
- net_class = 255;
- }
+Network CNetAddr::GetNetClass() const
+{
+ // Make sure that if we return NET_IPV6, then IsIPv6() is true. The callers expect that.
+
+ // Check for "internal" first because such addresses are also !IsRoutable()
+ // and we don't want to return NET_UNROUTABLE in that case.
if (IsInternal()) {
- net_class = NET_INTERNAL;
- } else if (!IsRoutable()) {
- net_class = NET_UNROUTABLE;
- } else if (HasLinkedIPv4()) {
- net_class = NET_IPV4;
- } else if (IsTor()) {
- net_class = NET_ONION;
+ return NET_INTERNAL;
+ }
+ if (!IsRoutable()) {
+ return NET_UNROUTABLE;
}
- return net_class;
+ if (HasLinkedIPv4()) {
+ return NET_IPV4;
+ }
+ return m_net;
}
uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
@@ -458,10 +674,10 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
}
std::vector<bool> ip_bits(128);
if (HasLinkedIPv4()) {
- // For lookup, treat as if it was just an IPv4 address (pchIPv4 prefix + IPv4 bits)
+ // For lookup, treat as if it was just an IPv4 address (IPV4_IN_IPV6_PREFIX + IPv4 bits)
for (int8_t byte_i = 0; byte_i < 12; ++byte_i) {
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
- ip_bits[byte_i * 8 + bit_i] = (pchIPv4[byte_i] >> (7 - bit_i)) & 1;
+ ip_bits[byte_i * 8 + bit_i] = (IPV4_IN_IPV6_PREFIX[byte_i] >> (7 - bit_i)) & 1;
}
}
uint32_t ipv4 = GetLinkedIPv4();
@@ -470,8 +686,9 @@ uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
}
} else {
// Use all 128 bits of the IPv6 address otherwise
+ assert(IsIPv6());
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
- uint8_t cur_byte = GetByte(15 - byte_i);
+ uint8_t cur_byte = m_addr[byte_i];
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
}
@@ -507,27 +724,22 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
}
vchRet.push_back(net_class);
- int nStartByte = 0;
- int nBits = 16;
+ int nBits{0};
if (IsLocal()) {
// all local addresses belong to the same group
- nBits = 0;
} else if (IsInternal()) {
// all internal-usage addresses get their own group
- nStartByte = sizeof(g_internal_prefix);
- nBits = (sizeof(ip) - sizeof(g_internal_prefix)) * 8;
+ nBits = ADDR_INTERNAL_SIZE * 8;
} else if (!IsRoutable()) {
// all other unroutable addresses belong to the same group
- nBits = 0;
} else if (HasLinkedIPv4()) {
// IPv4 addresses (and mapped IPv4 addresses) use /16 groups
uint32_t ipv4 = GetLinkedIPv4();
vchRet.push_back((ipv4 >> 24) & 0xFF);
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
- } else if (IsTor()) {
- nStartByte = 6;
+ } else if (IsTor() || IsI2P() || IsCJDNS()) {
nBits = 4;
} else if (IsHeNet()) {
// for he.net, use /36 groups
@@ -537,23 +749,32 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
nBits = 32;
}
- // push our ip onto vchRet byte by byte...
- while (nBits >= 8)
- {
- vchRet.push_back(GetByte(15 - nStartByte));
- nStartByte++;
- nBits -= 8;
- }
+ // Push our address onto vchRet.
+ const size_t num_bytes = nBits / 8;
+ vchRet.insert(vchRet.end(), m_addr.begin(), m_addr.begin() + num_bytes);
+ nBits %= 8;
// ...for the last byte, push nBits and for the rest of the byte push 1's
- if (nBits > 0)
- vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
+ if (nBits > 0) {
+ assert(num_bytes < m_addr.size());
+ vchRet.push_back(m_addr[num_bytes] | ((1 << (8 - nBits)) - 1));
+ }
return vchRet;
}
+std::vector<unsigned char> CNetAddr::GetAddrBytes() const
+{
+ if (IsAddrV1Compatible()) {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+ SerializeV1Array(serialized);
+ return {std::begin(serialized), std::end(serialized)};
+ }
+ return std::vector<unsigned char>(m_addr.begin(), m_addr.end());
+}
+
uint64_t CNetAddr::GetHash() const
{
- uint256 hash = Hash(ip);
+ uint256 hash = Hash(m_addr);
uint64_t nRet;
memcpy(&nRet, &hash, sizeof(nRet));
return nRet;
@@ -720,7 +941,7 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
memset(paddrin6, 0, *addrlen);
if (!GetIn6Addr(&paddrin6->sin6_addr))
return false;
- paddrin6->sin6_scope_id = scopeId;
+ paddrin6->sin6_scope_id = m_scope_id;
paddrin6->sin6_family = AF_INET6;
paddrin6->sin6_port = htons(port);
return true;
@@ -746,7 +967,7 @@ std::string CService::ToStringPort() const
std::string CService::ToStringIPPort() const
{
- if (IsIPv4() || IsTor() || IsInternal()) {
+ if (IsIPv4() || IsTor() || IsI2P() || IsInternal()) {
return ToStringIP() + ":" + ToStringPort();
} else {
return "[" + ToStringIP() + "]:" + ToStringPort();
@@ -764,53 +985,89 @@ CSubNet::CSubNet():
memset(netmask, 0, sizeof(netmask));
}
-CSubNet::CSubNet(const CNetAddr &addr, int32_t mask)
+CSubNet::CSubNet(const CNetAddr& addr, uint8_t mask) : CSubNet()
{
- valid = true;
+ valid = (addr.IsIPv4() && mask <= ADDR_IPV4_SIZE * 8) ||
+ (addr.IsIPv6() && mask <= ADDR_IPV6_SIZE * 8);
+ if (!valid) {
+ return;
+ }
+
+ assert(mask <= sizeof(netmask) * 8);
+
network = addr;
- // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
- memset(netmask, 255, sizeof(netmask));
-
- // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- const int astartofs = network.IsIPv4() ? 12 : 0;
-
- int32_t n = mask;
- if(n >= 0 && n <= (128 - astartofs*8)) // Only valid if in range of bits of address
- {
- n += astartofs*8;
- // Clear bits [n..127]
- for (; n < 128; ++n)
- netmask[n>>3] &= ~(1<<(7-(n&7)));
- } else
- valid = false;
- // Normalize network according to netmask
- for(int x=0; x<16; ++x)
- network.ip[x] &= netmask[x];
+ uint8_t n = mask;
+ for (size_t i = 0; i < network.m_addr.size(); ++i) {
+ const uint8_t bits = n < 8 ? n : 8;
+ netmask[i] = (uint8_t)((uint8_t)0xFF << (8 - bits)); // Set first bits.
+ network.m_addr[i] &= netmask[i]; // Normalize network according to netmask.
+ n -= bits;
+ }
}
-CSubNet::CSubNet(const CNetAddr &addr, const CNetAddr &mask)
+/**
+ * @returns The number of 1-bits in the prefix of the specified subnet mask. If
+ * the specified subnet mask is not a valid one, -1.
+ */
+static inline int NetmaskBits(uint8_t x)
{
- valid = true;
- network = addr;
- // Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
- memset(netmask, 255, sizeof(netmask));
+ switch(x) {
+ case 0x00: return 0;
+ case 0x80: return 1;
+ case 0xc0: return 2;
+ case 0xe0: return 3;
+ case 0xf0: return 4;
+ case 0xf8: return 5;
+ case 0xfc: return 6;
+ case 0xfe: return 7;
+ case 0xff: return 8;
+ default: return -1;
+ }
+}
+
+CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet()
+{
+ valid = (addr.IsIPv4() || addr.IsIPv6()) && addr.m_net == mask.m_net;
+ if (!valid) {
+ return;
+ }
+ // Check if `mask` contains 1-bits after 0-bits (which is an invalid netmask).
+ bool zeros_found = false;
+ for (auto b : mask.m_addr) {
+ const int num_bits = NetmaskBits(b);
+ if (num_bits == -1 || (zeros_found && num_bits != 0)) {
+ valid = false;
+ return;
+ }
+ if (num_bits < 8) {
+ zeros_found = true;
+ }
+ }
+
+ assert(mask.m_addr.size() <= sizeof(netmask));
- // IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
- const int astartofs = network.IsIPv4() ? 12 : 0;
+ memcpy(netmask, mask.m_addr.data(), mask.m_addr.size());
- for(int x=astartofs; x<16; ++x)
- netmask[x] = mask.ip[x];
+ network = addr;
// Normalize network according to netmask
- for(int x=0; x<16; ++x)
- network.ip[x] &= netmask[x];
+ for (size_t x = 0; x < network.m_addr.size(); ++x) {
+ network.m_addr[x] &= netmask[x];
+ }
}
-CSubNet::CSubNet(const CNetAddr &addr):
- valid(addr.IsValid())
+CSubNet::CSubNet(const CNetAddr& addr) : CSubNet()
{
- memset(netmask, 255, sizeof(netmask));
+ valid = addr.IsIPv4() || addr.IsIPv6();
+ if (!valid) {
+ return;
+ }
+
+ assert(addr.m_addr.size() <= sizeof(netmask));
+
+ memset(netmask, 0xFF, addr.m_addr.size());
+
network = addr;
}
@@ -822,68 +1079,29 @@ bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
return false;
- for(int x=0; x<16; ++x)
- if ((addr.ip[x] & netmask[x]) != network.ip[x])
+ assert(network.m_addr.size() == addr.m_addr.size());
+ for (size_t x = 0; x < addr.m_addr.size(); ++x) {
+ if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) {
return false;
- return true;
-}
-
-/**
- * @returns The number of 1-bits in the prefix of the specified subnet mask. If
- * the specified subnet mask is not a valid one, -1.
- */
-static inline int NetmaskBits(uint8_t x)
-{
- switch(x) {
- case 0x00: return 0;
- case 0x80: return 1;
- case 0xc0: return 2;
- case 0xe0: return 3;
- case 0xf0: return 4;
- case 0xf8: return 5;
- case 0xfc: return 6;
- case 0xfe: return 7;
- case 0xff: return 8;
- default: return -1;
+ }
}
+ return true;
}
std::string CSubNet::ToString() const
{
- /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */
- int cidr = 0;
- bool valid_cidr = true;
- int n = network.IsIPv4() ? 12 : 0;
- for (; n < 16 && netmask[n] == 0xff; ++n)
- cidr += 8;
- if (n < 16) {
- int bits = NetmaskBits(netmask[n]);
- if (bits < 0)
- valid_cidr = false;
- else
- cidr += bits;
- ++n;
- }
- for (; n < 16 && valid_cidr; ++n)
- if (netmask[n] != 0x00)
- valid_cidr = false;
-
- /* Format output */
- std::string strNetmask;
- if (valid_cidr) {
- strNetmask = strprintf("%u", cidr);
- } else {
- if (network.IsIPv4())
- strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
- else
- strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
- netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
- netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
- netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
+ assert(network.m_addr.size() <= sizeof(netmask));
+
+ uint8_t cidr = 0;
+
+ for (size_t i = 0; i < network.m_addr.size(); ++i) {
+ if (netmask[i] == 0x00) {
+ break;
+ }
+ cidr += NetmaskBits(netmask[i]);
}
- return network.ToString() + "/" + strNetmask;
+ return network.ToString() + strprintf("/%u", cidr);
}
bool CSubNet::IsValid() const
@@ -891,6 +1109,17 @@ bool CSubNet::IsValid() const
return valid;
}
+bool CSubNet::SanityCheck() const
+{
+ if (!(network.IsIPv4() || network.IsIPv6())) return false;
+
+ for (size_t x = 0; x < network.m_addr.size(); ++x) {
+ if (network.m_addr[x] & ~netmask[x]) return false;
+ }
+
+ return true;
+}
+
bool operator==(const CSubNet& a, const CSubNet& b)
{
return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
diff --git a/src/netaddress.h b/src/netaddress.h
index 0365907d44..f35b01d202 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -9,14 +9,29 @@
#include <config/bitcoin-config.h>
#endif
+#include <attributes.h>
#include <compat.h>
+#include <prevector.h>
#include <serialize.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+#include <array>
#include <cstdint>
+#include <ios>
#include <string>
#include <vector>
/**
+ * A flag that is ORed into the protocol version to designate that addresses
+ * should be serialized in (unserialized from) v2 format (BIP155).
+ * Make sure that this does not collide with any of the values in `version.h`
+ * or with `SERIALIZE_TRANSACTION_NO_WITNESS`.
+ */
+static const int ADDRV2_FORMAT = 0x20000000;
+
+/**
* A network type.
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
@@ -36,19 +51,68 @@ enum Network
/// IPv6
NET_IPV6,
- /// TORv2
+ /// TOR (v2 or v3)
NET_ONION,
- /// A set of dummy addresses that map a name to an IPv6 address. These
- /// addresses belong to RFC4193's fc00::/7 subnet (unique-local addresses).
- /// We use them to map a string or FQDN to an IPv6 address in CAddrMan to
- /// keep track of which DNS seeds were used.
+ /// I2P
+ NET_I2P,
+
+ /// CJDNS
+ NET_CJDNS,
+
+ /// A set of addresses that represent the hash of a string or FQDN. We use
+ /// them in CAddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
NET_MAX,
};
+/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
+};
+
+/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
+ 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
+};
+
+/// Prefix of an IPv6 address when it contains an embedded "internal" address.
+/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
+/// The prefix comes from 0xFD + SHA256("bitcoin")[0:5].
+/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
+/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
+ 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
+};
+
+/// Size of IPv4 address (in bytes).
+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;
+
+/// Size of I2P address (in bytes).
+static constexpr size_t ADDR_I2P_SIZE = 32;
+
+/// Size of CJDNS address (in bytes).
+static constexpr size_t ADDR_CJDNS_SIZE = 16;
+
+/// Size of "internal" (NET_INTERNAL) address (in bytes).
+static constexpr size_t ADDR_INTERNAL_SIZE = 10;
+
/**
* Network address.
*/
@@ -56,12 +120,21 @@ class CNetAddr
{
protected:
/**
+ * Raw representation of the network address.
+ * In network byte order (big endian) for IPv4 and IPv6.
+ */
+ prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
+
+ /**
* Network to which this address belongs.
*/
Network m_net{NET_IPV6};
- unsigned char ip[16]; // in network byte order
- uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
+ /**
+ * Scope id if scoped/link-local IPV6 address.
+ * See https://tools.ietf.org/html/rfc4007
+ */
+ uint32_t m_scope_id{0};
public:
CNetAddr();
@@ -74,13 +147,7 @@ class CNetAddr
* (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
* `addr` encoding.
*/
- void SetLegacyIPv6(const uint8_t ipv6[16]);
-
- /**
- * Set raw IPv4 or IPv6 address (in network byte order)
- * @note Only NET_IPV4 and NET_IPV6 are allowed for network.
- */
- void SetRaw(Network network, const uint8_t *data);
+ void SetLegacyIPv6(Span<const uint8_t> ipv6);
bool SetInternal(const std::string& name);
@@ -104,17 +171,24 @@ class CNetAddr
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
bool IsTor() const;
+ bool IsI2P() const;
+ bool IsCJDNS() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
bool IsValid() const;
+
+ /**
+ * Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
+ */
+ bool IsAddrV1Compatible() const;
+
enum Network GetNetwork() const;
std::string ToString() const;
std::string ToStringIP() const;
- unsigned int GetByte(int n) const;
uint64_t GetHash() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
- uint32_t GetNetClass() const;
+ Network GetNetClass() const;
//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
uint32_t GetLinkedIPv4() const;
@@ -127,7 +201,7 @@ class CNetAddr
uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
- std::vector<unsigned char> GetAddrBytes() const { return {std::begin(ip), std::end(ip)}; }
+ std::vector<unsigned char> GetAddrBytes() const;
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
@@ -138,12 +212,24 @@ class CNetAddr
friend bool operator<(const CNetAddr& a, const CNetAddr& b);
/**
+ * Whether this address should be relayed to other peers even if we can't reach it ourselves.
+ */
+ bool IsRelayable() const
+ {
+ return IsIPv4() || IsIPv6() || IsTor();
+ }
+
+ /**
* Serialize to a stream.
*/
template <typename Stream>
void Serialize(Stream& s) const
{
- s << ip;
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ SerializeV2Stream(s);
+ } else {
+ SerializeV1Stream(s);
+ }
}
/**
@@ -152,14 +238,215 @@ class CNetAddr
template <typename Stream>
void Unserialize(Stream& s)
{
- unsigned char ip_temp[sizeof(ip)];
- s >> ip_temp;
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ UnserializeV2Stream(s);
+ } else {
+ UnserializeV1Stream(s);
+ }
+ }
+
+ friend class CSubNet;
+
+ private:
+ /**
+ * BIP155 network ids recognized by this software.
+ */
+ enum BIP155Network : uint8_t {
+ IPV4 = 1,
+ IPV6 = 2,
+ TORV2 = 3,
+ TORV3 = 4,
+ I2P = 5,
+ CJDNS = 6,
+ };
+
+ /**
+ * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
+ */
+ static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
+
+ /**
+ * Maximum size of an address as defined in BIP155 (in bytes).
+ * This is only the size of the address, not the entire CNetAddr object
+ * when serialized.
+ */
+ static constexpr size_t MAX_ADDRV2_SIZE = 512;
+
+ /**
+ * Get the BIP155 network id of this address.
+ * Must not be called for IsInternal() objects.
+ * @returns BIP155 network id
+ */
+ BIP155Network GetBIP155Network() const;
+
+ /**
+ * Set `m_net` from the provided BIP155 network id and size after validation.
+ * @retval true the network was recognized, is valid and `m_net` was set
+ * @retval false not recognised (from future?) and should be silently ignored
+ * @throws std::ios_base::failure if the network is one of the BIP155 founding
+ * networks (id 1..6) with wrong address size.
+ */
+ bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to an array.
+ */
+ void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
+ {
+ size_t prefix_size;
+
+ switch (m_net) {
+ case NET_IPV6:
+ assert(m_addr.size() == sizeof(arr));
+ memcpy(arr, m_addr.data(), m_addr.size());
+ return;
+ case NET_IPV4:
+ prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ 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_I2P:
+ break;
+ case NET_CJDNS:
+ break;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ // Serialize TORv3, I2P and CJDNS as all-zeros.
+ memset(arr, 0x0, V1_SERIALIZATION_SIZE);
+ }
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to a stream.
+ */
+ template <typename Stream>
+ void SerializeV1Stream(Stream& s) const
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ SerializeV1Array(serialized);
+
+ s << serialized;
+ }
+
+ /**
+ * Serialize as ADDRv2 / BIP155.
+ */
+ template <typename Stream>
+ void SerializeV2Stream(Stream& s) const
+ {
+ if (IsInternal()) {
+ // Serialize NET_INTERNAL as embedded in IPv6. We need to
+ // serialize such addresses from addrman.
+ s << static_cast<uint8_t>(BIP155Network::IPV6);
+ s << COMPACTSIZE(ADDR_IPV6_SIZE);
+ SerializeV1Stream(s);
+ return;
+ }
+
+ s << static_cast<uint8_t>(GetBIP155Network());
+ s << m_addr;
+ }
+
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from an array.
+ */
+ void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
+ {
// Use SetLegacyIPv6() so that m_net is set correctly. For example
// ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
- SetLegacyIPv6(ip_temp);
+ SetLegacyIPv6(arr);
}
- friend class CSubNet;
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from a stream.
+ */
+ template <typename Stream>
+ void UnserializeV1Stream(Stream& s)
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
+
+ s >> serialized;
+
+ UnserializeV1Array(serialized);
+ }
+
+ /**
+ * Unserialize from a ADDRv2 / BIP155 format.
+ */
+ template <typename Stream>
+ void UnserializeV2Stream(Stream& s)
+ {
+ uint8_t bip155_net;
+ s >> bip155_net;
+
+ size_t address_size;
+ s >> COMPACTSIZE(address_size);
+
+ if (address_size > MAX_ADDRV2_SIZE) {
+ throw std::ios_base::failure(strprintf(
+ "Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
+ }
+
+ m_scope_id = 0;
+
+ if (SetNetFromBIP155Network(bip155_net, address_size)) {
+ m_addr.resize(address_size);
+ s >> MakeSpan(m_addr);
+
+ if (m_net != NET_IPV6) {
+ return;
+ }
+
+ // Do some special checks on IPv6 addresses.
+
+ // Recognize NET_INTERNAL embedded in IPv6, such addresses are not
+ // gossiped but could be coming from addrman, when unserializing from
+ // disk.
+ if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
+ m_net = NET_INTERNAL;
+ memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
+ ADDR_INTERNAL_SIZE);
+ m_addr.resize(ADDR_INTERNAL_SIZE);
+ return;
+ }
+
+ if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
+ !HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
+ return;
+ }
+
+ // IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
+ // encoding). Unserialize as !IsValid(), thus ignoring them.
+ } else {
+ // If we receive an unknown BIP155 network id (from the future?) then
+ // ignore the address - unserialize as !IsValid().
+ s.ignore(address_size);
+ }
+
+ // 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);
+ }
};
class CSubNet
@@ -172,13 +459,15 @@ class CSubNet
/// Is this value valid? (only used to signal parse errors)
bool valid;
+ bool SanityCheck() const;
+
public:
CSubNet();
- CSubNet(const CNetAddr &addr, int32_t mask);
- CSubNet(const CNetAddr &addr, const CNetAddr &mask);
+ CSubNet(const CNetAddr& addr, uint8_t mask);
+ CSubNet(const CNetAddr& addr, const CNetAddr& mask);
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
- explicit CSubNet(const CNetAddr &addr);
+ explicit CSubNet(const CNetAddr& addr);
bool Match(const CNetAddr &addr) const;
@@ -189,7 +478,23 @@ class CSubNet
friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
friend bool operator<(const CSubNet& a, const CSubNet& b);
- SERIALIZE_METHODS(CSubNet, obj) { READWRITE(obj.network, obj.netmask, obj.valid); }
+ SERIALIZE_METHODS(CSubNet, obj)
+ {
+ READWRITE(obj.network);
+ if (obj.network.IsIPv4()) {
+ // Before commit 102867c587f5f7954232fb8ed8e85cda78bb4d32, CSubNet used the last 4 bytes of netmask
+ // to store the relevant bytes for an IPv4 mask. For compatiblity reasons, keep doing so in
+ // serialized form.
+ unsigned char dummy[12] = {0};
+ READWRITE(dummy);
+ READWRITE(MakeSpan(obj.netmask).first(4));
+ } else {
+ READWRITE(obj.netmask);
+ }
+ READWRITE(obj.valid);
+ // Mark invalid if the result doesn't pass sanity checking.
+ SER_READ(obj, if (obj.valid) obj.valid = obj.SanityCheck());
+ }
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 3a3b5f3e66..0273839017 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cstdint>
+#include <limits>
#ifndef WIN32
#include <fcntl.h>
@@ -838,8 +839,8 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
if (slash != strSubnet.npos)
{
std::string strNetmask = strSubnet.substr(slash + 1);
- int32_t n;
- if (ParseInt32(strNetmask, &n)) {
+ uint8_t n;
+ if (ParseUInt8(strNetmask, &n)) {
// If valid number, assume CIDR variable-length subnet masking
ret = CSubNet(network, n);
return ret.IsValid();
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 0238aab0d9..49d0c37235 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -9,6 +9,7 @@
#include <net.h>
#include <net_processing.h>
#include <scheduler.h>
+#include <txmempool.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index be568cba36..3228831ed1 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -16,10 +16,11 @@ class CConnman;
class CScheduler;
class CTxMemPool;
class ChainstateManager;
-class PeerLogicValidation;
+class PeerManager;
namespace interfaces {
class Chain;
class ChainClient;
+class WalletClient;
} // namespace interfaces
//! NodeContext struct containing references to chain state and connection
@@ -34,13 +35,17 @@ class ChainClient;
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
std::unique_ptr<CConnman> connman;
- CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
- std::unique_ptr<PeerLogicValidation> peer_logic;
+ std::unique_ptr<CTxMemPool> mempool;
+ std::unique_ptr<PeerManager> peerman;
ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
+ //! List of all chain clients (wallet processes or other client) connected to node.
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
+ //! Reference to chain client that should used to load or create wallets
+ //! opened by the gui.
+ interfaces::WalletClient* wallet_client{nullptr};
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 5633abe817..97d5aad8e4 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -13,6 +13,19 @@
#include <future>
+static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out)
+{
+ err_string_out = state.ToString();
+ if (state.IsInvalid()) {
+ if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ return TransactionError::MEMPOOL_REJECTED;
+ } else {
+ return TransactionError::MEMPOOL_ERROR;
+ }
+}
+
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
{
// BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
@@ -36,20 +49,24 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
}
if (!node.mempool->exists(hashTx)) {
- // Transaction is not already in the mempool. Submit it.
+ // Transaction is not already in the mempool.
TxValidationState state;
- if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
- err_string = state.ToString();
- if (state.IsInvalid()) {
- if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
- return TransactionError::MISSING_INPUTS;
- }
- return TransactionError::MEMPOOL_REJECTED;
- } else {
- return TransactionError::MEMPOOL_ERROR;
+ if (max_tx_fee > 0) {
+ // First, call ATMP with test_accept and check the fee. If ATMP
+ // fails here, return error immediately.
+ CAmount fee{0};
+ if (!AcceptToMemoryPool(*node.mempool, state, tx,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
+ return HandleATMPError(state, err_string);
+ } else if (fee > max_tx_fee) {
+ return TransactionError::MAX_FEE_EXCEEDED;
}
}
+ // Try to submit the transaction to the mempool.
+ if (!AcceptToMemoryPool(*node.mempool, state, tx,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
+ return HandleATMPError(state, err_string);
+ }
// Transaction was accepted to the mempool.
@@ -80,7 +97,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (relay) {
// the mempool tracks locally submitted transactions to make a
// best-effort of initial broadcast
- node.mempool->AddUnbroadcastTx(hashTx, tx->GetWitnessHash());
+ node.mempool->AddUnbroadcastTx(hashTx);
LOCK(cs_main);
RelayTransaction(hashTx, tx->GetWitnessHash(), *node.connman);
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 25458eead2..0f31093dbb 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -55,7 +55,7 @@ private:
// Sum the total feerate of all tx's in each bucket
// Track the historical moving average of this total over blocks
- std::vector<double> avg;
+ std::vector<double> m_feerate_avg;
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
// Combine the total value with the tx counts to calculate the avg feerate per bucket
@@ -114,12 +114,10 @@ public:
* @param confTarget target number of confirmations
* @param sufficientTxVal required average number of transactions per block in a bucket range
* @param minSuccess the success probability we require
- * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
- * return the highest feerate such that all lower values fail minSuccess
* @param nBlockHeight the current block height
*/
double EstimateMedianVal(int confTarget, double sufficientTxVal,
- double minSuccess, bool requireGreater, unsigned int nBlockHeight,
+ double minSuccess, unsigned int nBlockHeight,
EstimationResult *result = nullptr) const;
/** Return the max number of confirms we're tracking */
@@ -139,22 +137,18 @@ public:
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
const std::map<double, unsigned int>& defaultBucketMap,
unsigned int maxPeriods, double _decay, unsigned int _scale)
- : buckets(defaultBuckets), bucketMap(defaultBucketMap)
+ : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
{
- decay = _decay;
assert(_scale != 0 && "_scale must be non-zero");
- scale = _scale;
confAvg.resize(maxPeriods);
- for (unsigned int i = 0; i < maxPeriods; i++) {
- confAvg[i].resize(buckets.size());
- }
failAvg.resize(maxPeriods);
for (unsigned int i = 0; i < maxPeriods; i++) {
+ confAvg[i].resize(buckets.size());
failAvg[i].resize(buckets.size());
}
txCtAvg.resize(buckets.size());
- avg.resize(buckets.size());
+ m_feerate_avg.resize(buckets.size());
resizeInMemoryCounters(buckets.size());
}
@@ -172,68 +166,61 @@ void TxConfirmStats::resizeInMemoryCounters(size_t newbuckets) {
void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
{
for (unsigned int j = 0; j < buckets.size(); j++) {
- oldUnconfTxs[j] += unconfTxs[nBlockHeight%unconfTxs.size()][j];
+ oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
}
}
-void TxConfirmStats::Record(int blocksToConfirm, double val)
+void TxConfirmStats::Record(int blocksToConfirm, double feerate)
{
// blocksToConfirm is 1-based
if (blocksToConfirm < 1)
return;
- int periodsToConfirm = (blocksToConfirm + scale - 1)/scale;
- unsigned int bucketindex = bucketMap.lower_bound(val)->second;
+ int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
+ unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
confAvg[i - 1][bucketindex]++;
}
txCtAvg[bucketindex]++;
- avg[bucketindex] += val;
+ m_feerate_avg[bucketindex] += feerate;
}
void TxConfirmStats::UpdateMovingAverages()
{
+ assert(confAvg.size() == failAvg.size());
for (unsigned int j = 0; j < buckets.size(); j++) {
- for (unsigned int i = 0; i < confAvg.size(); i++)
- confAvg[i][j] = confAvg[i][j] * decay;
- for (unsigned int i = 0; i < failAvg.size(); i++)
- failAvg[i][j] = failAvg[i][j] * decay;
- avg[j] = avg[j] * decay;
- txCtAvg[j] = txCtAvg[j] * decay;
+ for (unsigned int i = 0; i < confAvg.size(); i++) {
+ confAvg[i][j] *= decay;
+ failAvg[i][j] *= decay;
+ }
+ m_feerate_avg[j] *= decay;
+ txCtAvg[j] *= decay;
}
}
// returns -1 on error conditions
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
- double successBreakPoint, bool requireGreater,
- unsigned int nBlockHeight, EstimationResult *result) const
+ double successBreakPoint, unsigned int nBlockHeight,
+ EstimationResult *result) const
{
// Counters for a bucket (or range of buckets)
double nConf = 0; // Number of tx's confirmed within the confTarget
double totalNum = 0; // Total number of tx's that were ever confirmed
int extraNum = 0; // Number of tx's still in mempool for confTarget or longer
double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
- int periodTarget = (confTarget + scale - 1)/scale;
-
- int maxbucketindex = buckets.size() - 1;
-
- // requireGreater means we are looking for the lowest feerate such that all higher
- // values pass, so we start at maxbucketindex (highest feerate) and look at successively
- // smaller buckets until we reach failure. Otherwise, we are looking for the highest
- // feerate such that all lower values fail, and we go in the opposite direction.
- unsigned int startbucket = requireGreater ? maxbucketindex : 0;
- int step = requireGreater ? -1 : 1;
+ const int periodTarget = (confTarget + scale - 1) / scale;
+ const int maxbucketindex = buckets.size() - 1;
// We'll combine buckets until we have enough samples.
// The near and far variables will define the range we've combined
// The best variables are the last range we saw which still had a high
// enough confirmation rate to count as success.
// The cur variables are the current range we're counting.
- unsigned int curNearBucket = startbucket;
- unsigned int bestNearBucket = startbucket;
- unsigned int curFarBucket = startbucket;
- unsigned int bestFarBucket = startbucket;
+ unsigned int curNearBucket = maxbucketindex;
+ unsigned int bestNearBucket = maxbucketindex;
+ unsigned int curFarBucket = maxbucketindex;
+ unsigned int bestFarBucket = maxbucketindex;
bool foundAnswer = false;
unsigned int bins = unconfTxs.size();
@@ -242,8 +229,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
EstimatorBucket passBucket;
EstimatorBucket failBucket;
- // Start counting from highest(default) or lowest feerate transactions
- for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) {
+ // Start counting from highest feerate transactions
+ for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
if (newBucketRange) {
curNearBucket = bucket;
newBucketRange = false;
@@ -253,7 +240,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
totalNum += txCtAvg[bucket];
failNum += failAvg[periodTarget - 1][bucket];
for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
- extraNum += unconfTxs[(nBlockHeight - confct)%bins][bucket];
+ extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
extraNum += oldUnconfTxs[bucket];
// If we have enough transaction data points in this range of buckets,
// we can test for success
@@ -263,7 +250,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
double curPct = nConf / (totalNum + failNum + extraNum);
// Check to see if we are no longer getting confirmed at the success rate
- if ((requireGreater && curPct < successBreakPoint) || (!requireGreater && curPct > successBreakPoint)) {
+ if (curPct < successBreakPoint) {
if (passing == true) {
// First time we hit a failure record the failed bucket
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
@@ -317,7 +304,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
if (txCtAvg[j] < txSum)
txSum -= txCtAvg[j];
else { // we're in the right bucket
- median = avg[j] / txCtAvg[j];
+ median = m_feerate_avg[j] / txCtAvg[j];
break;
}
}
@@ -338,13 +325,22 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
failBucket.leftMempool = failNum;
}
- LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d %s%.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
- confTarget, requireGreater ? ">" : "<", 100.0 * successBreakPoint, decay,
+ float passed_within_target_perc = 0.0;
+ float failed_within_target_perc = 0.0;
+ if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
+ passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
+ }
+ if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
+ failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
+ }
+
+ LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d > %.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
+ confTarget, 100.0 * successBreakPoint, decay,
median, passBucket.start, passBucket.end,
- 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool),
+ passed_within_target_perc,
passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
failBucket.start, failBucket.end,
- 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool),
+ failed_within_target_perc,
failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
@@ -361,7 +357,7 @@ void TxConfirmStats::Write(CAutoFile& fileout) const
{
fileout << decay;
fileout << scale;
- fileout << avg;
+ fileout << m_feerate_avg;
fileout << txCtAvg;
fileout << confAvg;
fileout << failAvg;
@@ -384,8 +380,8 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
}
- filein >> avg;
- if (avg.size() != numBuckets) {
+ filein >> m_feerate_avg;
+ if (m_feerate_avg.size() != numBuckets) {
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
}
filein >> txCtAvg;
@@ -664,7 +660,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
if (successThreshold > 1)
return CFeeRate(0);
- double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, true, nBestSeenHeight, result);
+ double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
if (median < 0)
return CFeeRate(0);
@@ -725,26 +721,26 @@ double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, doubl
if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
// Find estimate from shortest time horizon possible
if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
- estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, result);
+ estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
}
else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
- estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
+ estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
}
else { // long horizon
- estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
+ estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
}
if (checkShorterHorizon) {
EstimationResult tempResult;
// If a lower confTarget from a more recent horizon returns a lower answer use it.
if (confTarget > feeStats->GetMaxConfirms()) {
- double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, &tempResult);
+ double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
estimate = medMax;
if (result) *result = tempResult;
}
}
if (confTarget > shortStats->GetMaxConfirms()) {
- double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, &tempResult);
+ double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
estimate = shortMax;
if (result) *result = tempResult;
@@ -763,10 +759,10 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget,
double estimate = -1;
EstimationResult tempResult;
if (doubleTarget <= shortStats->GetMaxConfirms()) {
- estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, result);
+ estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
}
if (doubleTarget <= feeStats->GetMaxConfirms()) {
- double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, &tempResult);
+ double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
if (longEstimate > estimate) {
estimate = longEstimate;
if (result) *result = tempResult;
diff --git a/src/policy/fees.h b/src/policy/fees.h
index e79dbc9868..8ea8816dc3 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -138,9 +138,9 @@ private:
/** Decay of .962 is a half-life of 18 blocks or about 3 hours */
static constexpr double SHORT_DECAY = .962;
- /** Decay of .998 is a half-life of 144 blocks or about 1 day */
+ /** Decay of .9952 is a half-life of 144 blocks or about 1 day */
static constexpr double MED_DECAY = .9952;
- /** Decay of .9995 is a half-life of 1008 blocks or about 1 week */
+ /** Decay of .99931 is a half-life of 1008 blocks or about 1 week */
static constexpr double LONG_DECAY = .99931;
/** Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks*/
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 0e9820da1e..69f2b456f1 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -9,7 +9,7 @@
#include <consensus/validation.h>
#include <coins.h>
-
+#include <span.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
@@ -206,6 +206,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
// get the scriptPubKey corresponding to this input:
CScript prevScript = prev.scriptPubKey;
+ bool p2sh = false;
if (prevScript.IsPayToScriptHash()) {
std::vector <std::vector<unsigned char> > stack;
// If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig
@@ -216,6 +217,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
if (stack.empty())
return false;
prevScript = CScript(stack.back().begin(), stack.back().end());
+ p2sh = true;
}
int witnessversion = 0;
@@ -237,6 +239,36 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return false;
}
}
+
+ // Check policy limits for Taproot spends:
+ // - MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE limit for stack item size
+ // - No annexes
+ if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) {
+ // Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
+ auto stack = MakeSpan(tx.vin[i].scriptWitness.stack);
+ if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
+ // Annexes are nonstandard as long as no semantics are defined for them.
+ return false;
+ }
+ if (stack.size() >= 2) {
+ // Script path spend (2 or more stack elements after removing optional annex)
+ const auto& control_block = SpanPopBack(stack);
+ SpanPopBack(stack); // Ignore script
+ if (control_block.empty()) return false; // Empty control block is invalid
+ if ((control_block[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
+ // Leaf version 0xc0 (aka Tapscript, see BIP 342)
+ for (const auto& item : stack) {
+ if (item.size() > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) return false;
+ }
+ }
+ } else if (stack.size() == 1) {
+ // Key path spend (1 stack element after removing optional annex)
+ // (no policy rules apply)
+ } else {
+ // 0 stack elements; this is already invalid by consensus rules
+ return false;
+ }
+ }
}
return true;
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 7f168ee20f..51d67b9390 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -40,6 +40,8 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
/** The maximum size of each witness stack item in a standard P2WSH script */
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
+/** The maximum size of each witness stack item in a standard BIP 342 script (Taproot, leaf version 0xc0) */
+static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
/** The maximum size of a standard witnessScript */
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
/** Min feerate for defining dust. Historically this has been based on the
@@ -68,7 +70,11 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
SCRIPT_VERIFY_WITNESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
- SCRIPT_VERIFY_CONST_SCRIPTCODE;
+ SCRIPT_VERIFY_CONST_SCRIPTCODE |
+ SCRIPT_VERIFY_TAPROOT |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
+ SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
/** For convenience, standard but not mandatory verify flags. */
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index f8b17d18d5..4b55934891 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -36,3 +36,9 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
}
return RBFTransactionState::FINAL;
}
+
+RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
+{
+ // If we don't have a local mempool we can only check the transaction itself.
+ return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
+}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index d335fbbb36..f84e6e5286 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -7,16 +7,28 @@
#include <txmempool.h>
+/** The rbf state of unconfirmed transactions */
enum class RBFTransactionState {
+ /** Unconfirmed tx that does not signal rbf and is not in the mempool */
UNKNOWN,
+ /** Either this tx or a mempool ancestor signals rbf */
REPLACEABLE_BIP125,
- FINAL
+ /** Neither this tx nor a mempool ancestor signals rbf */
+ FINAL,
};
-// Determine whether an in-mempool transaction is signaling opt-in to RBF
-// according to BIP 125
-// This involves checking sequence numbers of the transaction, as well
-// as the sequence numbers of all in-mempool ancestors.
+/**
+ * Determine whether an unconfirmed transaction is signaling opt-in to RBF
+ * according to BIP 125
+ * This involves checking sequence numbers of the transaction, as well
+ * as the sequence numbers of all in-mempool ancestors.
+ *
+ * @param tx The unconfirmed transaction
+ * @param pool The mempool, which may contain the tx
+ *
+ * @return The rbf state
+ */
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 544bab6d9b..00544f64fe 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -14,6 +14,12 @@
#include <tuple>
+/**
+ * A flag that is ORed into the protocol version to designate that a transaction
+ * should be (un)serialized without witness data.
+ * Make sure that this does not collide with any of the values in `version.h`
+ * or with `ADDRV2_FORMAT`.
+ */
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
/** An outpoint - a combination of a transaction hash and an index n into its vout */
@@ -393,8 +399,8 @@ template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txI
/** A generic txid reference (txid or wtxid). */
class GenTxid
{
- const bool m_is_wtxid;
- const uint256 m_hash;
+ bool m_is_wtxid;
+ uint256 m_hash;
public:
GenTxid(bool is_wtxid, const uint256& hash) : m_is_wtxid(is_wtxid), m_hash(hash) {}
bool IsWtxid() const { return m_is_wtxid; }
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 5a91acee0f..dc8f795a0c 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -8,16 +8,14 @@
#include <util/strencodings.h>
#include <util/system.h>
-#ifndef WIN32
-# include <arpa/inet.h>
-#endif
-
static std::atomic<bool> g_initial_block_download_completed(false);
namespace NetMsgType {
const char *VERSION="version";
const char *VERACK="verack";
const char *ADDR="addr";
+const char *ADDRV2="addrv2";
+const char *SENDADDRV2="sendaddrv2";
const char *INV="inv";
const char *GETDATA="getdata";
const char *MERKLEBLOCK="merkleblock";
@@ -56,6 +54,8 @@ const static std::string allNetMessageTypes[] = {
NetMsgType::VERSION,
NetMsgType::VERACK,
NetMsgType::ADDR,
+ NetMsgType::ADDRV2,
+ NetMsgType::SENDADDRV2,
NetMsgType::INV,
NetMsgType::GETDATA,
NetMsgType::MERKLEBLOCK,
@@ -88,9 +88,9 @@ const static std::string allNetMessageTypes[] = {
};
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
-CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
+CMessageHeader::CMessageHeader()
{
- memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
+ memset(pchMessageStart, 0, MESSAGE_START_SIZE);
memset(pchCommand, 0, sizeof(pchCommand));
nMessageSize = -1;
memset(pchChecksum, 0, CHECKSUM_SIZE);
@@ -115,31 +115,20 @@ std::string CMessageHeader::GetCommand() const
return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE));
}
-bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const
+bool CMessageHeader::IsCommandValid() const
{
- // Check start string
- if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0)
- return false;
-
// Check the command string for errors
- for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)
- {
- if (*p1 == 0)
- {
+ for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; ++p1) {
+ if (*p1 == 0) {
// Must be all zeros after the first zero
- for (; p1 < pchCommand + COMMAND_SIZE; p1++)
- if (*p1 != 0)
+ for (; p1 < pchCommand + COMMAND_SIZE; ++p1) {
+ if (*p1 != 0) {
return false;
- }
- else if (*p1 < ' ' || *p1 > 0x7E)
+ }
+ }
+ } else if (*p1 < ' ' || *p1 > 0x7E) {
return false;
- }
-
- // Message size
- if (nMessageSize > MAX_SIZE)
- {
- LogPrintf("CMessageHeader::IsValid(): (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand(), nMessageSize);
- return false;
+ }
}
return true;
@@ -163,7 +152,7 @@ CInv::CInv()
hash.SetNull();
}
-CInv::CInv(int typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {}
+CInv::CInv(uint32_t typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {}
bool operator<(const CInv& a, const CInv& b)
{
@@ -217,6 +206,7 @@ static std::string serviceFlagToStr(size_t bit)
case NODE_GETUTXO: return "GETUTXO";
case NODE_BLOOM: return "BLOOM";
case NODE_WITNESS: return "WITNESS";
+ case NODE_COMPACT_FILTERS: return "COMPACT_FILTERS";
case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
// Not using default, so we get warned when a case is missing
}
diff --git a/src/protocol.h b/src/protocol.h
index 1d0adaae6e..309fac621c 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -37,7 +37,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(const MessageStartChars& pchMessageStartIn);
+ explicit CMessageHeader();
/** 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.
@@ -45,7 +45,7 @@ public:
CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn);
std::string GetCommand() const;
- bool IsValid(const MessageStartChars& messageStart) const;
+ bool IsCommandValid() const;
SERIALIZE_METHODS(CMessageHeader, obj) { READWRITE(obj.pchMessageStart, obj.pchCommand, obj.nMessageSize, obj.pchChecksum); }
@@ -77,6 +77,18 @@ extern const char* VERACK;
*/
extern const char* ADDR;
/**
+ * The addrv2 message relays connection information for peers on the network just
+ * like the addr message, but is extended to allow gossiping of longer node
+ * addresses (see BIP155).
+ */
+extern const char *ADDRV2;
+/**
+ * The sendaddrv2 message signals support for receiving ADDRV2 messages (BIP155).
+ * It also implies that its sender can encode as ADDRV2 and would send ADDRV2
+ * instead of ADDR to a peer that has signaled ADDRV2 support by sending SENDADDRV2.
+ */
+extern const char *SENDADDRV2;
+/**
* The inv message (inventory message) transmits one or more inventories of
* objects known to the transmitting peer.
*/
@@ -247,7 +259,7 @@ extern const char* CFCHECKPT;
* txid.
* @since protocol version 70016 as described by BIP 339.
*/
-extern const char *WTXIDRELAY;
+extern const char* WTXIDRELAY;
}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
@@ -272,6 +284,9 @@ enum ServiceFlags : uint64_t {
// NODE_WITNESS indicates that a node can be asked for blocks and transactions including
// witness data.
NODE_WITNESS = (1 << 3),
+ // NODE_COMPACT_FILTERS means the node will service basic block filter requests.
+ // See BIP157 and BIP158 for details on how this is implemented.
+ NODE_COMPACT_FILTERS = (1 << 6),
// NODE_NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only
// serving the last 288 (2 day) blocks
// See BIP159 for details on how this is implemented.
@@ -348,7 +363,8 @@ class CAddress : public CService
public:
CAddress() : CService{} {};
- explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
+ CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
+ CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nTime{nTimeIn}, nServices{nServicesIn} {};
SERIALIZE_METHODS(CAddress, obj)
{
@@ -367,13 +383,21 @@ public:
// nTime.
READWRITE(obj.nTime);
}
- READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
+ if (nVersion & ADDRV2_FORMAT) {
+ uint64_t services_tmp;
+ SER_WRITE(obj, services_tmp = obj.nServices);
+ READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
+ SER_READ(obj, obj.nServices = static_cast<ServiceFlags>(services_tmp));
+ } else {
+ READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
+ }
READWRITEAS(CService, obj);
}
- ServiceFlags nServices{NODE_NONE};
// disk and network only
uint32_t nTime{TIME_INIT};
+
+ ServiceFlags nServices{NODE_NONE};
};
/** getdata message type flags */
@@ -394,7 +418,9 @@ enum GetDataMsg : uint32_t {
MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, //!< Defined in BIP144
MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, //!< Defined in BIP144
- MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
+ // MSG_FILTERED_WITNESS_BLOCK is defined in BIP144 as reserved for future
+ // use and remains unused.
+ // MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
};
/** inv message data */
@@ -402,7 +428,7 @@ class CInv
{
public:
CInv();
- CInv(int typeIn, const uint256& hashIn);
+ CInv(uint32_t typeIn, const uint256& hashIn);
SERIALIZE_METHODS(CInv, obj) { READWRITE(obj.type, obj.hash); }
@@ -412,14 +438,24 @@ public:
std::string ToString() const;
// Single-message helper methods
- bool IsMsgTx() const { return type == MSG_TX; }
- bool IsMsgWtx() const { return type == MSG_WTX; }
- bool IsMsgWitnessTx() const { return type == MSG_WITNESS_TX; }
+ bool IsMsgTx() const { return type == MSG_TX; }
+ bool IsMsgBlk() const { return type == MSG_BLOCK; }
+ bool IsMsgWtx() const { return type == MSG_WTX; }
+ bool IsMsgFilteredBlk() const { return type == MSG_FILTERED_BLOCK; }
+ bool IsMsgCmpctBlk() const { return type == MSG_CMPCT_BLOCK; }
+ bool IsMsgWitnessBlk() const { return type == MSG_WITNESS_BLOCK; }
// Combined-message helper methods
- bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
+ bool IsGenTxMsg() const
+ {
+ return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX;
+ }
+ bool IsGenBlkMsg() const
+ {
+ return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK;
+ }
- int type;
+ uint32_t type;
uint256 hash;
};
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index ef42aa5bc7..4d734fc891 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -7,6 +7,7 @@
#include <secp256k1.h>
#include <secp256k1_recovery.h>
+#include <secp256k1_schnorrsig.h>
namespace
{
@@ -24,7 +25,7 @@ secp256k1_context* secp256k1_context_verify = nullptr;
* strict DER before being passed to this module, and we know it supports all
* violations present in the blockchain before that point.
*/
-static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
@@ -166,6 +167,27 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1
return 1;
}
+XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
+{
+ assert(bytes.size() == 32);
+ std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
+}
+
+bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
+{
+ assert(sigbytes.size() == 64);
+ secp256k1_xonly_pubkey pubkey;
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data())) return false;
+ return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
+}
+
+bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) 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());
+}
+
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
diff --git a/src/pubkey.h b/src/pubkey.h
index fcbc7e8416..0f784b86e4 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -9,6 +9,7 @@
#include <hash.h>
#include <serialize.h>
+#include <span.h>
#include <uint256.h>
#include <stdexcept>
@@ -169,7 +170,7 @@ public:
/*
* Check syntactic correctness.
*
- * Note that this is consensus critical as CheckSig() calls it!
+ * Note that this is consensus critical as CheckECDSASignature() calls it!
*/
bool IsValid() const
{
@@ -206,6 +207,27 @@ public:
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
+class XOnlyPubKey
+{
+private:
+ uint256 m_keydata;
+
+public:
+ /** Construct an x-only pubkey from exactly 32 bytes. */
+ XOnlyPubKey(Span<const unsigned char> bytes);
+
+ /** 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;
+
+ 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(); }
+};
+
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 4f1e0056be..63b4107f7e 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -27,9 +27,11 @@
#include <qt/walletmodel.h>
#endif // ENABLE_WALLET
+#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <node/context.h>
+#include <node/ui_interface.h>
#include <noui.h>
#include <uint256.h>
#include <util/system.h>
@@ -37,6 +39,7 @@
#include <util/translation.h>
#include <validation.h>
+#include <boost/signals2/connection.hpp>
#include <memory>
#include <QApplication>
@@ -81,6 +84,7 @@ static void RegisterMetaTypes()
qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
+ qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
}
static QString GetLangTerritory()
@@ -162,10 +166,11 @@ void BitcoinCore::initialize()
{
try
{
- qDebug() << __func__ << ": Running initialization in thread";
util::ThreadRename("qt-init");
- bool rv = m_node.appInitMain();
- Q_EMIT initializeResult(rv);
+ qDebug() << __func__ << ": Running initialization in thread";
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
} catch (const std::exception& e) {
handleRunawayException(&e);
} catch (...) {
@@ -191,10 +196,9 @@ void BitcoinCore::shutdown()
static int qt_argc = 1;
static const char* qt_argv = "bitcoin-qt";
-BitcoinApplication::BitcoinApplication(interfaces::Node& node):
+BitcoinApplication::BitcoinApplication():
QApplication(qt_argc, const_cast<char **>(&qt_argv)),
coreThread(nullptr),
- m_node(node),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
@@ -202,6 +206,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node):
returnValue(0),
platformStyle(nullptr)
{
+ // Qt runs setlocale(LC_ALL, "") on initialization.
RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
@@ -244,12 +249,12 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(m_node, this, resetSettings);
+ optionsModel = new OptionsModel(this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
- window = new BitcoinGUI(m_node, platformStyle, networkStyle, nullptr);
+ window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -257,17 +262,27 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
- SplashScreen *splash = new SplashScreen(m_node, nullptr, networkStyle);
+ assert(!m_splash);
+ m_splash = new SplashScreen(nullptr, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when finish() happens.
- splash->show();
- connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
- connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
+ m_splash->show();
+ connect(this, &BitcoinApplication::requestedInitialize, m_splash, &SplashScreen::handleLoadWallet);
+ connect(this, &BitcoinApplication::splashFinished, m_splash, &SplashScreen::finish);
+ connect(this, &BitcoinApplication::requestedShutdown, m_splash, &QWidget::close);
+}
+
+void BitcoinApplication::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ if (optionsModel) optionsModel->setNode(*m_node);
+ if (m_splash) m_splash->setNode(*m_node);
}
bool BitcoinApplication::baseInitialize()
{
- return m_node.baseInitialize();
+ return node().baseInitialize();
}
void BitcoinApplication::startThread()
@@ -275,7 +290,7 @@ void BitcoinApplication::startThread()
if(coreThread)
return;
coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore(m_node);
+ BitcoinCore *executor = new BitcoinCore(node());
executor->moveToThread(coreThread);
/* communication to and from thread */
@@ -296,8 +311,8 @@ void BitcoinApplication::parameterSetup()
// print to the console unnecessarily.
gArgs.SoftSetBoolArg("-printtoconsole", false);
- m_node.initLogging();
- m_node.initParameterInteraction();
+ InitLogging(gArgs);
+ InitParameterInteraction(gArgs);
}
void BitcoinApplication::InitializePruneSetting(bool prune)
@@ -329,7 +344,7 @@ void BitcoinApplication::requestShutdown()
window->unsubscribeFromCoreSignals();
// Request node shutdown, which can interrupt long operations, like
// rescanning a wallet.
- m_node.startShutdown();
+ node().startShutdown();
// Unsetting the client model can cause the current thread to wait for node
// to complete an operation, like wait for a RPC execution to complete.
window->setClientModel(nullptr);
@@ -342,7 +357,7 @@ void BitcoinApplication::requestShutdown()
Q_EMIT requestedShutdown();
}
-void BitcoinApplication::initializeResult(bool success)
+void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info)
{
qDebug() << __func__ << ": Initialization result: " << success;
// Set exit result.
@@ -351,8 +366,8 @@ void BitcoinApplication::initializeResult(bool success)
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qInfo() << "Platform customization:" << platformStyle->getName();
- clientModel = new ClientModel(m_node, optionsModel);
- window->setClientModel(clientModel);
+ clientModel = new ClientModel(node(), optionsModel);
+ window->setClientModel(clientModel, &tip_info);
#ifdef ENABLE_WALLET
if (WalletModel::isWalletEnabled()) {
m_wallet_controller = new WalletController(*clientModel, platformStyle, this);
@@ -435,9 +450,9 @@ int GuiMain(int argc, char* argv[])
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
// Subscribe to global signals from core
- std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox);
- std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion);
- std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage);
+ boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
+ boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
+ boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
@@ -451,15 +466,15 @@ int GuiMain(int argc, char* argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
- BitcoinApplication app(*node);
+ BitcoinApplication app;
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- node->setupServerArgs();
+ SetupServerArgs(node_context);
SetupUIArgs(gArgs);
std::string error;
- if (!node->parseParameters(argc, argv, error)) {
- node->initError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
+ if (!gArgs.ParseParameters(argc, argv, error)) {
+ InitError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
// Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
// message can not be translated because translations have not been initialized
@@ -485,28 +500,31 @@ int GuiMain(int argc, char* argv[])
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
- HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
+ HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
+ // Install global event filter that makes sure that long tooltips can be word-wrapped
+ app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+
/// 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
// Gracefully exit if the user cancels
- if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS;
+ if (!Intro::showIfNeeded(did_show_intro, prune)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!CheckDataDirOption()) {
- node->initError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
+ InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
- if (!node->readConfigFiles(error)) {
- node->initError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
+ if (!gArgs.ReadConfigFiles(error, true)) {
+ InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
@@ -518,20 +536,20 @@ int GuiMain(int argc, char* argv[])
// - QSettings() will use the new application name after this, resulting in network-specific settings
// - Needs to be done before createOptionsModel
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
- node->selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- node->initError(Untranslated(strprintf("%s\n", e.what())));
+ InitError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
- PaymentServer::ipcParseCommandLine(*node, argc, argv);
+ PaymentServer::ipcParseCommandLine(argc, argv);
#endif
- if (!node->initSettings(error)) {
- node->initError(Untranslated(error));
+ if (!gArgs.InitSettings(error)) {
+ InitError(Untranslated(error));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
@@ -561,8 +579,6 @@ int GuiMain(int argc, char* argv[])
#endif // ENABLE_WALLET
/// 9. Main GUI initialization
- // Install global event filter that makes sure that long tooltips can be word-wrapped
- app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
// Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
@@ -585,6 +601,8 @@ int GuiMain(int argc, char* argv[])
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
+ app.setNode(*node);
+
int rv = EXIT_SUCCESS;
try
{
@@ -607,10 +625,10 @@ int GuiMain(int argc, char* argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings().translated));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
}
return rv;
}
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 077a37fde5..69e0a5921e 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -10,21 +10,21 @@
#endif
#include <QApplication>
+#include <assert.h>
#include <memory>
+#include <interfaces/node.h>
+
class BitcoinGUI;
class ClientModel;
class NetworkStyle;
class OptionsModel;
class PaymentServer;
class PlatformStyle;
+class SplashScreen;
class WalletController;
class WalletModel;
-namespace interfaces {
-class Handler;
-class Node;
-} // namespace interfaces
/** Class encapsulating Bitcoin Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
@@ -40,7 +40,7 @@ public Q_SLOTS:
void shutdown();
Q_SIGNALS:
- void initializeResult(bool success);
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
void runawayException(const QString &message);
@@ -56,7 +56,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(interfaces::Node& node);
+ explicit BitcoinApplication();
~BitcoinApplication();
#ifdef ENABLE_WALLET
@@ -90,8 +90,11 @@ public:
/// Setup platform style
void setupPlatformStyle();
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node);
+
public Q_SLOTS:
- void initializeResult(bool success);
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
@@ -104,7 +107,6 @@ Q_SIGNALS:
private:
QThread *coreThread;
- interfaces::Node& m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
@@ -116,6 +118,8 @@ private:
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
+ SplashScreen* m_splash = nullptr;
+ interfaces::Node* m_node = nullptr;
void startThread();
};
diff --git a/src/qt/bitcoin_locale.qrc b/src/qt/bitcoin_locale.qrc
index a35ca15d62..d438d6b3d5 100644
--- a/src/qt/bitcoin_locale.qrc
+++ b/src/qt/bitcoin_locale.qrc
@@ -28,6 +28,7 @@
<file alias="fi">locale/bitcoin_fi.qm</file>
<file alias="fil">locale/bitcoin_fil.qm</file>
<file alias="fr">locale/bitcoin_fr.qm</file>
+ <file alias="gl_ES">locale/bitcoin_gl_ES.qm</file>
<file alias="he">locale/bitcoin_he.qm</file>
<file alias="hi">locale/bitcoin_hi.qm</file>
<file alias="hr">locale/bitcoin_hr.qm</file>
@@ -72,7 +73,6 @@
<file alias="ta">locale/bitcoin_ta.qm</file>
<file alias="te">locale/bitcoin_te.qm</file>
<file alias="th">locale/bitcoin_th.qm</file>
- <file alias="tr">locale/bitcoin_tr.qm</file>
<file alias="uk">locale/bitcoin_uk.qm</file>
<file alias="ur">locale/bitcoin_ur.qm</file>
<file alias="uz@Cyrl">locale/bitcoin_uz@Cyrl.qm</file>
@@ -84,5 +84,6 @@
<file alias="zh_CN">locale/bitcoin_zh_CN.qm</file>
<file alias="zh_HK">locale/bitcoin_zh_HK.qm</file>
<file alias="zh_TW">locale/bitcoin_zh_TW.qm</file>
+ <file alias="zu">locale/bitcoin_zu.qm</file>
</qresource>
</RCC>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ebcc04a5eb..23370e6ad3 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -95,7 +95,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
updateWindowTitle();
rpcConsole = new RPCConsole(node, _platformStyle, nullptr);
- helpMessageDialog = new HelpMessageDialog(node, this, false);
+ helpMessageDialog = new HelpMessageDialog(this, false);
#ifdef ENABLE_WALLET
if(enableWallet)
{
@@ -574,7 +574,7 @@ void BitcoinGUI::createToolBars()
}
}
-void BitcoinGUI::setClientModel(ClientModel *_clientModel)
+void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndHeaderTipInfo* tip_info)
{
this->clientModel = _clientModel;
if(_clientModel)
@@ -588,8 +588,8 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
- modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
- setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false, SynchronizationState::INIT_DOWNLOAD);
+ modalOverlay->setKnownBestHeight(tip_info->header_height, QDateTime::fromTime_t(tip_info->header_time));
+ setNumBlocks(tip_info->block_height, QDateTime::fromTime_t(tip_info->block_time), tip_info->verification_progress, false, SynchronizationState::INIT_DOWNLOAD);
connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks);
// Receive and report messages from client model
@@ -600,7 +600,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
// Show progress dialog
connect(_clientModel, &ClientModel::showProgress, this, &BitcoinGUI::showProgress);
- rpcConsole->setClientModel(_clientModel);
+ rpcConsole->setClientModel(_clientModel, tip_info->block_height, tip_info->block_time, tip_info->verification_progress);
updateProxyIcon();
@@ -660,18 +660,24 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
}
}
+WalletController* BitcoinGUI::getWalletController()
+{
+ return m_wallet_controller;
+}
+
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
if (!walletFrame->addWallet(walletModel)) return;
- const QString display_name = walletModel->getDisplayName();
- setWalletActionsEnabled(true);
rpcConsole->addWallet(walletModel);
- m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
- if (m_wallet_selector->count() == 2) {
+ if (m_wallet_selector->count() == 0) {
+ setWalletActionsEnabled(true);
+ } else if (m_wallet_selector->count() == 1) {
m_wallet_selector_label_action->setVisible(true);
m_wallet_selector_action->setVisible(true);
}
+ const QString display_name = walletModel->getDisplayName();
+ m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
@@ -821,7 +827,7 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(m_node, this, true);
+ HelpMessageDialog dlg(this, true);
dlg.exec();
}
@@ -1189,7 +1195,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
- if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
+ if (m_node.walletClient().getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 697e83e772..912297a74e 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -43,6 +43,7 @@ enum class SynchronizationState;
namespace interfaces {
class Handler;
class Node;
+struct BlockAndHeaderTipInfo;
}
QT_BEGIN_NAMESPACE
@@ -75,9 +76,10 @@ public:
/** Set the client model.
The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic.
*/
- void setClientModel(ClientModel *clientModel);
+ void setClientModel(ClientModel *clientModel = nullptr, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
#ifdef ENABLE_WALLET
void setWalletController(WalletController* wallet_controller);
+ WalletController* getWalletController();
#endif
#ifdef ENABLE_WALLET
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 64900a4343..8b89242f6c 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -11,20 +11,19 @@
static const char UNUSED *bitcoin_strings[] = {
QT_TRANSLATE_NOOP("bitcoin-core", "The %s developers"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring "
+"a backup."),
+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", ""
-"Can't generate a change-address key. No keys in the internal keypool and "
-"can't generate any keys."),
-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 -upgradewallet=169900 or -upgradewallet with no version "
-"specified."),
+"keypool. Please use version 169900 or no version specified."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Distributed under the MIT software license, see the accompanying file %s or "
"%s"),
@@ -58,9 +57,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
QT_TRANSLATE_NOOP("bitcoin-core", ""
"The transaction amount is too small to send after the fee has been deducted"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"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"),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"This is a pre-release test build - use at your own risk - do not use for "
"mining or merchant applications"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"This is the maximum transaction fee you pay (in addition to the normal fee) "
+"to prioritize partial spend avoidance over regular coin selection."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"This is the transaction fee you may discard if change is smaller than dust "
"at this level"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -69,6 +75,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Total length of network version string (%i) exceeds maximum length (%i). "
"Reduce the number or size of uacomments."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Transaction needs a change address, but we can't generate it. Please call "
+"keypoolrefill first."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to replay blocks. You will need to rebuild the database using -"
"reindex-chainstate."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -80,21 +89,17 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: The network does not appear to fully agree! Some miners appear to "
"be experiencing issues."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
-"Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; "
-"if your balance or transactions are incorrect you should restore from a "
-"backup."),
-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", ""
"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", "%d of last 100 blocks have unexpected version"),
-QT_TRANSLATE_NOOP("bitcoin-core", "%s corrupt, salvage failed"),
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."),
QT_TRANSLATE_NOOP("bitcoin-core", "Change index out of range"),
QT_TRANSLATE_NOOP("bitcoin-core", "Config setting for %s only applied on %s network when in [%s] section."),
@@ -102,6 +107,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) %i-%i"),
QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"),
QT_TRANSLATE_NOOP("bitcoin-core", "Could not find asmap file %s"),
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", "Error initializing block database"),
@@ -115,11 +121,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet %s. Duplicate -wallet fi
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 upgrading chainstate database"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low for %s"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is too low!"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error: Keypool ran out, please call keypoolrefill first"),
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", "Fee rate (%s) is lower than the minimum fee rate setting (%s)"),
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."),
@@ -137,6 +143,7 @@ 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."),
@@ -184,5 +191,4 @@ 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)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."),
};
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 7822d4c5f3..a2f46c339b 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -15,6 +15,7 @@
#include <net.h>
#include <netbase.h>
#include <util/system.h>
+#include <util/threadnames.h>
#include <validation.h>
#include <stdint.h>
@@ -52,6 +53,9 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
// move timer to thread so that polling doesn't disturb main event loop
timer->moveToThread(m_thread);
m_thread->start();
+ QTimer::singleShot(0, timer, []() {
+ util::ThreadRename("qt-clientmodl");
+ });
subscribeToCoreSignals();
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 93840b4169..d210faec03 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -290,7 +290,7 @@
<item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>Current number of blocks</string>
+ <string>Current block height</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 7dbee6d689..06d39426c9 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -114,6 +114,12 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset>
</property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 9457ea37d6..882d2c8f52 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -46,6 +46,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80;
#define QAPP_ORG_DOMAIN "bitcoin.org"
#define QAPP_APP_NAME_DEFAULT "Bitcoin-Qt"
#define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet"
+#define QAPP_APP_NAME_SIGNET "Bitcoin-Qt-signet"
#define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest"
/* One gigabyte (GB) in bytes */
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 7f439fa45e..bab17562a6 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -21,11 +21,6 @@
#include <util/system.h>
#ifdef WIN32
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
-#define _WIN32_IE 0x0501
-#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -94,7 +89,7 @@ static std::string DummyAddress(const CChainParams &params)
std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
- std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
+ std::string s = EncodeBase58(sourcedata);
if (!IsValidDestinationString(s)) {
return s;
}
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index ad21dfc3ef..235722d091 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <chainparams.h>
#include <fs.h>
#include <qt/intro.h>
#include <qt/forms/ui_intro.h>
@@ -181,7 +182,7 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune)
+bool Intro::showIfNeeded(bool& did_show_intro, bool& prune)
{
did_show_intro = false;
@@ -199,13 +200,13 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
{
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
- node.selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch (const std::exception&) {
return false;
}
/* If current default data directory does not exist, let the user choose one */
- Intro intro(0, node.getAssumedBlockchainSize(), node.getAssumedChainStateSize());
+ Intro intro(0, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/bitcoin"));
did_show_intro = true;
@@ -242,7 +243,7 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
* (to be consistent with bitcoind behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
- node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 732393246e..51f42de7ac 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -47,7 +47,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune);
+ static bool showIfNeeded(bool& did_show_intro, bool& prune);
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/locale/bitcoin_af.ts b/src/qt/locale/bitcoin_af.ts
index 23d0f609d7..aa09dc197b 100644
--- a/src/qt/locale/bitcoin_af.ts
+++ b/src/qt/locale/bitcoin_af.ts
@@ -3,902 +3,1264 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Regs-klik om die adres of etiket te wysig</translation>
+ <translation>Right-click to edit address or label</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>Skep 'n nuwe adres</translation>
+ <translation>Create a new address</translation>
</message>
<message>
<source>&amp;New</source>
- <translation>&amp;Nuwe</translation>
+ <translation>&amp;New</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>Maak 'n kopie van die huidige adres na die stelsel klipbord</translation>
+ <translation>Copy the currently selected address to the system clipboard</translation>
</message>
<message>
<source>&amp;Copy</source>
- <translation>&amp;Kopie</translation>
+ <translation>&amp;Copy</translation>
</message>
<message>
<source>C&amp;lose</source>
- <translation>S&amp;luit</translation>
+ <translation>C&amp;lose</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>Verwyder die uitgekiesde adres van die lys</translation>
+ <translation>Delete the currently selected address from the list</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>Enter address or label to search</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Voer inligting uit van die huidige blad na n lêer</translation>
+ <translation>Export the data in the current tab to a file</translation>
</message>
<message>
<source>&amp;Export</source>
- <translation>&amp;Uitvoer</translation>
+ <translation>&amp;Export</translation>
</message>
<message>
<source>&amp;Delete</source>
- <translation>&amp;Verwyder</translation>
+ <translation>&amp;Delete</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>Kies die address na wie die muntstukke gestuur moet word</translation>
+ <translation>Choose the address to send coins to</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>Kies die adres vir die ontvangs van betaaling</translation>
+ <translation>Choose the address to receive coins with</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>K&amp;ies</translation>
+ <translation>C&amp;hoose</translation>
</message>
<message>
<source>Sending addresses</source>
- <translation>Stuur adresse</translation>
+ <translation>Sending addresses</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>Ontvang adresse</translation>
+ <translation>Receiving addresses</translation>
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>Dit is jou Bitcoin-adresse vir die stuur van betalings. Kontroleer altyd die bedrag en die ontvangsadres voordat u munte stuur.</translation>
+ <translation>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>&amp;Kopie Adres</translation>
+ <translation>&amp;Copy Address</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>Kopie &amp;Etiket</translation>
+ <translation>Copy &amp;Label</translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>&amp;Wysig</translation>
+ <translation>&amp;Edit</translation>
</message>
<message>
<source>Export Address List</source>
- <translation>Voer adres lys uit</translation>
+ <translation>Export Address List</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>Koma geskeide lêer (*.csv)</translation>
+ <translation>Comma separated file (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
- <translation>Uitvoering Misluk</translation>
+ <translation>Exporting Failed</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
- <translation>Kon nie die adreslys stoor na %1 nie. Probeer asseblief weer.</translation>
+ <translation>There was an error trying to save the address list to %1. Please try again.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
- <translation>Etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Address</source>
- <translation>Adres</translation>
+ <translation>Address</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(geen etiket)</translation>
+ <translation>(no label)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
- <translation>Wagfrase Dialoog</translation>
+ <translation>Passphrase Dialog</translation>
</message>
<message>
<source>Enter passphrase</source>
- <translation>Tik wagfrase in</translation>
+ <translation>Enter passphrase</translation>
</message>
<message>
<source>New passphrase</source>
- <translation>Nuwe wagfrase</translation>
+ <translation>New passphrase</translation>
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>Herhaal nuwe wagfrase</translation>
+ <translation>Repeat new passphrase</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>Show passphrase</translation>
</message>
<message>
<source>Encrypt wallet</source>
- <translation>Enkripteer beursie</translation>
+ <translation>Encrypt wallet</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>Hierdie operasie benodig 'n wagwoord om die beursie oop te sluit.</translation>
+ <translation>This operation needs your wallet passphrase to unlock the wallet.</translation>
</message>
<message>
<source>Unlock wallet</source>
- <translation>Ontsluit beursie</translation>
+ <translation>Unlock wallet</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
- <translation>Hierdie operasie benodig 'n wagwoord om die beursie oop te sluit.</translation>
+ <translation>This operation needs your wallet passphrase to decrypt the wallet.</translation>
</message>
<message>
<source>Decrypt wallet</source>
- <translation>Dekripteer beursie</translation>
+ <translation>Decrypt wallet</translation>
</message>
<message>
<source>Change passphrase</source>
- <translation>Verander wagfrase</translation>
+ <translation>Change passphrase</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
- <translation>Bevestig beursie enkripsie.</translation>
+ <translation>Confirm wallet encryption</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
- <translation>Waarskuwing: As jy jou beursie enkripteer en jou wagwoord verloor, sal jy &lt;b&gt;AL JOU BITCOINS VERLOOR&lt;/b&gt;!</translation>
+ <translation>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
- <translation>Is jy seker jy wil jou beursie enkripteer?</translation>
+ <translation>Are you sure you wish to encrypt your wallet?</translation>
</message>
<message>
<source>Wallet encrypted</source>
- <translation>Beursie enkriptasie voltooi</translation>
+ <translation>Wallet encrypted</translation>
+ </message>
+ <message>
+ <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>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;.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Enter the old passphrase and new passphrase for the wallet.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Wallet to be encrypted</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Your wallet is about to be encrypted. </translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Your wallet is now encrypted. </translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
- <translation>BELANGRIK: Enige vorige rugsteune wat u gemaak het van u beursie-lêer moet vervang word met die nuut-gegenereerde, versleutelde beursie-lêer. Vir sekuriteitsredes sal vorige rugsteune van die onversleutelde beursie-lêer onbruikbaar word sodra u die nuwe, versleutelde beursie begin gebruik.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Wallet encryption failed</source>
- <translation>Beursie enkriptasie het misluk</translation>
+ <translation>Wallet encryption failed</translation>
</message>
<message>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
- <translation>Beursie bewaaking het misluk as gevolg van 'n interne fout. Die beursie is nie bewaak nie!</translation>
+ <translation>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</translation>
</message>
<message>
<source>The supplied passphrases do not match.</source>
- <translation>Die wagfrase stem nie ooreen nie</translation>
+ <translation>The supplied passphrases do not match.</translation>
</message>
<message>
<source>Wallet unlock failed</source>
- <translation>Beursie oopsluiting het misluk</translation>
+ <translation>Wallet unlock failed</translation>
</message>
<message>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
- <translation>Die wagfrase wat ingetik was om die beursie oop te sluit, was verkeerd.</translation>
+ <translation>The passphrase entered for the wallet decryption was incorrect.</translation>
</message>
<message>
<source>Wallet decryption failed</source>
- <translation>Beursie dekripsie het misluk</translation>
+ <translation>Wallet decryption failed</translation>
</message>
<message>
<source>Wallet passphrase was successfully changed.</source>
- <translation>Die beursie se wagfrase verandering was suksesvol.</translation>
+ <translation>Wallet passphrase was successfully changed.</translation>
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
- <translation>Waarskuwing: Die Caps Lock is aan!</translation>
+ <translation>Warning: The Caps Lock key is on!</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
<message>
<source>IP/Netmask</source>
- <translation>IP/Netmasker</translation>
+ <translation>IP/Netmask</translation>
</message>
<message>
<source>Banned Until</source>
- <translation>Verban Tot</translation>
+ <translation>Banned Until</translation>
</message>
</context>
<context>
<name>BitcoinGUI</name>
<message>
<source>Sign &amp;message...</source>
- <translation>Teken &amp;Boodskap</translation>
+ <translation>Sign &amp;message...</translation>
</message>
<message>
<source>Synchronizing with network...</source>
- <translation>Sinchroniseer met die netwerk ...</translation>
+ <translation>Synchronizing with network...</translation>
</message>
<message>
<source>&amp;Overview</source>
- <translation>&amp;Oorsig</translation>
+ <translation>&amp;Overview</translation>
</message>
<message>
<source>Show general overview of wallet</source>
- <translation>Wys algemene oorsig van die beursie</translation>
+ <translation>Show general overview of wallet</translation>
</message>
<message>
<source>&amp;Transactions</source>
- <translation>&amp;Transaksies</translation>
+ <translation>&amp;Transactions</translation>
</message>
<message>
<source>Browse transaction history</source>
- <translation>Besoek transaksie geskiedenis</translation>
+ <translation>Browse transaction history</translation>
</message>
<message>
<source>E&amp;xit</source>
- <translation>S&amp;luit af</translation>
+ <translation>E&amp;xit</translation>
</message>
<message>
<source>Quit application</source>
- <translation>Sluit af</translation>
+ <translation>Quit application</translation>
</message>
<message>
<source>&amp;About %1</source>
- <translation>&amp;Oor %1</translation>
+ <translation>&amp;About %1</translation>
</message>
<message>
<source>Show information about %1</source>
- <translation>Wys inligting oor %1</translation>
+ <translation>Show information about %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
- <translation>Oor &amp;Qt</translation>
+ <translation>About &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
- <translation>Wys inligting oor Qt</translation>
+ <translation>Show information about Qt</translation>
</message>
<message>
<source>&amp;Options...</source>
- <translation>&amp;Opsies</translation>
+ <translation>&amp;Options...</translation>
</message>
<message>
<source>Modify configuration options for %1</source>
- <translation>Verander konfigurasie opsies vir %1</translation>
+ <translation>Modify configuration options for %1</translation>
</message>
<message>
<source>&amp;Encrypt Wallet...</source>
- <translation>&amp;Enkripteer Beursie...</translation>
+ <translation>&amp;Encrypt Wallet...</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
- <translation>&amp;Rugsteun Beursie...</translation>
+ <translation>&amp;Backup Wallet...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
- <translation>Verander wagwoord frase...</translation>
+ <translation>&amp;Change Passphrase...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
- <translation>Maak &amp;URI oop...</translation>
+ <translation>Open &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Create Wallet...</source>
+ <translation>Create Wallet...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Create a new wallet</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Wallet:</translation>
</message>
<message>
<source>Click to disable network activity.</source>
- <translation>Klik om netwerk aktiwiteit af te skakel.</translation>
+ <translation>Click to disable network activity.</translation>
</message>
<message>
<source>Network activity disabled.</source>
- <translation>Netwerk aktiwiteid afgeskakel.</translation>
+ <translation>Network activity disabled.</translation>
</message>
<message>
<source>Click to enable network activity again.</source>
- <translation>Klik om netwerk aktiwiteit weer aan te skakel.</translation>
+ <translation>Click to enable network activity again.</translation>
</message>
<message>
<source>Syncing Headers (%1%)...</source>
- <translation>Sinkroniseer tans Hoofde (%1%)...</translation>
+ <translation>Syncing Headers (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>Herindekseer blokke op skyf...</translation>
+ <translation>Reindexing blocks on disk...</translation>
+ </message>
+ <message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</translation>
</message>
<message>
<source>Send coins to a Bitcoin address</source>
- <translation>Stuur muntstukke na 'n Bitcoin adres</translation>
+ <translation>Send coins to a Bitcoin address</translation>
</message>
<message>
<source>Backup wallet to another location</source>
- <translation>Rugsteun beursie na 'n ander plek</translation>
+ <translation>Backup wallet to another location</translation>
</message>
<message>
<source>Change the passphrase used for wallet encryption</source>
- <translation>Verander die wagwoordfrase wat vir beursie-versleuteling gebruik word</translation>
+ <translation>Change the passphrase used for wallet encryption</translation>
</message>
<message>
<source>&amp;Verify message...</source>
- <translation>&amp;Verifieer boodskap...</translation>
+ <translation>&amp;Verify message...</translation>
</message>
<message>
<source>&amp;Send</source>
- <translation>&amp;Stuur</translation>
+ <translation>&amp;Send</translation>
</message>
<message>
<source>&amp;Receive</source>
- <translation>&amp;Ontvang</translation>
+ <translation>&amp;Receive</translation>
</message>
<message>
<source>&amp;Show / Hide</source>
- <translation>&amp;Wys / Versteek</translation>
+ <translation>&amp;Show / Hide</translation>
</message>
<message>
<source>Show or hide the main Window</source>
- <translation>Wys of versteek die hoof Venster</translation>
+ <translation>Show or hide the main Window</translation>
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
- <translation>Versleutel die private sleutels wat aan u beursie behoort</translation>
+ <translation>Encrypt the private keys that belong to your wallet</translation>
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>Teken boodskappe met u Bitcoin adresse om te bewys dat u hul besit</translation>
+ <translation>Sign messages with your Bitcoin addresses to prove you own them</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
- <translation>Bevestig boodskappe om te verseker dat hulle geteken was met gespesifiseerde Bitcoin adresse</translation>
+ <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
<source>&amp;File</source>
- <translation>&amp;Lêer</translation>
+ <translation>&amp;File</translation>
</message>
<message>
<source>&amp;Settings</source>
- <translation>&amp;Instellings</translation>
+ <translation>&amp;Settings</translation>
</message>
<message>
<source>&amp;Help</source>
- <translation>&amp;Hulp</translation>
+ <translation>&amp;Help</translation>
</message>
<message>
<source>Tabs toolbar</source>
- <translation>Blad nutsbalk</translation>
+ <translation>Tabs toolbar</translation>
</message>
<message>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
- <translation>Versoek betalings (genereer QR kodes en bitcoin: URIs)</translation>
+ <translation>Request payments (generates QR codes and bitcoin: URIs)</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
- <translation>Wys die lys van gebruikte stuur adresse en etikette</translation>
+ <translation>Show the list of used sending addresses and labels</translation>
</message>
<message>
<source>Show the list of used receiving addresses and labels</source>
- <translation>Wys die lys van gebruikte ontvangsadresse en etikette</translation>
+ <translation>Show the list of used receiving addresses and labels</translation>
</message>
<message>
<source>&amp;Command-line options</source>
- <translation>&amp;Opdrag lys opsies</translation>
+ <translation>&amp;Command-line options</translation>
+ </message>
+ <message numerus="yes">
+ <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>
<message>
<source>Indexing blocks on disk...</source>
- <translation>Indekseer tans blokke op skyf...</translation>
+ <translation>Indexing blocks on disk...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
- <translation>Prosesseer tans blokke op skyf...</translation>
+ <translation>Processing blocks on disk...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Processed %n block of transaction history.</numerusform><numerusform>Processed %n blocks of transaction history.</numerusform></translation>
</message>
<message>
<source>%1 behind</source>
- <translation>%1 agter</translation>
+ <translation>%1 behind</translation>
</message>
<message>
<source>Last received block was generated %1 ago.</source>
- <translation>Ontvangs van laaste blok is %1 terug.</translation>
+ <translation>Last received block was generated %1 ago.</translation>
</message>
<message>
<source>Transactions after this will not yet be visible.</source>
- <translation>Opvolgende transaksies sal nog nie sigbaar wees nie.</translation>
+ <translation>Transactions after this will not yet be visible.</translation>
</message>
<message>
<source>Error</source>
- <translation>Fout</translation>
+ <translation>Error</translation>
</message>
<message>
<source>Warning</source>
- <translation>Waarskuwing</translation>
+ <translation>Warning</translation>
</message>
<message>
<source>Information</source>
- <translation>Informasie</translation>
+ <translation>Information</translation>
</message>
<message>
<source>Up to date</source>
- <translation>Op datum</translation>
+ <translation>Up to date</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>Node window</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Open node debugging and diagnostic console</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses</source>
+ <translation>&amp;Sending addresses</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses</source>
+ <translation>&amp;Receiving addresses</translation>
+ </message>
+ <message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Open a bitcoin: URI</translation>
+ </message>
+ <message>
+ <source>Open Wallet</source>
+ <translation>Open Wallet</translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>Open a wallet</translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>Close Wallet...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>Close wallet</translation>
</message>
<message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
- <translation>Wys die %1 hulpboodskap om 'n lys met moontlike Bitcoin bevel-lyn opsies te verkry</translation>
+ <translation>Show the %1 help message to get a list with possible Bitcoin command-line options</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
+ <source>No wallets available</source>
+ <translation>No wallets available</translation>
</message>
<message>
<source>&amp;Window</source>
- <translation>&amp;Venster</translation>
+ <translation>&amp;Window</translation>
+ </message>
+ <message>
+ <source>Minimize</source>
+ <translation>Minimize</translation>
+ </message>
+ <message>
+ <source>Zoom</source>
+ <translation>Zoom</translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>Main Window</translation>
</message>
<message>
<source>%1 client</source>
- <translation>%1 klient</translation>
+ <translation>%1 client</translation>
</message>
<message>
<source>Connecting to peers...</source>
- <translation>Verbind tans aan eweknieë...</translation>
+ <translation>Connecting to peers...</translation>
</message>
<message>
<source>Catching up...</source>
- <translation>Besig om op te vang...</translation>
+ <translation>Catching up...</translation>
</message>
<message>
<source>Error: %1</source>
- <translation>Fout: %1</translation>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
+ <source>Warning: %1</source>
+ <translation>Warning: %1</translation>
</message>
<message>
<source>Date: %1
</source>
- <translation>Datum: %1
+ <translation>Date: %1
</translation>
</message>
<message>
<source>Amount: %1
</source>
- <translation>Bedrag: %1
+ <translation>Amount: %1
+</translation>
+ </message>
+ <message>
+ <source>Wallet: %1
+</source>
+ <translation>Wallet: %1
</translation>
</message>
<message>
<source>Type: %1
</source>
- <translation>Tipe: %1
+ <translation>Type: %1
</translation>
</message>
<message>
<source>Label: %1
</source>
- <translation>Etiket: %1
+ <translation>Label: %1
</translation>
</message>
<message>
<source>Address: %1
</source>
- <translation>Adres: %1
+ <translation>Address: %1
</translation>
</message>
<message>
<source>Sent transaction</source>
- <translation>Transaksie gestuur</translation>
+ <translation>Sent transaction</translation>
</message>
<message>
<source>Incoming transaction</source>
- <translation>Inkomende transaksie</translation>
+ <translation>Incoming transaction</translation>
</message>
<message>
<source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
- <translation>HD sleutel generasie is &lt;b&gt;aangesit&lt;/b&gt;</translation>
+ <translation>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</translation>
</message>
<message>
<source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
- <translation>HD sleutel generasie is &lt;b&gt;afgesit&lt;/b&gt;</translation>
+ <translation>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Private key &lt;b&gt;disabled&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
- <translation>Beursie is &lt;b&gt;versleutel&lt;/b&gt; en is tans &lt;b&gt;oopgesluit&lt;/b&gt;</translation>
+ <translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
- <translation>Beursie is &lt;b&gt;versleutel&lt;/b&gt; en is tans &lt;b&gt;gesluit&lt;/b&gt;</translation>
+ <translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
- <translation>'n Noodlottige fout het voorgekom. Bitcoin kan nie langer voortgaan nie en sal afsluit.</translation>
+ <translation>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Coin Selection</source>
- <translation>Munt Keuse</translation>
+ <translation>Coin Selection</translation>
</message>
<message>
<source>Quantity:</source>
- <translation>Hoeveelheid:</translation>
+ <translation>Quantity:</translation>
</message>
<message>
<source>Bytes:</source>
- <translation>Grepe:</translation>
+ <translation>Bytes:</translation>
</message>
<message>
<source>Amount:</source>
- <translation>Bedrag:</translation>
+ <translation>Amount:</translation>
</message>
<message>
<source>Fee:</source>
- <translation>Fooi:</translation>
+ <translation>Fee:</translation>
</message>
<message>
<source>Dust:</source>
- <translation>Stof:</translation>
+ <translation>Dust:</translation>
</message>
<message>
<source>After Fee:</source>
- <translation>Na Fooi:</translation>
+ <translation>After Fee:</translation>
</message>
<message>
<source>Change:</source>
- <translation>Verander:</translation>
+ <translation>Change:</translation>
</message>
<message>
<source>(un)select all</source>
- <translation>(on)selekteer alles</translation>
+ <translation>(un)select all</translation>
</message>
<message>
<source>Tree mode</source>
- <translation>Boom wyse</translation>
+ <translation>Tree mode</translation>
</message>
<message>
<source>List mode</source>
- <translation>Lys wyse</translation>
+ <translation>List mode</translation>
</message>
<message>
<source>Amount</source>
- <translation>Bedrag</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>Received with label</source>
- <translation>Ontvang met etiket</translation>
+ <translation>Received with label</translation>
</message>
<message>
<source>Received with address</source>
- <translation>Ontvang met adres</translation>
+ <translation>Received with address</translation>
</message>
<message>
<source>Date</source>
- <translation>Datum</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Confirmations</source>
- <translation>Bevestigings</translation>
+ <translation>Confirmations</translation>
</message>
<message>
<source>Confirmed</source>
- <translation>Bevestig</translation>
+ <translation>Confirmed</translation>
</message>
<message>
<source>Copy address</source>
- <translation>Maak kopie van adres</translation>
+ <translation>Copy address</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Kopieer etiket</translation>
+ <translation>Copy label</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Kopieer bedrag</translation>
+ <translation>Copy amount</translation>
</message>
<message>
<source>Copy transaction ID</source>
- <translation>Kopieer transaksie ID</translation>
+ <translation>Copy transaction ID</translation>
</message>
<message>
<source>Lock unspent</source>
- <translation>Sluit ongespandeerde</translation>
+ <translation>Lock unspent</translation>
</message>
<message>
<source>Unlock unspent</source>
- <translation>Onsluit ongespandeerde</translation>
+ <translation>Unlock unspent</translation>
</message>
<message>
<source>Copy quantity</source>
- <translation>Kopieer hoeveelheid</translation>
+ <translation>Copy quantity</translation>
</message>
<message>
<source>Copy fee</source>
- <translation>Kopieer fooi</translation>
+ <translation>Copy fee</translation>
</message>
<message>
<source>Copy after fee</source>
- <translation>Kopieer na fooi</translation>
+ <translation>Copy after fee</translation>
</message>
<message>
<source>Copy bytes</source>
- <translation>Kopieer grepe</translation>
+ <translation>Copy bytes</translation>
</message>
<message>
<source>Copy dust</source>
- <translation>Kopieer stof</translation>
+ <translation>Copy dust</translation>
</message>
<message>
<source>Copy change</source>
- <translation>Kopieer verandering</translation>
+ <translation>Copy change</translation>
</message>
<message>
<source>(%1 locked)</source>
- <translation>(%1 gesluit)</translation>
+ <translation>(%1 locked)</translation>
</message>
<message>
<source>yes</source>
- <translation>ja</translation>
+ <translation>yes</translation>
</message>
<message>
<source>no</source>
- <translation>nee</translation>
+ <translation>no</translation>
</message>
<message>
<source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
- <translation>Hierdie etiket verander na rooi as enige ontvanger 'n bedrag kleiner as die huidige stof drempelwaarde ontvang.</translation>
+ <translation>This label turns red if any recipient receives an amount smaller than the current dust threshold.</translation>
</message>
<message>
<source>Can vary +/- %1 satoshi(s) per input.</source>
- <translation>Kan wissel met +/- %1 satoshi(s) per inset.</translation>
+ <translation>Can vary +/- %1 satoshi(s) per input.</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(geen etiket)</translation>
+ <translation>(no label)</translation>
</message>
<message>
<source>change from %1 (%2)</source>
- <translation>Verander vanaf %1 (%2)</translation>
+ <translation>change from %1 (%2)</translation>
</message>
<message>
<source>(change)</source>
- <translation>(verander)</translation>
+ <translation>(change)</translation>
</message>
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Create wallet failed</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Create wallet warning</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Create Wallet</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Wallet Name</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Encrypt Wallet</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Disable Private Keys</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Make Blank Wallet</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Create</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
- <translation>Wysig Adres</translation>
+ <translation>Edit Address</translation>
</message>
<message>
<source>&amp;Label</source>
- <translation>&amp;Etiket</translation>
+ <translation>&amp;Label</translation>
</message>
<message>
<source>The label associated with this address list entry</source>
- <translation>Die etiket geassosieer met hierdie adreslys inskrywing</translation>
+ <translation>The label associated with this address list entry</translation>
</message>
<message>
<source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
- <translation>Die adres geassosieer met hierdie adreslys inskrywing. Dié kan slegs gewysig word vir stuur-adresse.</translation>
+ <translation>The address associated with this address list entry. This can only be modified for sending addresses.</translation>
</message>
<message>
<source>&amp;Address</source>
- <translation>&amp;Adres</translation>
+ <translation>&amp;Address</translation>
</message>
<message>
<source>New sending address</source>
- <translation>Nuwe stuurende adres</translation>
+ <translation>New sending address</translation>
</message>
<message>
<source>Edit receiving address</source>
- <translation>Wysig ontvangende adres</translation>
+ <translation>Edit receiving address</translation>
</message>
<message>
<source>Edit sending address</source>
- <translation>Wysig stuurende adres</translation>
+ <translation>Edit sending address</translation>
</message>
<message>
<source>The entered address "%1" is not a valid Bitcoin address.</source>
- <translation>Die ingeskrewe adres "%1" is nie 'n geldige Bitcoin adres nie.</translation>
+ <translation>The entered address "%1" is not a valid Bitcoin address.</translation>
+ </message>
+ <message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>The entered address "%1" is already in the address book with label "%2".</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
- <translation>Kon nie die beursie oopsluit nie.</translation>
+ <translation>Could not unlock wallet.</translation>
</message>
<message>
<source>New key generation failed.</source>
- <translation>Nuwe sleutel genereering het misluk.</translation>
+ <translation>New key generation failed.</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>n Nuwe data lêer sal geskep word.</translation>
+ <translation>A new data directory will be created.</translation>
</message>
<message>
<source>name</source>
- <translation>naam</translation>
+ <translation>name</translation>
</message>
<message>
<source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
- <translation>Lêer bestaan reeds. Voeg %1 indien u van plan is om n nuwe lêer hier te skep.</translation>
+ <translation>Directory already exists. Add %1 if you intend to create a new directory here.</translation>
</message>
<message>
<source>Path already exists, and is not a directory.</source>
- <translation>Pad bestaan reeds en is nie 'n lêergids nie.</translation>
+ <translation>Path already exists, and is not a directory.</translation>
</message>
<message>
<source>Cannot create data directory here.</source>
- <translation>Kan nie data gids hier skep nie.</translation>
+ <translation>Cannot create data directory here.</translation>
</message>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
- <translation>weergawe</translation>
+ <translation>version</translation>
</message>
<message>
<source>About %1</source>
- <translation>Oor %1</translation>
+ <translation>About %1</translation>
</message>
<message>
<source>Command-line options</source>
- <translation>Opdrag lys opsies</translation>
+ <translation>Command-line options</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Welcome</source>
- <translation>Welkom</translation>
+ <translation>Welcome</translation>
</message>
<message>
<source>Welcome to %1.</source>
- <translation>Welkom by %1.</translation>
+ <translation>Welcome to %1.</translation>
+ </message>
+ <message>
+ <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <translation>As this is the first time the program is launched, you can choose where %1 will store its data.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
</message>
<message>
<source>Use the default data directory</source>
- <translation>Gebruik die standaard data gids</translation>
+ <translation>Use the default data directory</translation>
</message>
<message>
<source>Use a custom data directory:</source>
- <translation>Gebruik 'n persoonlike data gids:</translation>
+ <translation>Use a custom data directory:</translation>
</message>
<message>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Discard blocks after verification, except most recent %1 GB (prune)</translation>
+ </message>
+ <message>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation>At least %1 GB of data will be stored in this directory, and it will grow over time.</translation>
+ </message>
+ <message>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation>Approximately %1 GB of data will be stored in this directory.</translation>
+ </message>
+ <message>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation>%1 will download and store a copy of the Bitcoin block chain.</translation>
+ </message>
+ <message>
<source>The wallet will also be stored in this directory.</source>
- <translation>Die beursie sal ook gestoor word in hierdie lêer.</translation>
+ <translation>The wallet will also be stored in this directory.</translation>
</message>
<message>
<source>Error: Specified data directory "%1" cannot be created.</source>
- <translation>Fout: Gespesifiseerde dataleêr "%1" kon nie geskep word nie.</translation>
+ <translation>Error: Specified data directory "%1" cannot be created.</translation>
</message>
<message>
<source>Error</source>
- <translation>Fout</translation>
+ <translation>Error</translation>
+ </message>
+ <message numerus="yes">
+ <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>
- </context>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(of %n GB needed)</numerusform><numerusform>(of %n GB needed)</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n GB needed for full chain)</numerusform><numerusform>(%n GB needed for full chain)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
<source>Form</source>
- <translation>Vorm</translation>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</translation>
</message>
<message>
<source>Number of blocks left</source>
- <translation>Aantal blokke oor</translation>
+ <translation>Number of blocks left</translation>
</message>
<message>
<source>Unknown...</source>
- <translation>Onbekend...</translation>
+ <translation>Unknown...</translation>
</message>
<message>
<source>Last block time</source>
- <translation>Laaste blok tyd</translation>
+ <translation>Last block time</translation>
</message>
<message>
<source>Progress</source>
- <translation>Vorderering</translation>
+ <translation>Progress</translation>
</message>
<message>
<source>Progress increase per hour</source>
- <translation>Vorderingstoename per uur</translation>
+ <translation>Progress increase per hour</translation>
</message>
<message>
<source>calculating...</source>
- <translation>besig met bereken...</translation>
+ <translation>calculating...</translation>
</message>
<message>
<source>Estimated time left until synced</source>
- <translation>Geskatte tyd oor totdat gesinkroniseer</translation>
+ <translation>Estimated time left until synced</translation>
</message>
<message>
<source>Hide</source>
- <translation>Steek weg</translation>
+ <translation>Hide</translation>
</message>
- </context>
+ <message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
+ <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>%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1, %2%)...</source>
+ <translation>Unknown. Syncing Headers (%1, %2%)...</translation>
+ </message>
+</context>
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Open bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
</context>
<context>
<name>OpenWalletActivity</name>
- </context>
+ <message>
+ <source>Open wallet failed</source>
+ <translation>Open wallet failed</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Open wallet warning</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+</context>
<context>
<name>OptionsDialog</name>
<message>
<source>Options</source>
- <translation>Opsies</translation>
+ <translation>Options</translation>
</message>
<message>
<source>&amp;Main</source>
- <translation>&amp;Hoof</translation>
+ <translation>&amp;Main</translation>
</message>
<message>
<source>Automatically start %1 after logging in to the system.</source>
- <translation>Begin %1 outomaties nadat jy aangemeld is by die stelsel.</translation>
+ <translation>Automatically start %1 after logging in to the system.</translation>
</message>
<message>
<source>&amp;Start %1 on system login</source>
- <translation>&amp;Begin %1 op stelsel aanmelding</translation>
+ <translation>&amp;Start %1 on system login</translation>
</message>
<message>
<source>Size of &amp;database cache</source>
- <translation>Grootte van &amp;databasis kas</translation>
+ <translation>Size of &amp;database cache</translation>
</message>
<message>
<source>Number of script &amp;verification threads</source>
- <translation>Aantal skrip &amp;verifikasie drade</translation>
+ <translation>Number of script &amp;verification threads</translation>
+ </message>
+ <message>
+ <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <translation>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ </message>
+ <message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</translation>
+ </message>
+ <message>
+ <source>Hide the icon from the system tray.</source>
+ <translation>Hide the icon from the system tray.</translation>
+ </message>
+ <message>
+ <source>&amp;Hide tray icon</source>
+ <translation>&amp;Hide tray icon</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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 |.</translation>
</message>
<message>
<source>Open the %1 configuration file from the working directory.</source>
- <translation>Maak die %1 konfigurasie lêer oop van die werk gids.</translation>
+ <translation>Open the %1 configuration file from the working directory.</translation>
</message>
<message>
<source>Open Configuration File</source>
- <translation>Open Konfigurasie Lêer</translation>
+ <translation>Open Configuration File</translation>
</message>
<message>
<source>Reset all client options to default.</source>
- <translation>Alle kliëntopsies na verstek terugstel.</translation>
+ <translation>Reset all client options to default.</translation>
</message>
<message>
<source>&amp;Reset Options</source>
- <translation>&amp;Herstel Opsies</translation>
+ <translation>&amp;Reset Options</translation>
</message>
<message>
<source>&amp;Network</source>
- <translation>&amp;Netwerk</translation>
+ <translation>&amp;Network</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Prune &amp;block storage to</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Reverting this setting requires re-downloading the entire blockchain.</translation>
+ </message>
+ <message>
+ <source>MiB</source>
+ <translation>MiB</translation>
+ </message>
+ <message>
+ <source>(0 = auto, &lt;0 = leave that many cores free)</source>
+ <translation>(0 = auto, &lt;0 = leave that many cores free)</translation>
</message>
<message>
<source>W&amp;allet</source>
- <translation>&amp;Beursie</translation>
+ <translation>W&amp;allet</translation>
</message>
<message>
<source>Expert</source>
- <translation>Kenner</translation>
+ <translation>Expert</translation>
</message>
<message>
<source>Enable coin &amp;control features</source>
- <translation>Bemagtig munt &amp;beheer funksies.</translation>
+ <translation>Enable coin &amp;control features</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
</message>
<message>
<source>&amp;Spend unconfirmed change</source>
- <translation>&amp;Spandeer onbevestigde kleingeld</translation>
+ <translation>&amp;Spend unconfirmed change</translation>
+ </message>
+ <message>
+ <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
+ <translation>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</translation>
+ </message>
+ <message>
+ <source>Map port using &amp;UPnP</source>
+ <translation>Map port using &amp;UPnP</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>Accept connections from outside.</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Allow incomin&amp;g connections</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <translation>Connect to the Bitcoin network through a SOCKS5 proxy.</translation>
+ </message>
+ <message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Connect through SOCKS5 proxy (default proxy):</translation>
+ </message>
+ <message>
+ <source>Proxy &amp;IP:</source>
+ <translation>Proxy &amp;IP:</translation>
</message>
<message>
<source>&amp;Port:</source>
<translation>&amp;Port:</translation>
</message>
<message>
+ <source>Port of the proxy (e.g. 9050)</source>
+ <translation>Port of the proxy (e.g. 9050)</translation>
+ </message>
+ <message>
<source>Used for reaching peers via:</source>
- <translation>Word gebruik vir die bereik van eweknieë via:</translation>
+ <translation>Used for reaching peers via:</translation>
</message>
<message>
<source>IPv4</source>
@@ -913,28 +1275,56 @@
<translation>Tor</translation>
</message>
<message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
- <translation>&amp;Venster</translation>
+ <translation>&amp;Window</translation>
+ </message>
+ <message>
+ <source>Show only a tray icon after minimizing the window.</source>
+ <translation>Show only a tray icon after minimizing the window.</translation>
+ </message>
+ <message>
+ <source>&amp;Minimize to the tray instead of the taskbar</source>
+ <translation>&amp;Minimize to the tray instead of the taskbar</translation>
</message>
<message>
<source>M&amp;inimize on close</source>
- <translation>V&amp;erminder op toemaak</translation>
+ <translation>M&amp;inimize on close</translation>
</message>
<message>
<source>&amp;Display</source>
- <translation>&amp;Vertoon</translation>
+ <translation>&amp;Display</translation>
</message>
<message>
<source>User Interface &amp;language:</source>
- <translation>Gebruikers Koppelvlak &amp;taal:</translation>
+ <translation>User Interface &amp;language:</translation>
+ </message>
+ <message>
+ <source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <translation>The user interface language can be set here. This setting will take effect after restarting %1.</translation>
</message>
<message>
<source>&amp;Unit to show amounts in:</source>
- <translation>&amp;Eenheid om bedrae te toon in:</translation>
+ <translation>&amp;Unit to show amounts in:</translation>
+ </message>
+ <message>
+ <source>Choose the default subdivision unit to show in the interface and when sending coins.</source>
+ <translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation>
</message>
<message>
<source>Whether to show coin control features or not.</source>
- <translation>Of om munt beheer funksies te wys of nie.</translation>
+ <translation>Whether to show coin control features or not.</translation>
+ </message>
+ <message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>&amp;Third party transaction URLs</translation>
+ </message>
+ <message>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation>Options set in this dialog are overridden by the command line or in the configuration file:</translation>
</message>
<message>
<source>&amp;OK</source>
@@ -942,135 +1332,207 @@
</message>
<message>
<source>&amp;Cancel</source>
- <translation>&amp;Kanselleer</translation>
+ <translation>&amp;Cancel</translation>
</message>
<message>
<source>default</source>
- <translation>standaard</translation>
+ <translation>default</translation>
</message>
<message>
<source>none</source>
- <translation>niks</translation>
+ <translation>none</translation>
</message>
<message>
<source>Confirm options reset</source>
- <translation>Bevestig terugstel van opsies</translation>
+ <translation>Confirm options reset</translation>
</message>
<message>
<source>Client restart required to activate changes.</source>
- <translation>Kliënt moet herbegin word om veranderinge te aktiveer.</translation>
+ <translation>Client restart required to activate changes.</translation>
</message>
<message>
<source>Client will be shut down. Do you want to proceed?</source>
- <translation>Kliënt sal toegemaak word. Wil u voortgaan?</translation>
+ <translation>Client will be shut down. Do you want to proceed?</translation>
</message>
<message>
<source>Configuration options</source>
- <translation>Konfigurasie opsies</translation>
+ <translation>Configuration options</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</translation>
</message>
<message>
<source>Error</source>
- <translation>Fout</translation>
+ <translation>Error</translation>
</message>
<message>
<source>The configuration file could not be opened.</source>
- <translation>Die konfigurasie lêer kon nie oopgemaak word nie.</translation>
+ <translation>The configuration file could not be opened.</translation>
</message>
<message>
<source>This change would require a client restart.</source>
- <translation>Hierdie verandering sal 'n herbegin van die kliënt vereis. </translation>
+ <translation>This change would require a client restart.</translation>
</message>
<message>
<source>The supplied proxy address is invalid.</source>
- <translation>Die verskafde volmag adres is ongeldig.</translation>
+ <translation>The supplied proxy address is invalid.</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
<message>
<source>Form</source>
- <translation>Vorm</translation>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <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>
<source>Watch-only:</source>
- <translation>Kyk-net:</translation>
+ <translation>Watch-only:</translation>
</message>
<message>
<source>Available:</source>
- <translation>Beskikbaar:</translation>
+ <translation>Available:</translation>
</message>
<message>
<source>Your current spendable balance</source>
- <translation>U huidige bruikbare balans</translation>
+ <translation>Your current spendable balance</translation>
</message>
<message>
<source>Pending:</source>
- <translation>Hangend:</translation>
+ <translation>Pending:</translation>
+ </message>
+ <message>
+ <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>
<source>Immature:</source>
- <translation>Onvolwasse:</translation>
+ <translation>Immature:</translation>
+ </message>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>Mined balance that has not yet matured</translation>
</message>
<message>
<source>Balances</source>
- <translation>Balans</translation>
+ <translation>Balances</translation>
</message>
<message>
<source>Total:</source>
- <translation>Totaal:</translation>
+ <translation>Total:</translation>
</message>
<message>
<source>Your current total balance</source>
- <translation>U huidige totale balans</translation>
+ <translation>Your current total balance</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Your current balance in watch-only addresses</translation>
</message>
<message>
<source>Spendable:</source>
- <translation>Besteebaar:</translation>
+ <translation>Spendable:</translation>
</message>
<message>
<source>Recent transactions</source>
- <translation>Onlangse transaksies</translation>
+ <translation>Recent transactions</translation>
+ </message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Unconfirmed transactions to watch-only addresses</translation>
</message>
- </context>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Mined balance in watch-only addresses that has not yet matured</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Current total balance in watch-only addresses</translation>
+ </message>
+</context>
<context>
<name>PaymentServer</name>
<message>
<source>Payment request error</source>
- <translation>Betalings versoek fout</translation>
+ <translation>Payment request error</translation>
+ </message>
+ <message>
+ <source>Cannot start bitcoin: click-to-pay handler</source>
+ <translation>Cannot start bitcoin: click-to-pay handler</translation>
</message>
<message>
<source>URI handling</source>
- <translation>URI hantering</translation>
+ <translation>URI handling</translation>
+ </message>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</translation>
+ </message>
+ <message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Cannot process payment request because BIP70 is not supported.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</translation>
+ </message>
+ <message>
+ <source>Invalid payment address %1</source>
+ <translation>Invalid payment address %1</translation>
</message>
- </context>
+ <message>
+ <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <translation>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation>
+ </message>
+ <message>
+ <source>Payment request file handling</source>
+ <translation>Payment request file handling</translation>
+ </message>
+</context>
<context>
<name>PeerTableModel</name>
<message>
<source>User Agent</source>
- <translation>Gebruikeragent</translation>
+ <translation>User Agent</translation>
+ </message>
+ <message>
+ <source>Node/Service</source>
+ <translation>Node/Service</translation>
</message>
<message>
<source>NodeId</source>
- <translation>NodusId</translation>
+ <translation>NodeId</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>Ping</translation>
</message>
<message>
<source>Sent</source>
- <translation>Gestuur</translation>
+ <translation>Sent</translation>
</message>
<message>
<source>Received</source>
- <translation>Ontvang</translation>
+ <translation>Received</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
- <translation>Bedrag</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>Enter a Bitcoin address (e.g. %1)</source>
- <translation>Voer in 'n Bitcoin adres (bv. %1)</translation>
+ <translation>Enter a Bitcoin address (e.g. %1)</translation>
</message>
<message>
<source>%1 d</source>
@@ -1090,19 +1552,43 @@
</message>
<message>
<source>None</source>
- <translation>Geen</translation>
+ <translation>None</translation>
</message>
<message>
<source>N/A</source>
- <translation>n.v.t.</translation>
+ <translation>N/A</translation>
</message>
<message>
<source>%1 ms</source>
<translation>%1 ms</translation>
</message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>%n second</numerusform><numerusform>%n seconds</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>%n minute</numerusform><numerusform>%n minutes</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>%n hour</numerusform><numerusform>%n hours</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>%n day</numerusform><numerusform>%n days</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>%n week</numerusform><numerusform>%n weeks</numerusform></translation>
+ </message>
<message>
<source>%1 and %2</source>
- <translation>%1 en %2</translation>
+ <translation>%1 and %2</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>%n year</numerusform><numerusform>%n years</numerusform></translation>
</message>
<message>
<source>%1 B</source>
@@ -1121,98 +1607,274 @@
<translation>%1 GB</translation>
</message>
<message>
+ <source>Error: Specified data directory "%1" does not exist.</source>
+ <translation>Error: Specified data directory "%1" does not exist.</translation>
+ </message>
+ <message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Error: Cannot parse configuration file: %1.</translation>
+ </message>
+ <message>
<source>Error: %1</source>
- <translation>Fout: %1</translation>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
+ <source>%1 didn't yet exit safely...</source>
+ <translation>%1 didn't yet exit safely...</translation>
</message>
<message>
<source>unknown</source>
- <translation>onbekend</translation>
+ <translation>unknown</translation>
</message>
</context>
<context>
<name>QRImageWidget</name>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Stoor beeld</translation>
+ <translation>&amp;Save Image...</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;Copy Image</translation>
</message>
<message>
<source>Resulting URI too long, try to reduce the text for label / message.</source>
- <translation>Gevolglike URI te lank, probeer teks verkort vir etiket/boodskap</translation>
+ <translation>Resulting URI too long, try to reduce the text for label / message.</translation>
</message>
<message>
<source>Error encoding URI into QR Code.</source>
- <translation>Fout met enkodering van URI na QR kode</translation>
+ <translation>Error encoding URI into QR Code.</translation>
+ </message>
+ <message>
+ <source>QR code support not available.</source>
+ <translation>QR code support not available.</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>Save QR Code</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG Image (*.png)</translation>
</message>
- </context>
+</context>
<context>
<name>RPCConsole</name>
<message>
<source>N/A</source>
- <translation>n.v.t.</translation>
+ <translation>N/A</translation>
</message>
<message>
<source>Client version</source>
- <translation>Kliëntweergawe</translation>
+ <translation>Client version</translation>
</message>
<message>
<source>&amp;Information</source>
- <translation>Informasie</translation>
+ <translation>&amp;Information</translation>
</message>
<message>
<source>General</source>
- <translation>Algemeen</translation>
+ <translation>General</translation>
+ </message>
+ <message>
+ <source>Using BerkeleyDB version</source>
+ <translation>Using BerkeleyDB version</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>Datadir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the data directory use the '%1' option.</source>
+ <translation>To specify a non-default location of the data directory use the '%1' option.</translation>
+ </message>
+ <message>
+ <source>Blocksdir</source>
+ <translation>Blocksdir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the blocks directory use the '%1' option.</source>
+ <translation>To specify a non-default location of the blocks directory use the '%1' option.</translation>
+ </message>
+ <message>
+ <source>Startup time</source>
+ <translation>Startup time</translation>
</message>
<message>
<source>Network</source>
- <translation>Netwerk</translation>
+ <translation>Network</translation>
</message>
<message>
<source>Name</source>
- <translation>Naam</translation>
+ <translation>Name</translation>
</message>
<message>
<source>Number of connections</source>
- <translation>Aantal verbindings</translation>
+ <translation>Number of connections</translation>
</message>
<message>
<source>Block chain</source>
- <translation>Blokketting</translation>
+ <translation>Block chain</translation>
</message>
<message>
<source>Current number of blocks</source>
- <translation>Huidige aantal blokke</translation>
+ <translation>Current number of blocks</translation>
+ </message>
+ <message>
+ <source>Memory Pool</source>
+ <translation>Memory Pool</translation>
+ </message>
+ <message>
+ <source>Current number of transactions</source>
+ <translation>Current number of transactions</translation>
+ </message>
+ <message>
+ <source>Memory usage</source>
+ <translation>Memory usage</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Wallet: </translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(none)</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;Reset</translation>
</message>
<message>
<source>Received</source>
- <translation>Ontvang</translation>
+ <translation>Received</translation>
</message>
<message>
<source>Sent</source>
- <translation>Gestuur</translation>
+ <translation>Sent</translation>
+ </message>
+ <message>
+ <source>&amp;Peers</source>
+ <translation>&amp;Peers</translation>
</message>
<message>
<source>Banned peers</source>
- <translation>Verbanne porture</translation>
+ <translation>Banned peers</translation>
+ </message>
+ <message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>Select a peer to view detailed information.</translation>
</message>
<message>
<source>Whitelisted</source>
- <translation>Gewitlys</translation>
+ <translation>Whitelisted</translation>
</message>
<message>
<source>Direction</source>
- <translation>Rigting</translation>
+ <translation>Direction</translation>
</message>
<message>
<source>Version</source>
- <translation>Weergawe</translation>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <source>Starting Block</source>
+ <translation>Starting Block</translation>
+ </message>
+ <message>
+ <source>Synced Headers</source>
+ <translation>Synced Headers</translation>
+ </message>
+ <message>
+ <source>Synced Blocks</source>
+ <translation>Synced Blocks</translation>
+ </message>
+ <message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>The mapped Autonomous System used for diversifying peer selection.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapped AS</translation>
</message>
<message>
<source>User Agent</source>
- <translation>Gebruikeragent</translation>
+ <translation>User Agent</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>Node window</translation>
+ </message>
+ <message>
+ <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <translation>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>Decrease font size</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Increase font size</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Services</translation>
+ </message>
+ <message>
+ <source>Ban Score</source>
+ <translation>Ban Score</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>Connection Time</translation>
+ </message>
+ <message>
+ <source>Last Send</source>
+ <translation>Last Send</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>Last Receive</translation>
+ </message>
+ <message>
+ <source>Ping Time</source>
+ <translation>Ping Time</translation>
+ </message>
+ <message>
+ <source>The duration of a currently outstanding ping.</source>
+ <translation>The duration of a currently outstanding ping.</translation>
+ </message>
+ <message>
+ <source>Ping Wait</source>
+ <translation>Ping Wait</translation>
+ </message>
+ <message>
+ <source>Min Ping</source>
+ <translation>Min Ping</translation>
+ </message>
+ <message>
+ <source>Time Offset</source>
+ <translation>Time Offset</translation>
</message>
<message>
<source>Last block time</source>
- <translation>Laaste blok tyd</translation>
+ <translation>Last block time</translation>
+ </message>
+ <message>
+ <source>&amp;Open</source>
+ <translation>&amp;Open</translation>
+ </message>
+ <message>
+ <source>&amp;Console</source>
+ <translation>&amp;Console</translation>
+ </message>
+ <message>
+ <source>&amp;Network Traffic</source>
+ <translation>&amp;Network Traffic</translation>
+ </message>
+ <message>
+ <source>Totals</source>
+ <translation>Totals</translation>
</message>
<message>
<source>In:</source>
@@ -1220,15 +1882,23 @@
</message>
<message>
<source>Out:</source>
- <translation>Uit:</translation>
+ <translation>Out:</translation>
+ </message>
+ <message>
+ <source>Debug log file</source>
+ <translation>Debug log file</translation>
+ </message>
+ <message>
+ <source>Clear console</source>
+ <translation>Clear console</translation>
</message>
<message>
<source>1 &amp;hour</source>
- <translation>1 &amp;uur</translation>
+ <translation>1 &amp;hour</translation>
</message>
<message>
<source>1 &amp;day</source>
- <translation>1 &amp;dag</translation>
+ <translation>1 &amp;day</translation>
</message>
<message>
<source>1 &amp;week</source>
@@ -1236,27 +1906,55 @@
</message>
<message>
<source>1 &amp;year</source>
- <translation>1 &amp;jaar</translation>
+ <translation>1 &amp;year</translation>
</message>
<message>
<source>&amp;Disconnect</source>
- <translation>&amp;Ontkoppel</translation>
+ <translation>&amp;Disconnect</translation>
</message>
<message>
<source>Ban for</source>
- <translation>Verbied vir</translation>
+ <translation>Ban for</translation>
</message>
<message>
<source>&amp;Unban</source>
- <translation>&amp;Toegelaat</translation>
+ <translation>&amp;Unban</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 RPC console.</source>
+ <translation>Welcome to the %1 RPC console.</translation>
+ </message>
+ <message>
+ <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <translation>Use up and down arrows to navigate history, and %1 to clear screen.</translation>
+ </message>
+ <message>
+ <source>Type %1 for an overview of available commands.</source>
+ <translation>Type %1 for an overview of available commands.</translation>
+ </message>
+ <message>
+ <source>For more information on using this console type %1.</source>
+ <translation>For more information on using this console type %1.</translation>
+ </message>
+ <message>
+ <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>
+ <translation>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.</translation>
</message>
<message>
<source>Network activity disabled</source>
- <translation>Netwerk aktiewiteit gedeaktiveer</translation>
+ <translation>Network activity disabled</translation>
+ </message>
+ <message>
+ <source>Executing command without any wallet</source>
+ <translation>Executing command without any wallet</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Executing command using "%1" wallet</translation>
</message>
<message>
<source>(node id: %1)</source>
- <translation>(nodus id: %1)</translation>
+ <translation>(node id: %1)</translation>
</message>
<message>
<source>via %1</source>
@@ -1264,97 +1962,149 @@
</message>
<message>
<source>never</source>
- <translation>nooit</translation>
+ <translation>never</translation>
</message>
<message>
<source>Inbound</source>
- <translation>Inkomende</translation>
+ <translation>Inbound</translation>
</message>
<message>
<source>Outbound</source>
- <translation>Uitgaande</translation>
+ <translation>Outbound</translation>
</message>
<message>
<source>Yes</source>
- <translation>Ja</translation>
+ <translation>Yes</translation>
</message>
<message>
<source>No</source>
- <translation>Nee</translation>
+ <translation>No</translation>
</message>
<message>
<source>Unknown</source>
- <translation>Onbekend</translation>
+ <translation>Unknown</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>&amp;Amount:</source>
- <translation>&amp;Bedrag:</translation>
+ <translation>&amp;Amount:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Etiket:</translation>
+ <translation>&amp;Label:</translation>
</message>
<message>
<source>&amp;Message:</source>
- <translation>&amp;Boodskap:</translation>
+ <translation>&amp;Message:</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>An optional label to associate with the new receiving address.</translation>
+ </message>
+ <message>
+ <source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <translation>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <translation>An optional amount to request. Leave this empty or zero to not request a specific amount.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>An optional message that is attached to the payment request and may be displayed to the sender.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Create new receiving address</translation>
</message>
<message>
<source>Clear all fields of the form.</source>
- <translation>Vee alle velde op die vorm skoon</translation>
+ <translation>Clear all fields of the form.</translation>
</message>
<message>
<source>Clear</source>
- <translation>Skoonmaak</translation>
+ <translation>Clear</translation>
+ </message>
+ <message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generate native segwit (Bech32) address</translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>Requested payments history</translation>
+ </message>
+ <message>
+ <source>Show the selected request (does the same as double clicking an entry)</source>
+ <translation>Show the selected request (does the same as double clicking an entry)</translation>
</message>
<message>
<source>Show</source>
- <translation>Wys</translation>
+ <translation>Show</translation>
+ </message>
+ <message>
+ <source>Remove the selected entries from the list</source>
+ <translation>Remove the selected entries from the list</translation>
</message>
<message>
<source>Remove</source>
- <translation>Verwyder</translation>
+ <translation>Remove</translation>
+ </message>
+ <message>
+ <source>Copy URI</source>
+ <translation>Copy URI</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Kopieer etiket</translation>
+ <translation>Copy label</translation>
</message>
<message>
<source>Copy message</source>
- <translation>Kopieer boodskap</translation>
+ <translation>Copy message</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Kopieer bedrag</translation>
+ <translation>Copy amount</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>QR Code</source>
- <translation>QR Kode</translation>
+ <translation>QR Code</translation>
</message>
<message>
<source>Copy &amp;URI</source>
- <translation>Kopieer &amp;URI</translation>
+ <translation>Copy &amp;URI</translation>
</message>
<message>
<source>Copy &amp;Address</source>
- <translation>Kopieer &amp;Address</translation>
+ <translation>Copy &amp;Address</translation>
</message>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Stoor beeld</translation>
+ <translation>&amp;Save Image...</translation>
</message>
<message>
<source>Request payment to %1</source>
- <translation>Versoek betaling van %1</translation>
+ <translation>Request payment to %1</translation>
</message>
<message>
<source>Payment information</source>
- <translation>Betaling informasie</translation>
+ <translation>Payment information</translation>
</message>
<message>
<source>URI</source>
@@ -1362,314 +2112,404 @@
</message>
<message>
<source>Address</source>
- <translation>Adres</translation>
+ <translation>Address</translation>
</message>
<message>
<source>Amount</source>
- <translation>Bedrag</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>Label</source>
- <translation>Etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Message</source>
- <translation>Boodskap</translation>
+ <translation>Message</translation>
</message>
<message>
<source>Wallet</source>
- <translation>Beursie</translation>
+ <translation>Wallet</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
- <translation>Datum</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Label</source>
- <translation>Etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Message</source>
- <translation>Boodskap</translation>
+ <translation>Message</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(geen etiket)</translation>
+ <translation>(no label)</translation>
</message>
<message>
<source>(no message)</source>
- <translation>(geen boodskap)</translation>
+ <translation>(no message)</translation>
</message>
<message>
<source>(no amount requested)</source>
- <translation>(geen bedrag versoek)</translation>
+ <translation>(no amount requested)</translation>
</message>
<message>
<source>Requested</source>
- <translation>Versoekte</translation>
+ <translation>Requested</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Send Coins</source>
- <translation>Stuur Munstukke</translation>
+ <translation>Send Coins</translation>
</message>
<message>
<source>Coin Control Features</source>
- <translation>Munt Beheer Kenmerke</translation>
+ <translation>Coin Control Features</translation>
</message>
<message>
<source>Inputs...</source>
- <translation>Insette...</translation>
+ <translation>Inputs...</translation>
</message>
<message>
<source>automatically selected</source>
- <translation>outomaties gekies</translation>
+ <translation>automatically selected</translation>
</message>
<message>
<source>Insufficient funds!</source>
- <translation>Onvoldoende fondse</translation>
+ <translation>Insufficient funds!</translation>
</message>
<message>
<source>Quantity:</source>
- <translation>Hoeveelheid:</translation>
+ <translation>Quantity:</translation>
</message>
<message>
<source>Bytes:</source>
- <translation>Grepe:</translation>
+ <translation>Bytes:</translation>
</message>
<message>
<source>Amount:</source>
- <translation>Bedrag:</translation>
+ <translation>Amount:</translation>
</message>
<message>
<source>Fee:</source>
- <translation>Fooi:</translation>
+ <translation>Fee:</translation>
</message>
<message>
<source>After Fee:</source>
- <translation>Na Fooi:</translation>
+ <translation>After Fee:</translation>
</message>
<message>
<source>Change:</source>
- <translation>Verander:</translation>
+ <translation>Change:</translation>
+ </message>
+ <message>
+ <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
+ <translation>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</translation>
+ </message>
+ <message>
+ <source>Custom change address</source>
+ <translation>Custom change address</translation>
</message>
<message>
<source>Transaction Fee:</source>
- <translation>Transaksie fooi:</translation>
+ <translation>Transaction Fee:</translation>
</message>
<message>
<source>Choose...</source>
- <translation>Kies...</translation>
+ <translation>Choose...</translation>
</message>
<message>
<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>Die verstekfooi kan veroorsaak dat 'n transaksie gestuur word wat
-etlike ure of dae (of nooit) sal neem om te bevestig. Oorweeg om
-'n fooi met die hand te kies, of wag tot jy die hele ketting bevestig het.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Warning: Fee estimation is currently not possible.</source>
- <translation>Waarskuwing: fooiskatting is tans onbeskikbaar</translation>
+ <translation>Warning: Fee estimation is currently not possible.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>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.</translation>
</message>
<message>
<source>per kilobyte</source>
- <translation>per kilogreep</translation>
+ <translation>per kilobyte</translation>
</message>
<message>
<source>Hide</source>
- <translation>Steek weg</translation>
+ <translation>Hide</translation>
</message>
<message>
<source>Recommended:</source>
- <translation>Aanbeveel:</translation>
+ <translation>Recommended:</translation>
</message>
<message>
<source>Custom:</source>
- <translation>Aangepaste:</translation>
+ <translation>Custom:</translation>
</message>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
- <translation>(Slimfooi nog nie opgestel nie. Dit neem gewoonlik 'n paar blokke...)</translation>
+ <translation>(Smart fee not initialized yet. This usually takes a few blocks...)</translation>
</message>
<message>
<source>Send to multiple recipients at once</source>
- <translation>Stuur aan vele ontvangers op eens</translation>
+ <translation>Send to multiple recipients at once</translation>
</message>
<message>
<source>Add &amp;Recipient</source>
- <translation>Voeg by &amp;Ontvanger</translation>
+ <translation>Add &amp;Recipient</translation>
</message>
<message>
<source>Clear all fields of the form.</source>
- <translation>Vee alle velde op die vorm skoon</translation>
+ <translation>Clear all fields of the form.</translation>
</message>
<message>
<source>Dust:</source>
- <translation>Stof:</translation>
+ <translation>Dust:</translation>
+ </message>
+ <message>
+ <source>Hide transaction fee settings</source>
+ <translation>Hide transaction fee settings</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>A too low fee might result in a never confirming transaction (read the tooltip)</translation>
</message>
<message>
<source>Confirmation time target:</source>
- <translation>Bevestigingstyd teiken:</translation>
+ <translation>Confirmation time target:</translation>
</message>
<message>
<source>Enable Replace-By-Fee</source>
- <translation>Bemoontlik vervang-deur-fooi</translation>
+ <translation>Enable Replace-By-Fee</translation>
</message>
<message>
<source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
- <translation>Met Vervang-Met-Fooi (BIP-125) kan jy 'n transaskiefooi verhoog nadat dit gestuur is.
-Daarsonder mag 'n hoër fooi dalk aanbeveel word om te kompenseer vir 'n verhoogde
-transaksievertragingsrisiko.</translation>
+ <translation>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</translation>
</message>
<message>
<source>Clear &amp;All</source>
- <translation>Vee &amp;Alles skoon</translation>
+ <translation>Clear &amp;All</translation>
</message>
<message>
<source>Balance:</source>
- <translation>Balans:</translation>
+ <translation>Balance:</translation>
</message>
<message>
<source>Confirm the send action</source>
- <translation>Bevestig stuuraksie</translation>
+ <translation>Confirm the send action</translation>
</message>
<message>
<source>S&amp;end</source>
- <translation>S&amp;tuur</translation>
+ <translation>S&amp;end</translation>
</message>
<message>
<source>Copy quantity</source>
- <translation>Kopieer hoeveelheid</translation>
+ <translation>Copy quantity</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Kopieer bedrag</translation>
+ <translation>Copy amount</translation>
</message>
<message>
<source>Copy fee</source>
- <translation>Kopieer fooi</translation>
+ <translation>Copy fee</translation>
</message>
<message>
<source>Copy after fee</source>
- <translation>Kopieer na fooi</translation>
+ <translation>Copy after fee</translation>
</message>
<message>
<source>Copy bytes</source>
- <translation>Kopieer grepe</translation>
+ <translation>Copy bytes</translation>
</message>
<message>
<source>Copy dust</source>
- <translation>Kopieer stof</translation>
+ <translation>Copy dust</translation>
</message>
<message>
<source>Copy change</source>
- <translation>Kopieer verandering</translation>
+ <translation>Copy change</translation>
</message>
<message>
<source>%1 (%2 blocks)</source>
- <translation>%1 (%2 blokke)</translation>
+ <translation>%1 (%2 blocks)</translation>
+ </message>
+ <message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Cr&amp;eate Unsigned</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
+ <source> from wallet '%1'</source>
+ <translation> from wallet '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1 to '%2'</translation>
</message>
<message>
<source>%1 to %2</source>
- <translation>%1 tot %2</translation>
+ <translation>%1 to %2</translation>
+ </message>
+ <message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Do you want to draft this transaction?</translation>
</message>
<message>
<source>Are you sure you want to send?</source>
- <translation>Is u seker u wil verstuur?</translation>
+ <translation>Are you sure you want to send?</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
</message>
<message>
<source>or</source>
- <translation>of</translation>
+ <translation>or</translation>
</message>
<message>
<source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
- <translation>U kan die fooi later verhoog (sein Vervang-met-Fooi, BIP-125)</translation>
+ <translation>You can increase the fee later (signals Replace-By-Fee, BIP-125).</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Please, review your transaction.</translation>
</message>
<message>
<source>Transaction fee</source>
- <translation>Transaksie fooi</translation>
+ <translation>Transaction fee</translation>
</message>
<message>
<source>Not signalling Replace-By-Fee, BIP-125.</source>
- <translation>Sein nie Vervang-Met-Fooi nie, BIP-25</translation>
+ <translation>Not signalling Replace-By-Fee, BIP-125.</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Total Amount</translation>
+ </message>
+ <message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>To review recipient list click "Show Details..."</translation>
</message>
<message>
<source>Confirm send coins</source>
- <translation>Bevestig versending van munte</translation>
+ <translation>Confirm send coins</translation>
+ </message>
+ <message>
+ <source>Confirm transaction proposal</source>
+ <translation>Confirm transaction proposal</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Copy PSBT to clipboard</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Send</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copied</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Watch-only balance:</translation>
</message>
<message>
<source>The recipient address is not valid. Please recheck.</source>
- <translation>Die ontvangeradres is ongeldig. Kyk asseblief weer mooi.</translation>
+ <translation>The recipient address is not valid. Please recheck.</translation>
</message>
<message>
<source>The amount to pay must be larger than 0.</source>
- <translation>Bedrag moet groter as nul wees</translation>
+ <translation>The amount to pay must be larger than 0.</translation>
</message>
<message>
<source>The amount exceeds your balance.</source>
- <translation>Die bedrag oorskry jou saldo</translation>
+ <translation>The amount exceeds your balance.</translation>
</message>
<message>
<source>The total exceeds your balance when the %1 transaction fee is included.</source>
- <translation>Die somtotaal oorskry jou saldo as die %1 transaksiefooi ingereken word</translation>
+ <translation>The total exceeds your balance when the %1 transaction fee is included.</translation>
</message>
<message>
<source>Duplicate address found: addresses should only be used once each.</source>
- <translation>Duplikaatadres: adresse behoort slegs eenkeer gebruik te word</translation>
+ <translation>Duplicate address found: addresses should only be used once each.</translation>
</message>
<message>
<source>Transaction creation failed!</source>
- <translation>Transaksieopstelling het gefaal</translation>
+ <translation>Transaction creation failed!</translation>
</message>
<message>
<source>A fee higher than %1 is considered an absurdly high fee.</source>
- <translation>'n Fooi hoër as %1 word as buitensporig beskou</translation>
+ <translation>A fee higher than %1 is considered an absurdly high fee.</translation>
</message>
<message>
<source>Payment request expired.</source>
- <translation>Betalings versoek verstryk.</translation>
+ <translation>Payment request expired.</translation>
+ </message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>Estimated to begin confirmation within %n block.</numerusform><numerusform>Estimated to begin confirmation within %n blocks.</numerusform></translation>
</message>
<message>
<source>Warning: Invalid Bitcoin address</source>
- <translation>Waarskuwing: Ongeldige Bitcoinadres</translation>
+ <translation>Warning: Invalid Bitcoin address</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Warning: Unknown change address</translation>
+ </message>
+ <message>
+ <source>Confirm custom change address</source>
+ <translation>Confirm custom change address</translation>
</message>
<message>
<source>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>
- <translation>Die adres wat u gekies het vir verandering is nie deel van hierdie
-beursie nie. Enige of alle fondse mag dalk daarheen gestuur word.
-Is u seker?</translation>
+ <translation>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?</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(geen etiket)</translation>
+ <translation>(no label)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>A&amp;mount:</source>
- <translation>&amp;Bedrag:</translation>
+ <translation>A&amp;mount:</translation>
</message>
<message>
<source>Pay &amp;To:</source>
- <translation>Betaal &amp;Vir:</translation>
+ <translation>Pay &amp;To:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Etiket:</translation>
+ <translation>&amp;Label:</translation>
</message>
<message>
<source>Choose previously used address</source>
- <translation>Kies voorheen gebruikte adres</translation>
+ <translation>Choose previously used address</translation>
</message>
<message>
<source>The Bitcoin address to send the payment to</source>
- <translation>Die Bitcoinadres waarheen die betaling gestuur word</translation>
+ <translation>The Bitcoin address to send the payment to</translation>
</message>
<message>
<source>Alt+A</source>
@@ -1677,57 +2517,93 @@ Is u seker?</translation>
</message>
<message>
<source>Paste address from clipboard</source>
- <translation>Plak adres van aanknipbord af</translation>
+ <translation>Paste address from clipboard</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
</message>
<message>
<source>Remove this entry</source>
- <translation>Verwyder hierdie inskrywing</translation>
+ <translation>Remove this entry</translation>
+ </message>
+ <message>
+ <source>The amount to send in the selected unit</source>
+ <translation>The amount to send in the selected unit</translation>
</message>
<message>
<source>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>
- <translation>De fooi word afgetrek van die gestuurde bedrag.
-Die ontvanger sal minder ontvang as wat u in die
-bedrag opgee. As daar meer as een ontvanger is,
-word die fooi eweredig verdeel.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>S&amp;ubtract fee from amount</source>
- <translation>Bedrag &amp;Sonder fooi </translation>
+ <translation>S&amp;ubtract fee from amount</translation>
</message>
<message>
<source>Use available balance</source>
- <translation>Gebruik beskikbare saldo</translation>
+ <translation>Use available balance</translation>
</message>
<message>
<source>Message:</source>
- <translation>Boodskap:</translation>
+ <translation>Message:</translation>
</message>
<message>
<source>This is an unauthenticated payment request.</source>
- <translation>Hierdie is 'n ongemagtigde uitbetalingsversoek</translation>
+ <translation>This is an unauthenticated payment request.</translation>
</message>
<message>
<source>This is an authenticated payment request.</source>
- <translation>Hierdie is 'n gemagtigde uitbetalingsversoek
-</translation>
+ <translation>This is an authenticated payment request.</translation>
+ </message>
+ <message>
+ <source>Enter a label for this address to add it to the list of used addresses</source>
+ <translation>Enter a label for this address to add it to the list of used addresses</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
</message>
<message>
<source>Pay To:</source>
- <translation>Betaal Vir:</translation>
+ <translation>Pay To:</translation>
+ </message>
+ <message>
+ <source>Memo:</source>
+ <translation>Memo:</translation>
</message>
- </context>
+</context>
<context>
<name>ShutdownWindow</name>
- </context>
+ <message>
+ <source>%1 is shutting down...</source>
+ <translation>%1 is shutting down...</translation>
+ </message>
+ <message>
+ <source>Do not shut down the computer until this window disappears.</source>
+ <translation>Do not shut down the computer until this window disappears.</translation>
+ </message>
+</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>Signatures - Sign / Verify a Message</translation>
+ </message>
+ <message>
<source>&amp;Sign Message</source>
- <translation>&amp;Teken boodskap</translation>
+ <translation>&amp;Sign Message</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to sign the message with</source>
+ <translation>The Bitcoin address to sign the message with</translation>
</message>
<message>
<source>Choose previously used address</source>
- <translation>Kies voorheen gebruikte adres</translation>
+ <translation>Choose previously used address</translation>
</message>
<message>
<source>Alt+A</source>
@@ -1735,35 +2611,127 @@ word die fooi eweredig verdeel.</translation>
</message>
<message>
<source>Paste address from clipboard</source>
- <translation>Plak adres van aanknipbord af</translation>
+ <translation>Paste address from clipboard</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Enter the message you want to sign here</source>
+ <translation>Enter the message you want to sign here</translation>
</message>
<message>
<source>Signature</source>
- <translation>Handtekening</translation>
+ <translation>Signature</translation>
+ </message>
+ <message>
+ <source>Copy the current signature to the system clipboard</source>
+ <translation>Copy the current signature to the system clipboard</translation>
+ </message>
+ <message>
+ <source>Sign the message to prove you own this Bitcoin address</source>
+ <translation>Sign the message to prove you own this Bitcoin address</translation>
</message>
<message>
<source>Sign &amp;Message</source>
- <translation>Teken &amp;Boodskap</translation>
+ <translation>Sign &amp;Message</translation>
+ </message>
+ <message>
+ <source>Reset all sign message fields</source>
+ <translation>Reset all sign message fields</translation>
</message>
<message>
<source>Clear &amp;All</source>
- <translation>Vee &amp;Alles skoon</translation>
+ <translation>Clear &amp;All</translation>
</message>
<message>
<source>&amp;Verify Message</source>
- <translation>&amp;Verifieer Boodskap</translation>
+ <translation>&amp;Verify Message</translation>
+ </message>
+ <message>
+ <source>Enter the receiver'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>
+ <translation>Enter the receiver'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!</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address the message was signed with</source>
+ <translation>The Bitcoin address the message was signed with</translation>
+ </message>
+ <message>
+ <source>The signed message to verify</source>
+ <translation>The signed message to verify</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>The signature given when the message was signed</translation>
+ </message>
+ <message>
+ <source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
+ <translation>Verify the message to ensure it was signed with the specified Bitcoin address</translation>
</message>
<message>
<source>Verify &amp;Message</source>
- <translation>Verifieer &amp;Boodskap</translation>
+ <translation>Verify &amp;Message</translation>
+ </message>
+ <message>
+ <source>Reset all verify message fields</source>
+ <translation>Reset all verify message fields</translation>
+ </message>
+ <message>
+ <source>Click "Sign Message" to generate signature</source>
+ <translation>Click "Sign Message" to generate signature</translation>
+ </message>
+ <message>
+ <source>The entered address is invalid.</source>
+ <translation>The entered address is invalid.</translation>
+ </message>
+ <message>
+ <source>Please check the address and try again.</source>
+ <translation>Please check the address and try again.</translation>
+ </message>
+ <message>
+ <source>The entered address does not refer to a key.</source>
+ <translation>The entered address does not refer to a key.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock was cancelled.</source>
+ <translation>Wallet unlock was cancelled.</translation>
+ </message>
+ <message>
+ <source>No error</source>
+ <translation>No error</translation>
+ </message>
+ <message>
+ <source>Private key for the entered address is not available.</source>
+ <translation>Private key for the entered address is not available.</translation>
+ </message>
+ <message>
+ <source>Message signing failed.</source>
+ <translation>Message signing failed.</translation>
</message>
<message>
<source>Message signed.</source>
- <translation>Boodskap geteken.</translation>
+ <translation>Message signed.</translation>
+ </message>
+ <message>
+ <source>The signature could not be decoded.</source>
+ <translation>The signature could not be decoded.</translation>
+ </message>
+ <message>
+ <source>Please check the signature and try again.</source>
+ <translation>Please check the signature and try again.</translation>
+ </message>
+ <message>
+ <source>The signature did not match the message digest.</source>
+ <translation>The signature did not match the message digest.</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>Message verification failed.</translation>
</message>
<message>
<source>Message verified.</source>
- <translation>Boodskap geverifieer.</translation>
+ <translation>Message verified.</translation>
</message>
</context>
<context>
@@ -1775,246 +2743,422 @@ word die fooi eweredig verdeel.</translation>
</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
+ </message>
+ <message>
+ <source>Open until %1</source>
+ <translation>Open until %1</translation>
+ </message>
+ <message>
+ <source>conflicted with a transaction with %1 confirmations</source>
+ <translation>conflicted with a transaction with %1 confirmations</translation>
+ </message>
+ <message>
+ <source>0/unconfirmed, %1</source>
+ <translation>0/unconfirmed, %1</translation>
+ </message>
+ <message>
+ <source>in memory pool</source>
+ <translation>in memory pool</translation>
+ </message>
+ <message>
+ <source>not in memory pool</source>
+ <translation>not in memory pool</translation>
+ </message>
+ <message>
+ <source>abandoned</source>
+ <translation>abandoned</translation>
+ </message>
+ <message>
+ <source>%1/unconfirmed</source>
+ <translation>%1/unconfirmed</translation>
+ </message>
+ <message>
+ <source>%1 confirmations</source>
+ <translation>%1 confirmations</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
<message>
<source>Date</source>
- <translation>Datum</translation>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>Source</translation>
+ </message>
+ <message>
+ <source>Generated</source>
+ <translation>Generated</translation>
</message>
<message>
<source>From</source>
- <translation>Van</translation>
+ <translation>From</translation>
</message>
<message>
<source>unknown</source>
- <translation>onbekend</translation>
+ <translation>unknown</translation>
</message>
<message>
<source>To</source>
- <translation>Na</translation>
+ <translation>To</translation>
</message>
<message>
<source>own address</source>
- <translation>eie adres</translation>
+ <translation>own address</translation>
</message>
<message>
<source>watch-only</source>
- <translation>kyk-net</translation>
+ <translation>watch-only</translation>
</message>
<message>
<source>label</source>
- <translation>etiket</translation>
+ <translation>label</translation>
</message>
<message>
<source>Credit</source>
- <translation>Krediet</translation>
+ <translation>Credit</translation>
+ </message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>matures in %n more block</numerusform><numerusform>matures in %n more blocks</numerusform></translation>
</message>
<message>
<source>not accepted</source>
- <translation>nie aanvaar nie</translation>
+ <translation>not accepted</translation>
</message>
<message>
<source>Debit</source>
- <translation>Debiet</translation>
+ <translation>Debit</translation>
</message>
<message>
<source>Total debit</source>
- <translation>Totale debiet</translation>
+ <translation>Total debit</translation>
</message>
<message>
<source>Total credit</source>
- <translation>Totale crediet</translation>
+ <translation>Total credit</translation>
</message>
<message>
<source>Transaction fee</source>
- <translation>Transaksie fooi</translation>
+ <translation>Transaction fee</translation>
</message>
<message>
<source>Net amount</source>
- <translation>Netto bedrag</translation>
+ <translation>Net amount</translation>
</message>
<message>
<source>Message</source>
- <translation>Boodskap</translation>
+ <translation>Message</translation>
</message>
<message>
<source>Comment</source>
- <translation>Kommentaar</translation>
+ <translation>Comment</translation>
</message>
<message>
<source>Transaction ID</source>
- <translation>Transaksie ID</translation>
+ <translation>Transaction ID</translation>
</message>
<message>
<source>Transaction total size</source>
- <translation>Transaksie totale grootte</translation>
+ <translation>Transaction total size</translation>
+ </message>
+ <message>
+ <source>Transaction virtual size</source>
+ <translation>Transaction virtual size</translation>
+ </message>
+ <message>
+ <source>Output index</source>
+ <translation>Output index</translation>
+ </message>
+ <message>
+ <source> (Certificate was not verified)</source>
+ <translation> (Certificate was not verified)</translation>
+ </message>
+ <message>
+ <source>Merchant</source>
+ <translation>Merchant</translation>
+ </message>
+ <message>
+ <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <translation>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</translation>
+ </message>
+ <message>
+ <source>Debug information</source>
+ <translation>Debug information</translation>
</message>
<message>
<source>Transaction</source>
- <translation>Transaksie</translation>
+ <translation>Transaction</translation>
+ </message>
+ <message>
+ <source>Inputs</source>
+ <translation>Inputs</translation>
</message>
<message>
<source>Amount</source>
- <translation>Bedrag</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>true</source>
- <translation>waar</translation>
+ <translation>true</translation>
</message>
<message>
<source>false</source>
- <translation>onwaar</translation>
+ <translation>false</translation>
</message>
</context>
<context>
<name>TransactionDescDialog</name>
- </context>
+ <message>
+ <source>This pane shows a detailed description of the transaction</source>
+ <translation>This pane shows a detailed description of the transaction</translation>
+ </message>
+ <message>
+ <source>Details for %1</source>
+ <translation>Details for %1</translation>
+ </message>
+</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
- <translation>Datum</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Type</source>
- <translation>Tipe</translation>
+ <translation>Type</translation>
</message>
<message>
<source>Label</source>
- <translation>Etiket</translation>
+ <translation>Label</translation>
+ </message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
+ </message>
+ <message>
+ <source>Open until %1</source>
+ <translation>Open until %1</translation>
+ </message>
+ <message>
+ <source>Unconfirmed</source>
+ <translation>Unconfirmed</translation>
+ </message>
+ <message>
+ <source>Abandoned</source>
+ <translation>Abandoned</translation>
+ </message>
+ <message>
+ <source>Confirming (%1 of %2 recommended confirmations)</source>
+ <translation>Confirming (%1 of %2 recommended confirmations)</translation>
+ </message>
+ <message>
+ <source>Confirmed (%1 confirmations)</source>
+ <translation>Confirmed (%1 confirmations)</translation>
+ </message>
+ <message>
+ <source>Conflicted</source>
+ <translation>Conflicted</translation>
+ </message>
+ <message>
+ <source>Immature (%1 confirmations, will be available after %2)</source>
+ <translation>Immature (%1 confirmations, will be available after %2)</translation>
+ </message>
+ <message>
+ <source>Generated but not accepted</source>
+ <translation>Generated but not accepted</translation>
</message>
<message>
<source>Received with</source>
- <translation>Ontvang met</translation>
+ <translation>Received with</translation>
</message>
<message>
<source>Received from</source>
- <translation>Ontvang van</translation>
+ <translation>Received from</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Gestuur na</translation>
+ <translation>Sent to</translation>
</message>
<message>
<source>Payment to yourself</source>
- <translation>Betalings Aan/na jouself</translation>
+ <translation>Payment to yourself</translation>
</message>
<message>
<source>Mined</source>
- <translation>Gemyn</translation>
+ <translation>Mined</translation>
</message>
<message>
<source>watch-only</source>
- <translation>kyk-net</translation>
+ <translation>watch-only</translation>
</message>
<message>
<source>(n/a)</source>
- <translation>(n.v.t)</translation>
+ <translation>(n/a)</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(geen etiket)</translation>
+ <translation>(no label)</translation>
+ </message>
+ <message>
+ <source>Transaction status. Hover over this field to show number of confirmations.</source>
+ <translation>Transaction status. Hover over this field to show number of confirmations.</translation>
</message>
<message>
<source>Date and time that the transaction was received.</source>
- <translation>Datum en tyd wat die transaksie ontvang was.</translation>
+ <translation>Date and time that the transaction was received.</translation>
</message>
<message>
<source>Type of transaction.</source>
- <translation>Tipe transaksie.</translation>
+ <translation>Type of transaction.</translation>
+ </message>
+ <message>
+ <source>Whether or not a watch-only address is involved in this transaction.</source>
+ <translation>Whether or not a watch-only address is involved in this transaction.</translation>
+ </message>
+ <message>
+ <source>User-defined intent/purpose of the transaction.</source>
+ <translation>User-defined intent/purpose of the transaction.</translation>
</message>
- </context>
+ <message>
+ <source>Amount removed from or added to balance.</source>
+ <translation>Amount removed from or added to balance.</translation>
+ </message>
+</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
- <translation>Alles</translation>
+ <translation>All</translation>
</message>
<message>
<source>Today</source>
- <translation>Vandag</translation>
+ <translation>Today</translation>
</message>
<message>
<source>This week</source>
- <translation>Hierdie week</translation>
+ <translation>This week</translation>
</message>
<message>
<source>This month</source>
- <translation>Hierdie maand</translation>
+ <translation>This month</translation>
</message>
<message>
<source>Last month</source>
- <translation>Verlede maand</translation>
+ <translation>Last month</translation>
</message>
<message>
<source>This year</source>
- <translation>Hierdie jaar</translation>
+ <translation>This year</translation>
</message>
<message>
<source>Range...</source>
- <translation>Reeks...</translation>
+ <translation>Range...</translation>
</message>
<message>
<source>Received with</source>
- <translation>Ontvang met</translation>
+ <translation>Received with</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Gestuur na</translation>
+ <translation>Sent to</translation>
</message>
<message>
<source>To yourself</source>
- <translation>Aan/na jouself</translation>
+ <translation>To yourself</translation>
</message>
<message>
<source>Mined</source>
- <translation>Gemyn</translation>
+ <translation>Mined</translation>
</message>
<message>
<source>Other</source>
- <translation>Ander</translation>
+ <translation>Other</translation>
+ </message>
+ <message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>Enter address, transaction id, or label to search</translation>
</message>
<message>
<source>Min amount</source>
- <translation>Min bedrag</translation>
+ <translation>Min amount</translation>
+ </message>
+ <message>
+ <source>Abandon transaction</source>
+ <translation>Abandon transaction</translation>
+ </message>
+ <message>
+ <source>Increase transaction fee</source>
+ <translation>Increase transaction fee</translation>
</message>
<message>
<source>Copy address</source>
- <translation>Maak kopie van adres</translation>
+ <translation>Copy address</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Kopieer etiket</translation>
+ <translation>Copy label</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Kopieer bedrag</translation>
+ <translation>Copy amount</translation>
</message>
<message>
<source>Copy transaction ID</source>
- <translation>Kopieer transaksie ID</translation>
+ <translation>Copy transaction ID</translation>
+ </message>
+ <message>
+ <source>Copy raw transaction</source>
+ <translation>Copy raw transaction</translation>
+ </message>
+ <message>
+ <source>Copy full transaction details</source>
+ <translation>Copy full transaction details</translation>
+ </message>
+ <message>
+ <source>Edit label</source>
+ <translation>Edit label</translation>
+ </message>
+ <message>
+ <source>Show transaction details</source>
+ <translation>Show transaction details</translation>
+ </message>
+ <message>
+ <source>Export Transaction History</source>
+ <translation>Export Transaction History</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>Koma geskeide lêer (*.csv)</translation>
+ <translation>Comma separated file (*.csv)</translation>
</message>
<message>
<source>Confirmed</source>
- <translation>Bevestig</translation>
+ <translation>Confirmed</translation>
+ </message>
+ <message>
+ <source>Watch-only</source>
+ <translation>Watch-only</translation>
</message>
<message>
<source>Date</source>
- <translation>Datum</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Type</source>
- <translation>Tipe</translation>
+ <translation>Type</translation>
</message>
<message>
<source>Label</source>
- <translation>Etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Address</source>
- <translation>Adres</translation>
+ <translation>Address</translation>
</message>
<message>
<source>ID</source>
@@ -2022,133 +3166,655 @@ word die fooi eweredig verdeel.</translation>
</message>
<message>
<source>Exporting Failed</source>
- <translation>Uitvoering Misluk</translation>
+ <translation>Exporting Failed</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the transaction history to %1.</source>
+ <translation>There was an error trying to save the transaction history to %1.</translation>
+ </message>
+ <message>
+ <source>Exporting Successful</source>
+ <translation>Exporting Successful</translation>
+ </message>
+ <message>
+ <source>The transaction history was successfully saved to %1.</source>
+ <translation>The transaction history was successfully saved to %1.</translation>
</message>
<message>
<source>Range:</source>
- <translation>Reeks:</translation>
+ <translation>Range:</translation>
</message>
<message>
<source>to</source>
- <translation>aan</translation>
+ <translation>to</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
- </context>
+ <message>
+ <source>Unit to show amounts in. Click to select another unit.</source>
+ <translation>Unit to show amounts in. Click to select another unit.</translation>
+ </message>
+</context>
<context>
<name>WalletController</name>
- </context>
+ <message>
+ <source>Close wallet</source>
+ <translation>Close wallet</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
- </context>
+ <message>
+ <source>No wallet has been loaded.</source>
+ <translation>No wallet has been loaded.</translation>
+ </message>
+</context>
<context>
<name>WalletModel</name>
<message>
<source>Send Coins</source>
- <translation>Stuur Munstukke</translation>
+ <translation>Send Coins</translation>
+ </message>
+ <message>
+ <source>Fee bump error</source>
+ <translation>Fee bump error</translation>
+ </message>
+ <message>
+ <source>Increasing transaction fee failed</source>
+ <translation>Increasing transaction fee failed</translation>
+ </message>
+ <message>
+ <source>Do you want to increase the fee?</source>
+ <translation>Do you want to increase the fee?</translation>
+ </message>
+ <message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Do you want to draft a transaction with fee increase?</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>Current fee:</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>Increase:</translation>
</message>
<message>
<source>New fee:</source>
- <translation>Nuwe fooi:</translation>
+ <translation>New fee:</translation>
</message>
- </context>
+ <message>
+ <source>Confirm fee bump</source>
+ <translation>Confirm fee bump</translation>
+ </message>
+ <message>
+ <source>Can't draft transaction.</source>
+ <translation>Can't draft transaction.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copied</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>Can't sign transaction.</translation>
+ </message>
+ <message>
+ <source>Could not commit transaction</source>
+ <translation>Could not commit transaction</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
- <translation>&amp;Uitvoer</translation>
+ <translation>&amp;Export</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Voer inligting uit van die huidige blad na n lêer</translation>
+ <translation>Export the data in the current tab to a file</translation>
+ </message>
+ <message>
+ <source>Backup Wallet</source>
+ <translation>Backup Wallet</translation>
+ </message>
+ <message>
+ <source>Wallet Data (*.dat)</source>
+ <translation>Wallet Data (*.dat)</translation>
+ </message>
+ <message>
+ <source>Backup Failed</source>
+ <translation>Backup Failed</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the wallet data to %1.</source>
+ <translation>There was an error trying to save the wallet data to %1.</translation>
+ </message>
+ <message>
+ <source>Backup Successful</source>
+ <translation>Backup Successful</translation>
+ </message>
+ <message>
+ <source>The wallet data was successfully saved to %1.</source>
+ <translation>The wallet data was successfully saved to %1.</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Cancel</translation>
</message>
- </context>
+</context>
<context>
<name>bitcoin-core</name>
<message>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <translation>Distributed under the MIT software license, see the accompanying file %s or %s</translation>
+ </message>
+ <message>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <translation>Prune configured below the minimum of %d MiB. Please use a higher number.</translation>
+ </message>
+ <message>
+ <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>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</translation>
+ </message>
+ <message>
+ <source>Error: A fatal internal error occurred, see debug.log for details</source>
+ <translation>Error: A fatal internal error occurred, see debug.log for details</translation>
+ </message>
+ <message>
+ <source>Pruning blockstore...</source>
+ <translation>Pruning blockstore...</translation>
+ </message>
+ <message>
+ <source>Unable to start HTTP server. See debug log for details.</source>
+ <translation>Unable to start HTTP server. See debug log for details.</translation>
+ </message>
+ <message>
+ <source>The %s developers</source>
+ <translation>The %s developers</translation>
+ </message>
+ <message>
+ <source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
+ <translation>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</translation>
+ </message>
+ <message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Cannot obtain a lock on data directory %s. %s is probably already running.</translation>
+ </message>
+ <message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Cannot provide specific connections and have addrman find outgoing connections at the same.</translation>
+ </message>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation>
+ </message>
+ <message>
+ <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <translation>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</translation>
+ </message>
+ <message>
+ <source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <translation>Please contribute if you find %s useful. Visit %s for further information about the software.</translation>
+ </message>
+ <message>
+ <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
+ <translation>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</translation>
+ </message>
+ <message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <translation>This is the transaction fee you may discard if change is smaller than dust at this level</translation>
+ </message>
+ <message>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <translation>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</translation>
+ </message>
+ <message>
+ <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
+ <translation>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</translation>
+ </message>
+ <message>
+ <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>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</translation>
+ </message>
+ <message>
+ <source>%d of last 100 blocks have unexpected version</source>
+ <translation>%d of last 100 blocks have unexpected version</translation>
+ </message>
+ <message>
+ <source>%s corrupt, salvage failed</source>
+ <translation>%s corrupt, salvage failed</translation>
+ </message>
+ <message>
+ <source>-maxmempool must be at least %d MB</source>
+ <translation>-maxmempool must be at least %d MB</translation>
+ </message>
+ <message>
+ <source>Cannot resolve -%s address: '%s'</source>
+ <translation>Cannot resolve -%s address: '%s'</translation>
+ </message>
+ <message>
+ <source>Change index out of range</source>
+ <translation>Change index out of range</translation>
+ </message>
+ <message>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation>Config setting for %s only applied on %s network when in [%s] section.</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>Copyright (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Corrupted block database detected</source>
+ <translation>Corrupted block database detected</translation>
+ </message>
+ <message>
+ <source>Could not find asmap file %s</source>
+ <translation>Could not find asmap file %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Could not parse asmap file %s</translation>
+ </message>
+ <message>
+ <source>Do you want to rebuild the block database now?</source>
+ <translation>Do you want to rebuild the block database now?</translation>
+ </message>
+ <message>
+ <source>Error initializing block database</source>
+ <translation>Error initializing block database</translation>
+ </message>
+ <message>
+ <source>Error initializing wallet database environment %s!</source>
+ <translation>Error initializing wallet database environment %s!</translation>
+ </message>
+ <message>
<source>Error loading %s</source>
- <translation>Fout met laai %s</translation>
+ <translation>Error loading %s</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Error loading %s: Private keys can only be disabled during creation</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>Error loading %s: Wallet corrupted</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>Error loading %s: Wallet requires newer version of %s</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>Error loading block database</translation>
+ </message>
+ <message>
+ <source>Error opening block database</source>
+ <translation>Error opening block database</translation>
+ </message>
+ <message>
+ <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>
+ </message>
+ <message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Failed to rescan the wallet during initialization</translation>
</message>
<message>
<source>Importing...</source>
- <translation>Invoer proses tans besig..</translation>
+ <translation>Importing...</translation>
+ </message>
+ <message>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
+ </message>
+ <message>
+ <source>Initialization sanity check failed. %s is shutting down.</source>
+ <translation>Initialization sanity check failed. %s is shutting down.</translation>
+ </message>
+ <message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Invalid P2P permission: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -%s=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Specified blocks directory "%s" does not exist.</translation>
+ </message>
+ <message>
+ <source>Unknown address type '%s'</source>
+ <translation>Unknown address type '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Unknown change type '%s'</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Upgrading txindex database</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>Loading P2P addresses...</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Error: Disk space is too low!</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Loading banlist...</translation>
+ </message>
+ <message>
+ <source>Not enough file descriptors available.</source>
+ <translation>Not enough file descriptors available.</translation>
+ </message>
+ <message>
+ <source>Prune cannot be configured with a negative value.</source>
+ <translation>Prune cannot be configured with a negative value.</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -txindex.</source>
+ <translation>Prune mode is incompatible with -txindex.</translation>
+ </message>
+ <message>
+ <source>Replaying blocks...</source>
+ <translation>Replaying blocks...</translation>
+ </message>
+ <message>
+ <source>Rewinding blocks...</source>
+ <translation>Rewinding blocks...</translation>
+ </message>
+ <message>
+ <source>The source code is available from %s.</source>
+ <translation>The source code is available from %s.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>Transaction fee and change calculation failed</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <translation>Unable to bind to %s on this computer. %s is probably already running.</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Unable to generate keys</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Unsupported logging category %s=%s.</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>Upgrading UTXO database</translation>
+ </message>
+ <message>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation>User Agent comment (%s) contains unsafe characters.</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>Verifying blocks...</translation>
+ </message>
+ <message>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation>Wallet needed to be rewritten: restart %s to complete</translation>
+ </message>
+ <message>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation>Error: Listening for incoming connections failed (listen returned error %s)</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>The transaction amount is too small to send after the fee has been deducted</translation>
+ </message>
+ <message>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Error reading from database, shutting down.</translation>
+ </message>
+ <message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Error upgrading chainstate database</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is low for %s</source>
+ <translation>Error: Disk space is low for %s</translation>
+ </message>
+ <message>
+ <source>Invalid -onion address or hostname: '%s'</source>
+ <translation>Invalid -onion address or hostname: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid -proxy address or hostname: '%s'</source>
+ <translation>Invalid -proxy address or hostname: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</source>
+ <translation>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</translation>
+ </message>
+ <message>
+ <source>Invalid netmask specified in -whitelist: '%s'</source>
+ <translation>Invalid netmask specified in -whitelist: '%s'</translation>
+ </message>
+ <message>
+ <source>Need to specify a port with -whitebind: '%s'</source>
+ <translation>Need to specify a port with -whitebind: '%s'</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Prune mode is incompatible with -blockfilterindex.</translation>
+ </message>
+ <message>
+ <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <translation>Reducing -maxconnections from %d to %d, because of system limitations.</translation>
+ </message>
+ <message>
+ <source>Section [%s] is not recognized.</source>
+ <translation>Section [%s] is not recognized.</translation>
</message>
<message>
<source>Signing transaction failed</source>
- <translation>Teken van transaksie het misluk</translation>
+ <translation>Signing transaction failed</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Specified -walletdir "%s" does not exist</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Specified -walletdir "%s" is a relative path</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>Specified -walletdir "%s" is not a directory</translation>
+ </message>
+ <message>
+ <source>The specified config file %s does not exist
+</source>
+ <translation>The specified config file %s does not exist
+</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>The transaction amount is too small to pay the fee</translation>
</message>
<message>
<source>This is experimental software.</source>
- <translation>Dié is eksperimentele sagteware.</translation>
+ <translation>This is experimental software.</translation>
</message>
<message>
<source>Transaction amount too small</source>
- <translation>Transaksie bedrag te klein</translation>
+ <translation>Transaction amount too small</translation>
</message>
<message>
<source>Transaction too large</source>
- <translation>Transaksie te groot</translation>
+ <translation>Transaction too large</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer (bind returned error %s)</source>
+ <translation>Unable to bind to %s on this computer (bind returned error %s)</translation>
+ </message>
+ <message>
+ <source>Unable to create the PID file '%s': %s</source>
+ <translation>Unable to create the PID file '%s': %s</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>Unable to generate initial keys</translation>
+ </message>
+ <message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Unknown -blockfilterindex value %s.</translation>
</message>
<message>
<source>Verifying wallet(s)...</source>
- <translation>Besig met verifieer van beursie(s)...</translation>
+ <translation>Verifying wallet(s)...</translation>
+ </message>
+ <message>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation>Warning: unknown new rules activated (versionbit %i)</translation>
+ </message>
+ <message>
+ <source>Zapping all transactions from wallet...</source>
+ <translation>Zapping all transactions from wallet...</translation>
+ </message>
+ <message>
+ <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <translation>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>This is the transaction fee you may pay when fee estimates are not available.</translation>
+ </message>
+ <message>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <translation>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</translation>
+ </message>
+ <message>
+ <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
+ <translation>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</translation>
</message>
<message>
<source>%s is set very high!</source>
- <translation>%s is baie hoog gestel!</translation>
+ <translation>%s is set very high!</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Error loading wallet %s. Duplicate -wallet filename specified.</translation>
</message>
<message>
<source>Starting network threads...</source>
- <translation>Begin tans netwerkdrade...</translation>
+ <translation>Starting network threads...</translation>
+ </message>
+ <message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>The wallet will avoid paying less than the minimum relay fee.</translation>
</message>
<message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
- <translation>Dit is die minimum transaksie fooi wat u betaal op elke transaksie.</translation>
+ <translation>This is the minimum transaction fee you pay on every transaction.</translation>
</message>
<message>
<source>This is the transaction fee you will pay if you send a transaction.</source>
- <translation>Dit is die transaksie fooi wat u sal betaal as u 'n transaksie stuur.</translation>
+ <translation>This is the transaction fee you will pay if you send a transaction.</translation>
</message>
<message>
<source>Transaction amounts must not be negative</source>
- <translation>Transaksies bedrae moet nie negatief wees nie</translation>
+ <translation>Transaction amounts must not be negative</translation>
</message>
<message>
<source>Transaction has too long of a mempool chain</source>
- <translation>Transaksie se mempool ketting is te lank</translation>
+ <translation>Transaction has too long of a mempool chain</translation>
</message>
<message>
<source>Transaction must have at least one recipient</source>
- <translation>Transaksie moet ten minste een ontvanger hê</translation>
+ <translation>Transaction must have at least one recipient</translation>
</message>
<message>
<source>Unknown network specified in -onlynet: '%s'</source>
- <translation>Onbekende netwerk gespesifiseer in -onlynet: '%s'</translation>
+ <translation>Unknown network specified in -onlynet: '%s'</translation>
</message>
<message>
<source>Insufficient funds</source>
- <translation>Onvoldoende fondse</translation>
+ <translation>Insufficient funds</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Warning: Private keys detected in wallet {%s} with disabled private keys</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Cannot write to data directory '%s'; check permissions.</translation>
</message>
<message>
<source>Loading block index...</source>
- <translation>Laai blok indeks...</translation>
+ <translation>Loading block index...</translation>
</message>
<message>
<source>Loading wallet...</source>
- <translation>Laai beursie...</translation>
+ <translation>Loading wallet...</translation>
</message>
<message>
<source>Cannot downgrade wallet</source>
- <translation>Kan nie beursie afgradeer nie</translation>
+ <translation>Cannot downgrade wallet</translation>
</message>
<message>
<source>Rescanning...</source>
- <translation>Word herskandeer...</translation>
+ <translation>Rescanning...</translation>
</message>
<message>
<source>Done loading</source>
- <translation>Klaar gelaai</translation>
+ <translation>Done loading</translation>
</message>
</context>
</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts
index 0d138d353c..0209705b25 100644
--- a/src/qt/locale/bitcoin_ar.ts
+++ b/src/qt/locale/bitcoin_ar.ts
@@ -184,6 +184,10 @@
<translation>ادخل كملة المرور القديمة وكلمة المرور الجديدة للمحفظة.</translation>
</message>
<message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>تذكر أن تشفير محفظتك لا يحمي البيتكوين الخاصة بك بشكل كامل من السرقة من قبل البرامج الخبيثةالتي تصيب حاسوبك</translation>
+ </message>
+ <message>
<source>Wallet to be encrypted</source>
<translation>سوف يتم تشفير محفظتك</translation>
</message>
@@ -756,10 +760,42 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>فشل إنشاء المحفظة</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>تحذير إنشاء محفظة</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
<message>
+ <source>Create Wallet</source>
+ <translation>إنشاء محفظة</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>إسم المحفظة</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>شفر المحفظة. المحفظة سيتم تشفيرها بإستخدام كلمة مرور من إختيارك.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>تشفير محفظة</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>إيقاف المفاتيح الخاصة</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>أنشئ محفظة فارغة</translation>
+ </message>
+ <message>
<source>Create</source>
<translation>إنشاء</translation>
</message>
@@ -803,6 +839,14 @@
<translation>العنوان المدخل "%1" ليس عنوان بيت كوين صحيح.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>العنوان "%1" موجود بالفعل كعنوان إستقبال تحت مسمى "%2" ولذلك لا يمكن إضافته كعنوان إرسال.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>العنوان المدخل "%1" موجود بالفعل في سجل العناوين تحت مسمى " "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation> يمكن فتح المحفظة.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_bn.ts b/src/qt/locale/bitcoin_bn.ts
index 3a68e2847c..5a98c56acd 100644
--- a/src/qt/locale/bitcoin_bn.ts
+++ b/src/qt/locale/bitcoin_bn.ts
@@ -3,184 +3,3818 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>ঠিকানা কিংবা লেভেল সম্পাদনার জন্য রাইট-ক্লিক করুন</translation>
+ <translation>Right-click to edit address or label</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>নতুন একটি ঠিকানা তৈরি করুন</translation>
+ <translation>Create a new address</translation>
</message>
<message>
<source>&amp;New</source>
- <translation>নতুন</translation>
+ <translation>&amp;New</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>Copy the currently selected address to the system clipboard</translation>
</message>
<message>
<source>&amp;Copy</source>
- <translation>কপি/প্রতিলিপি</translation>
+ <translation>&amp;Copy</translation>
</message>
<message>
<source>C&amp;lose</source>
- <translation>কপি/প্রতিলিপি</translation>
+ <translation>C&amp;lose</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>Delete the currently selected address from the list</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>Enter address or label to search</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Export the data in the current tab to a file</translation>
</message>
- </context>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Export</translation>
+ </message>
+ <message>
+ <source>&amp;Delete</source>
+ <translation>&amp;Delete</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>Choose the address to send coins to</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Choose the address to receive coins with</translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>C&amp;hoose</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Sending addresses</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Receiving addresses</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Address</source>
+ <translation>&amp;Copy Address</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Label</source>
+ <translation>Copy &amp;Label</translation>
+ </message>
+ <message>
+ <source>&amp;Edit</source>
+ <translation>&amp;Edit</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Export Address List</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Comma separated file (*.csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Exporting Failed</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>There was an error trying to save the address list to %1. Please try again.</translation>
+ </message>
+</context>
<context>
<name>AddressTableModel</name>
<message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
<source>Address</source>
- <translation>ঠিকানা </translation>
+ <translation>Address</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(no label)</translation>
</message>
- </context>
+</context>
<context>
<name>AskPassphraseDialog</name>
- </context>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>Passphrase Dialog</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>Enter passphrase</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>New passphrase</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>Repeat new passphrase</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>Show passphrase</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Encrypt wallet</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>This operation needs your wallet passphrase to unlock the wallet.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Unlock wallet</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>This operation needs your wallet passphrase to decrypt the wallet.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Decrypt wallet</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Change passphrase</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Confirm wallet encryption</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Are you sure you wish to encrypt your wallet?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Wallet encrypted</translation>
+ </message>
+ <message>
+ <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>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;.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Enter the old passphrase and new passphrase for the wallet.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Wallet to be encrypted</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Your wallet is about to be encrypted. </translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Your wallet is now encrypted. </translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>Wallet encryption failed</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>The supplied passphrases do not match.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>Wallet unlock failed</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>The passphrase entered for the wallet decryption was incorrect.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>Wallet decryption failed</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>Wallet passphrase was successfully changed.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>Warning: The Caps Lock key is on!</translation>
+ </message>
+</context>
<context>
<name>BanTableModel</name>
- </context>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmask</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation>Banned Until</translation>
+ </message>
+</context>
<context>
<name>BitcoinGUI</name>
<message>
+ <source>Sign &amp;message...</source>
+ <translation>Sign &amp;message...</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>Synchronizing with network...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>&amp;Overview</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>Show general overview of wallet</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>&amp;Transactions</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>Browse transaction history</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>E&amp;xit</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>Quit application</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>&amp;About %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>Show information about %1</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>About &amp;Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>Show information about Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>&amp;Options...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>Modify configuration options for %1</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>&amp;Encrypt Wallet...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>&amp;Backup Wallet...</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>&amp;Change Passphrase...</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>Open &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Create Wallet...</source>
+ <translation>Create Wallet...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Create a new wallet</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Wallet:</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>Click to disable network activity.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>Network activity disabled.</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>Click to enable network activity again.</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>Syncing Headers (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>Reindexing blocks on disk...</translation>
+ </message>
+ <message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</translation>
+ </message>
+ <message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>Send coins to a Bitcoin address</translation>
+ </message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>Backup wallet to another location</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>Change the passphrase used for wallet encryption</translation>
+ </message>
+ <message>
+ <source>&amp;Verify message...</source>
+ <translation>&amp;Verify message...</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>&amp;Send</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>&amp;Receive</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>&amp;Show / Hide</translation>
+ </message>
+ <message>
+ <source>Show or hide the main Window</source>
+ <translation>Show or hide the main Window</translation>
+ </message>
+ <message>
+ <source>Encrypt the private keys that belong to your wallet</source>
+ <translation>Encrypt the private keys that belong to your wallet</translation>
+ </message>
+ <message>
+ <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>
+ <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>
+ <source>&amp;File</source>
+ <translation>&amp;File</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>&amp;Settings</translation>
+ </message>
+ <message>
+ <source>&amp;Help</source>
+ <translation>&amp;Help</translation>
+ </message>
+ <message>
+ <source>Tabs toolbar</source>
+ <translation>Tabs toolbar</translation>
+ </message>
+ <message>
+ <source>Request payments (generates QR codes and bitcoin: URIs)</source>
+ <translation>Request payments (generates QR codes and bitcoin: URIs)</translation>
+ </message>
+ <message>
+ <source>Show the list of used sending addresses and labels</source>
+ <translation>Show the list of used sending addresses and labels</translation>
+ </message>
+ <message>
+ <source>Show the list of used receiving addresses and labels</source>
+ <translation>Show the list of used receiving addresses and labels</translation>
+ </message>
+ <message>
+ <source>&amp;Command-line options</source>
+ <translation>&amp;Command-line options</translation>
+ </message>
+ <message numerus="yes">
+ <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>
+ <message>
+ <source>Indexing blocks on disk...</source>
+ <translation>Indexing blocks on disk...</translation>
+ </message>
+ <message>
+ <source>Processing blocks on disk...</source>
+ <translation>Processing blocks on disk...</translation>
+ </message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Processed %n block of transaction history.</numerusform><numerusform>Processed %n blocks of transaction history.</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 behind</source>
+ <translation>%1 behind</translation>
+ </message>
+ <message>
+ <source>Last received block was generated %1 ago.</source>
+ <translation>Last received block was generated %1 ago.</translation>
+ </message>
+ <message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Transactions after this will not yet be visible.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
<source>Warning</source>
- <translation>সতর্কতা</translation>
+ <translation>Warning</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>Information</translation>
+ </message>
+ <message>
+ <source>Up to date</source>
+ <translation>Up to date</translation>
</message>
- </context>
+ <message>
+ <source>Node window</source>
+ <translation>Node window</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Open node debugging and diagnostic console</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses</source>
+ <translation>&amp;Sending addresses</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses</source>
+ <translation>&amp;Receiving addresses</translation>
+ </message>
+ <message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Open a bitcoin: URI</translation>
+ </message>
+ <message>
+ <source>Open Wallet</source>
+ <translation>Open Wallet</translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>Open a wallet</translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>Close Wallet...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>Close wallet</translation>
+ </message>
+ <message>
+ <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
+ <translation>Show the %1 help message to get a list with possible Bitcoin command-line options</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
+ <source>No wallets available</source>
+ <translation>No wallets available</translation>
+ </message>
+ <message>
+ <source>&amp;Window</source>
+ <translation>&amp;Window</translation>
+ </message>
+ <message>
+ <source>Minimize</source>
+ <translation>Minimize</translation>
+ </message>
+ <message>
+ <source>Zoom</source>
+ <translation>Zoom</translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>Main Window</translation>
+ </message>
+ <message>
+ <source>%1 client</source>
+ <translation>%1 client</translation>
+ </message>
+ <message>
+ <source>Connecting to peers...</source>
+ <translation>Connecting to peers...</translation>
+ </message>
+ <message>
+ <source>Catching up...</source>
+ <translation>Catching up...</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
+ <source>Warning: %1</source>
+ <translation>Warning: %1</translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>Date: %1
+</translation>
+ </message>
+ <message>
+ <source>Amount: %1
+</source>
+ <translation>Amount: %1
+</translation>
+ </message>
+ <message>
+ <source>Wallet: %1
+</source>
+ <translation>Wallet: %1
+</translation>
+ </message>
+ <message>
+ <source>Type: %1
+</source>
+ <translation>Type: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>Label: %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>Address: %1
+</translation>
+ </message>
+ <message>
+ <source>Sent transaction</source>
+ <translation>Sent transaction</translation>
+ </message>
+ <message>
+ <source>Incoming transaction</source>
+ <translation>Incoming transaction</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Private key &lt;b&gt;disabled&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
+ <translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
+ <translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</translation>
+ </message>
+</context>
<context>
<name>CoinControlDialog</name>
- </context>
+ <message>
+ <source>Coin Selection</source>
+ <translation>Coin Selection</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Quantity:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bytes:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Amount:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Fee:</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Dust:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>After Fee:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Change:</translation>
+ </message>
+ <message>
+ <source>(un)select all</source>
+ <translation>(un)select all</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Tree mode</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>List mode</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Amount</translation>
+ </message>
+ <message>
+ <source>Received with label</source>
+ <translation>Received with label</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>Received with address</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Confirmations</source>
+ <translation>Confirmations</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Confirmed</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Copy address</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Copy label</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Copy amount</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Copy transaction ID</translation>
+ </message>
+ <message>
+ <source>Lock unspent</source>
+ <translation>Lock unspent</translation>
+ </message>
+ <message>
+ <source>Unlock unspent</source>
+ <translation>Unlock unspent</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Copy quantity</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Copy fee</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Copy after fee</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Copy bytes</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Copy dust</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Copy change</translation>
+ </message>
+ <message>
+ <source>(%1 locked)</source>
+ <translation>(%1 locked)</translation>
+ </message>
+ <message>
+ <source>yes</source>
+ <translation>yes</translation>
+ </message>
+ <message>
+ <source>no</source>
+ <translation>no</translation>
+ </message>
+ <message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>This label turns red if any recipient receives an amount smaller than the current dust threshold.</translation>
+ </message>
+ <message>
+ <source>Can vary +/- %1 satoshi(s) per input.</source>
+ <translation>Can vary +/- %1 satoshi(s) per input.</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(no label)</translation>
+ </message>
+ <message>
+ <source>change from %1 (%2)</source>
+ <translation>change from %1 (%2)</translation>
+ </message>
+ <message>
+ <source>(change)</source>
+ <translation>(change)</translation>
+ </message>
+</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Create wallet failed</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Create wallet warning</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Create Wallet</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Wallet Name</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Encrypt Wallet</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Disable Private Keys</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Make Blank Wallet</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Create</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
- </context>
+ <message>
+ <source>Edit Address</source>
+ <translation>Edit Address</translation>
+ </message>
+ <message>
+ <source>&amp;Label</source>
+ <translation>&amp;Label</translation>
+ </message>
+ <message>
+ <source>The label associated with this address list entry</source>
+ <translation>The label associated with this address list entry</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>The address associated with this address list entry. This can only be modified for sending addresses.</translation>
+ </message>
+ <message>
+ <source>&amp;Address</source>
+ <translation>&amp;Address</translation>
+ </message>
+ <message>
+ <source>New sending address</source>
+ <translation>New sending address</translation>
+ </message>
+ <message>
+ <source>Edit receiving address</source>
+ <translation>Edit receiving address</translation>
+ </message>
+ <message>
+ <source>Edit sending address</source>
+ <translation>Edit sending address</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is not a valid Bitcoin address.</source>
+ <translation>The entered address "%1" is not a valid Bitcoin address.</translation>
+ </message>
+ <message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>The entered address "%1" is already in the address book with label "%2".</translation>
+ </message>
+ <message>
+ <source>Could not unlock wallet.</source>
+ <translation>Could not unlock wallet.</translation>
+ </message>
+ <message>
+ <source>New key generation failed.</source>
+ <translation>New key generation failed.</translation>
+ </message>
+</context>
<context>
<name>FreespaceChecker</name>
- </context>
+ <message>
+ <source>A new data directory will be created.</source>
+ <translation>A new data directory will be created.</translation>
+ </message>
+ <message>
+ <source>name</source>
+ <translation>name</translation>
+ </message>
+ <message>
+ <source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <translation>Directory already exists. Add %1 if you intend to create a new directory here.</translation>
+ </message>
+ <message>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Path already exists, and is not a directory.</translation>
+ </message>
+ <message>
+ <source>Cannot create data directory here.</source>
+ <translation>Cannot create data directory here.</translation>
+ </message>
+</context>
<context>
<name>HelpMessageDialog</name>
- </context>
+ <message>
+ <source>version</source>
+ <translation>version</translation>
+ </message>
+ <message>
+ <source>About %1</source>
+ <translation>About %1</translation>
+ </message>
+ <message>
+ <source>Command-line options</source>
+ <translation>Command-line options</translation>
+ </message>
+</context>
<context>
<name>Intro</name>
- </context>
+ <message>
+ <source>Welcome</source>
+ <translation>Welcome</translation>
+ </message>
+ <message>
+ <source>Welcome to %1.</source>
+ <translation>Welcome to %1.</translation>
+ </message>
+ <message>
+ <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
+ <translation>As this is the first time the program is launched, you can choose where %1 will store its data.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>Use the default data directory</source>
+ <translation>Use the default data directory</translation>
+ </message>
+ <message>
+ <source>Use a custom data directory:</source>
+ <translation>Use a custom data directory:</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Discard blocks after verification, except most recent %1 GB (prune)</translation>
+ </message>
+ <message>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation>At least %1 GB of data will be stored in this directory, and it will grow over time.</translation>
+ </message>
+ <message>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation>Approximately %1 GB of data will be stored in this directory.</translation>
+ </message>
+ <message>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation>%1 will download and store a copy of the Bitcoin block chain.</translation>
+ </message>
+ <message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>The wallet will also be stored in this directory.</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" cannot be created.</source>
+ <translation>Error: Specified data directory "%1" cannot be created.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message numerus="yes">
+ <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">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(of %n GB needed)</numerusform><numerusform>(of %n GB needed)</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n GB needed for full chain)</numerusform><numerusform>(%n GB needed for full chain)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
- </context>
+ <message>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</translation>
+ </message>
+ <message>
+ <source>Number of blocks left</source>
+ <translation>Number of blocks left</translation>
+ </message>
+ <message>
+ <source>Unknown...</source>
+ <translation>Unknown...</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Last block time</translation>
+ </message>
+ <message>
+ <source>Progress</source>
+ <translation>Progress</translation>
+ </message>
+ <message>
+ <source>Progress increase per hour</source>
+ <translation>Progress increase per hour</translation>
+ </message>
+ <message>
+ <source>calculating...</source>
+ <translation>calculating...</translation>
+ </message>
+ <message>
+ <source>Estimated time left until synced</source>
+ <translation>Estimated time left until synced</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Hide</translation>
+ </message>
+ <message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
+ <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>%1 is currently syncing. It will download headers and blocks from peers and validate them until reaching the tip of the block chain.</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1, %2%)...</source>
+ <translation>Unknown. Syncing Headers (%1, %2%)...</translation>
+ </message>
+</context>
<context>
<name>OpenURIDialog</name>
- </context>
+ <message>
+ <source>Open bitcoin URI</source>
+ <translation>Open bitcoin URI</translation>
+ </message>
+ <message>
+ <source>URI:</source>
+ <translation>URI:</translation>
+ </message>
+</context>
<context>
<name>OpenWalletActivity</name>
- </context>
+ <message>
+ <source>Open wallet failed</source>
+ <translation>Open wallet failed</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Open wallet warning</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+ <message>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+</context>
<context>
<name>OptionsDialog</name>
- </context>
+ <message>
+ <source>Options</source>
+ <translation>Options</translation>
+ </message>
+ <message>
+ <source>&amp;Main</source>
+ <translation>&amp;Main</translation>
+ </message>
+ <message>
+ <source>Automatically start %1 after logging in to the system.</source>
+ <translation>Automatically start %1 after logging in to the system.</translation>
+ </message>
+ <message>
+ <source>&amp;Start %1 on system login</source>
+ <translation>&amp;Start %1 on system login</translation>
+ </message>
+ <message>
+ <source>Size of &amp;database cache</source>
+ <translation>Size of &amp;database cache</translation>
+ </message>
+ <message>
+ <source>Number of script &amp;verification threads</source>
+ <translation>Number of script &amp;verification threads</translation>
+ </message>
+ <message>
+ <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <translation>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ </message>
+ <message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</translation>
+ </message>
+ <message>
+ <source>Hide the icon from the system tray.</source>
+ <translation>Hide the icon from the system tray.</translation>
+ </message>
+ <message>
+ <source>&amp;Hide tray icon</source>
+ <translation>&amp;Hide tray icon</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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 |.</translation>
+ </message>
+ <message>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation>Open the %1 configuration file from the working directory.</translation>
+ </message>
+ <message>
+ <source>Open Configuration File</source>
+ <translation>Open Configuration File</translation>
+ </message>
+ <message>
+ <source>Reset all client options to default.</source>
+ <translation>Reset all client options to default.</translation>
+ </message>
+ <message>
+ <source>&amp;Reset Options</source>
+ <translation>&amp;Reset Options</translation>
+ </message>
+ <message>
+ <source>&amp;Network</source>
+ <translation>&amp;Network</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Prune &amp;block storage to</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Reverting this setting requires re-downloading the entire blockchain.</translation>
+ </message>
+ <message>
+ <source>MiB</source>
+ <translation>MiB</translation>
+ </message>
+ <message>
+ <source>(0 = auto, &lt;0 = leave that many cores free)</source>
+ <translation>(0 = auto, &lt;0 = leave that many cores free)</translation>
+ </message>
+ <message>
+ <source>W&amp;allet</source>
+ <translation>W&amp;allet</translation>
+ </message>
+ <message>
+ <source>Expert</source>
+ <translation>Expert</translation>
+ </message>
+ <message>
+ <source>Enable coin &amp;control features</source>
+ <translation>Enable coin &amp;control features</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>&amp;Spend unconfirmed change</source>
+ <translation>&amp;Spend unconfirmed change</translation>
+ </message>
+ <message>
+ <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
+ <translation>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</translation>
+ </message>
+ <message>
+ <source>Map port using &amp;UPnP</source>
+ <translation>Map port using &amp;UPnP</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>Accept connections from outside.</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Allow incomin&amp;g connections</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <translation>Connect to the Bitcoin network through a SOCKS5 proxy.</translation>
+ </message>
+ <message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Connect through SOCKS5 proxy (default proxy):</translation>
+ </message>
+ <message>
+ <source>Proxy &amp;IP:</source>
+ <translation>Proxy &amp;IP:</translation>
+ </message>
+ <message>
+ <source>&amp;Port:</source>
+ <translation>&amp;Port:</translation>
+ </message>
+ <message>
+ <source>Port of the proxy (e.g. 9050)</source>
+ <translation>Port of the proxy (e.g. 9050)</translation>
+ </message>
+ <message>
+ <source>Used for reaching peers via:</source>
+ <translation>Used for reaching peers via:</translation>
+ </message>
+ <message>
+ <source>IPv4</source>
+ <translation>IPv4</translation>
+ </message>
+ <message>
+ <source>IPv6</source>
+ <translation>IPv6</translation>
+ </message>
+ <message>
+ <source>Tor</source>
+ <translation>Tor</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</translation>
+ </message>
+ <message>
+ <source>&amp;Window</source>
+ <translation>&amp;Window</translation>
+ </message>
+ <message>
+ <source>Show only a tray icon after minimizing the window.</source>
+ <translation>Show only a tray icon after minimizing the window.</translation>
+ </message>
+ <message>
+ <source>&amp;Minimize to the tray instead of the taskbar</source>
+ <translation>&amp;Minimize to the tray instead of the taskbar</translation>
+ </message>
+ <message>
+ <source>M&amp;inimize on close</source>
+ <translation>M&amp;inimize on close</translation>
+ </message>
+ <message>
+ <source>&amp;Display</source>
+ <translation>&amp;Display</translation>
+ </message>
+ <message>
+ <source>User Interface &amp;language:</source>
+ <translation>User Interface &amp;language:</translation>
+ </message>
+ <message>
+ <source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <translation>The user interface language can be set here. This setting will take effect after restarting %1.</translation>
+ </message>
+ <message>
+ <source>&amp;Unit to show amounts in:</source>
+ <translation>&amp;Unit to show amounts in:</translation>
+ </message>
+ <message>
+ <source>Choose the default subdivision unit to show in the interface and when sending coins.</source>
+ <translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation>
+ </message>
+ <message>
+ <source>Whether to show coin control features or not.</source>
+ <translation>Whether to show coin control features or not.</translation>
+ </message>
+ <message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>&amp;Third party transaction URLs</translation>
+ </message>
+ <message>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation>Options set in this dialog are overridden by the command line or in the configuration file:</translation>
+ </message>
+ <message>
+ <source>&amp;OK</source>
+ <translation>&amp;OK</translation>
+ </message>
+ <message>
+ <source>&amp;Cancel</source>
+ <translation>&amp;Cancel</translation>
+ </message>
+ <message>
+ <source>default</source>
+ <translation>default</translation>
+ </message>
+ <message>
+ <source>none</source>
+ <translation>none</translation>
+ </message>
+ <message>
+ <source>Confirm options reset</source>
+ <translation>Confirm options reset</translation>
+ </message>
+ <message>
+ <source>Client restart required to activate changes.</source>
+ <translation>Client restart required to activate changes.</translation>
+ </message>
+ <message>
+ <source>Client will be shut down. Do you want to proceed?</source>
+ <translation>Client will be shut down. Do you want to proceed?</translation>
+ </message>
+ <message>
+ <source>Configuration options</source>
+ <translation>Configuration options</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
+ <source>The configuration file could not be opened.</source>
+ <translation>The configuration file could not be opened.</translation>
+ </message>
+ <message>
+ <source>This change would require a client restart.</source>
+ <translation>This change would require a client restart.</translation>
+ </message>
+ <message>
+ <source>The supplied proxy address is invalid.</source>
+ <translation>The supplied proxy address is invalid.</translation>
+ </message>
+</context>
<context>
<name>OverviewPage</name>
- </context>
+ <message>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <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>
+ <source>Watch-only:</source>
+ <translation>Watch-only:</translation>
+ </message>
+ <message>
+ <source>Available:</source>
+ <translation>Available:</translation>
+ </message>
+ <message>
+ <source>Your current spendable balance</source>
+ <translation>Your current spendable balance</translation>
+ </message>
+ <message>
+ <source>Pending:</source>
+ <translation>Pending:</translation>
+ </message>
+ <message>
+ <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>
+ <source>Immature:</source>
+ <translation>Immature:</translation>
+ </message>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>Mined balance that has not yet matured</translation>
+ </message>
+ <message>
+ <source>Balances</source>
+ <translation>Balances</translation>
+ </message>
+ <message>
+ <source>Total:</source>
+ <translation>Total:</translation>
+ </message>
+ <message>
+ <source>Your current total balance</source>
+ <translation>Your current total balance</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Your current balance in watch-only addresses</translation>
+ </message>
+ <message>
+ <source>Spendable:</source>
+ <translation>Spendable:</translation>
+ </message>
+ <message>
+ <source>Recent transactions</source>
+ <translation>Recent transactions</translation>
+ </message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Unconfirmed transactions to watch-only addresses</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Mined balance in watch-only addresses that has not yet matured</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Current total balance in watch-only addresses</translation>
+ </message>
+</context>
<context>
<name>PaymentServer</name>
- </context>
+ <message>
+ <source>Payment request error</source>
+ <translation>Payment request error</translation>
+ </message>
+ <message>
+ <source>Cannot start bitcoin: click-to-pay handler</source>
+ <translation>Cannot start bitcoin: click-to-pay handler</translation>
+ </message>
+ <message>
+ <source>URI handling</source>
+ <translation>URI handling</translation>
+ </message>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</translation>
+ </message>
+ <message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Cannot process payment request because BIP70 is not supported.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</translation>
+ </message>
+ <message>
+ <source>Invalid payment address %1</source>
+ <translation>Invalid payment address %1</translation>
+ </message>
+ <message>
+ <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <translation>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation>
+ </message>
+ <message>
+ <source>Payment request file handling</source>
+ <translation>Payment request file handling</translation>
+ </message>
+</context>
<context>
<name>PeerTableModel</name>
- </context>
+ <message>
+ <source>User Agent</source>
+ <translation>User Agent</translation>
+ </message>
+ <message>
+ <source>Node/Service</source>
+ <translation>Node/Service</translation>
+ </message>
+ <message>
+ <source>NodeId</source>
+ <translation>NodeId</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>Ping</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>Sent</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Received</translation>
+ </message>
+</context>
<context>
<name>QObject</name>
- </context>
+ <message>
+ <source>Amount</source>
+ <translation>Amount</translation>
+ </message>
+ <message>
+ <source>Enter a Bitcoin address (e.g. %1)</source>
+ <translation>Enter a Bitcoin address (e.g. %1)</translation>
+ </message>
+ <message>
+ <source>%1 d</source>
+ <translation>%1 d</translation>
+ </message>
+ <message>
+ <source>%1 h</source>
+ <translation>%1 h</translation>
+ </message>
+ <message>
+ <source>%1 m</source>
+ <translation>%1 m</translation>
+ </message>
+ <message>
+ <source>%1 s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <source>None</source>
+ <translation>None</translation>
+ </message>
+ <message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>%n second</numerusform><numerusform>%n seconds</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>%n minute</numerusform><numerusform>%n minutes</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>%n hour</numerusform><numerusform>%n hours</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>%n day</numerusform><numerusform>%n days</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>%n week</numerusform><numerusform>%n weeks</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 and %2</source>
+ <translation>%1 and %2</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>%n year</numerusform><numerusform>%n years</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 B</source>
+ <translation>%1 B</translation>
+ </message>
+ <message>
+ <source>%1 KB</source>
+ <translation>%1 KB</translation>
+ </message>
+ <message>
+ <source>%1 MB</source>
+ <translation>%1 MB</translation>
+ </message>
+ <message>
+ <source>%1 GB</source>
+ <translation>%1 GB</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" does not exist.</source>
+ <translation>Error: Specified data directory "%1" does not exist.</translation>
+ </message>
+ <message>
+ <source>Error: Cannot parse configuration file: %1.</source>
+ <translation>Error: Cannot parse configuration file: %1.</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
+ <source>%1 didn't yet exit safely...</source>
+ <translation>%1 didn't yet exit safely...</translation>
+ </message>
+ <message>
+ <source>unknown</source>
+ <translation>unknown</translation>
+ </message>
+</context>
<context>
<name>QRImageWidget</name>
- </context>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Save Image...</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;Copy Image</translation>
+ </message>
+ <message>
+ <source>Resulting URI too long, try to reduce the text for label / message.</source>
+ <translation>Resulting URI too long, try to reduce the text for label / message.</translation>
+ </message>
+ <message>
+ <source>Error encoding URI into QR Code.</source>
+ <translation>Error encoding URI into QR Code.</translation>
+ </message>
+ <message>
+ <source>QR code support not available.</source>
+ <translation>QR code support not available.</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>Save QR Code</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG Image (*.png)</translation>
+ </message>
+</context>
<context>
<name>RPCConsole</name>
- </context>
+ <message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>Client version</source>
+ <translation>Client version</translation>
+ </message>
+ <message>
+ <source>&amp;Information</source>
+ <translation>&amp;Information</translation>
+ </message>
+ <message>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <source>Using BerkeleyDB version</source>
+ <translation>Using BerkeleyDB version</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>Datadir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the data directory use the '%1' option.</source>
+ <translation>To specify a non-default location of the data directory use the '%1' option.</translation>
+ </message>
+ <message>
+ <source>Blocksdir</source>
+ <translation>Blocksdir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the blocks directory use the '%1' option.</source>
+ <translation>To specify a non-default location of the blocks directory use the '%1' option.</translation>
+ </message>
+ <message>
+ <source>Startup time</source>
+ <translation>Startup time</translation>
+ </message>
+ <message>
+ <source>Network</source>
+ <translation>Network</translation>
+ </message>
+ <message>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <source>Number of connections</source>
+ <translation>Number of connections</translation>
+ </message>
+ <message>
+ <source>Block chain</source>
+ <translation>Block chain</translation>
+ </message>
+ <message>
+ <source>Current number of blocks</source>
+ <translation>Current number of blocks</translation>
+ </message>
+ <message>
+ <source>Memory Pool</source>
+ <translation>Memory Pool</translation>
+ </message>
+ <message>
+ <source>Current number of transactions</source>
+ <translation>Current number of transactions</translation>
+ </message>
+ <message>
+ <source>Memory usage</source>
+ <translation>Memory usage</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Wallet: </translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(none)</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;Reset</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Received</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>Sent</translation>
+ </message>
+ <message>
+ <source>&amp;Peers</source>
+ <translation>&amp;Peers</translation>
+ </message>
+ <message>
+ <source>Banned peers</source>
+ <translation>Banned peers</translation>
+ </message>
+ <message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>Select a peer to view detailed information.</translation>
+ </message>
+ <message>
+ <source>Whitelisted</source>
+ <translation>Whitelisted</translation>
+ </message>
+ <message>
+ <source>Direction</source>
+ <translation>Direction</translation>
+ </message>
+ <message>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <source>Starting Block</source>
+ <translation>Starting Block</translation>
+ </message>
+ <message>
+ <source>Synced Headers</source>
+ <translation>Synced Headers</translation>
+ </message>
+ <message>
+ <source>Synced Blocks</source>
+ <translation>Synced Blocks</translation>
+ </message>
+ <message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>The mapped Autonomous System used for diversifying peer selection.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapped AS</translation>
+ </message>
+ <message>
+ <source>User Agent</source>
+ <translation>User Agent</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>Node window</translation>
+ </message>
+ <message>
+ <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <translation>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>Decrease font size</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Increase font size</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Services</translation>
+ </message>
+ <message>
+ <source>Ban Score</source>
+ <translation>Ban Score</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>Connection Time</translation>
+ </message>
+ <message>
+ <source>Last Send</source>
+ <translation>Last Send</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>Last Receive</translation>
+ </message>
+ <message>
+ <source>Ping Time</source>
+ <translation>Ping Time</translation>
+ </message>
+ <message>
+ <source>The duration of a currently outstanding ping.</source>
+ <translation>The duration of a currently outstanding ping.</translation>
+ </message>
+ <message>
+ <source>Ping Wait</source>
+ <translation>Ping Wait</translation>
+ </message>
+ <message>
+ <source>Min Ping</source>
+ <translation>Min Ping</translation>
+ </message>
+ <message>
+ <source>Time Offset</source>
+ <translation>Time Offset</translation>
+ </message>
+ <message>
+ <source>Last block time</source>
+ <translation>Last block time</translation>
+ </message>
+ <message>
+ <source>&amp;Open</source>
+ <translation>&amp;Open</translation>
+ </message>
+ <message>
+ <source>&amp;Console</source>
+ <translation>&amp;Console</translation>
+ </message>
+ <message>
+ <source>&amp;Network Traffic</source>
+ <translation>&amp;Network Traffic</translation>
+ </message>
+ <message>
+ <source>Totals</source>
+ <translation>Totals</translation>
+ </message>
+ <message>
+ <source>In:</source>
+ <translation>In:</translation>
+ </message>
+ <message>
+ <source>Out:</source>
+ <translation>Out:</translation>
+ </message>
+ <message>
+ <source>Debug log file</source>
+ <translation>Debug log file</translation>
+ </message>
+ <message>
+ <source>Clear console</source>
+ <translation>Clear console</translation>
+ </message>
+ <message>
+ <source>1 &amp;hour</source>
+ <translation>1 &amp;hour</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;day</translation>
+ </message>
+ <message>
+ <source>1 &amp;week</source>
+ <translation>1 &amp;week</translation>
+ </message>
+ <message>
+ <source>1 &amp;year</source>
+ <translation>1 &amp;year</translation>
+ </message>
+ <message>
+ <source>&amp;Disconnect</source>
+ <translation>&amp;Disconnect</translation>
+ </message>
+ <message>
+ <source>Ban for</source>
+ <translation>Ban for</translation>
+ </message>
+ <message>
+ <source>&amp;Unban</source>
+ <translation>&amp;Unban</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 RPC console.</source>
+ <translation>Welcome to the %1 RPC console.</translation>
+ </message>
+ <message>
+ <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <translation>Use up and down arrows to navigate history, and %1 to clear screen.</translation>
+ </message>
+ <message>
+ <source>Type %1 for an overview of available commands.</source>
+ <translation>Type %1 for an overview of available commands.</translation>
+ </message>
+ <message>
+ <source>For more information on using this console type %1.</source>
+ <translation>For more information on using this console type %1.</translation>
+ </message>
+ <message>
+ <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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>Network activity disabled</translation>
+ </message>
+ <message>
+ <source>Executing command without any wallet</source>
+ <translation>Executing command without any wallet</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Executing command using "%1" wallet</translation>
+ </message>
+ <message>
+ <source>(node id: %1)</source>
+ <translation>(node id: %1)</translation>
+ </message>
+ <message>
+ <source>via %1</source>
+ <translation>via %1</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>never</translation>
+ </message>
+ <message>
+ <source>Inbound</source>
+ <translation>Inbound</translation>
+ </message>
+ <message>
+ <source>Outbound</source>
+ <translation>Outbound</translation>
+ </message>
+ <message>
+ <source>Yes</source>
+ <translation>Yes</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>No</translation>
+ </message>
+ <message>
+ <source>Unknown</source>
+ <translation>Unknown</translation>
+ </message>
+</context>
<context>
<name>ReceiveCoinsDialog</name>
- </context>
+ <message>
+ <source>&amp;Amount:</source>
+ <translation>&amp;Amount:</translation>
+ </message>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Label:</translation>
+ </message>
+ <message>
+ <source>&amp;Message:</source>
+ <translation>&amp;Message:</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>An optional label to associate with the new receiving address.</translation>
+ </message>
+ <message>
+ <source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <translation>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <translation>An optional amount to request. Leave this empty or zero to not request a specific amount.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>An optional message that is attached to the payment request and may be displayed to the sender.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Create new receiving address</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>Clear all fields of the form.</translation>
+ </message>
+ <message>
+ <source>Clear</source>
+ <translation>Clear</translation>
+ </message>
+ <message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generate native segwit (Bech32) address</translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>Requested payments history</translation>
+ </message>
+ <message>
+ <source>Show the selected request (does the same as double clicking an entry)</source>
+ <translation>Show the selected request (does the same as double clicking an entry)</translation>
+ </message>
+ <message>
+ <source>Show</source>
+ <translation>Show</translation>
+ </message>
+ <message>
+ <source>Remove the selected entries from the list</source>
+ <translation>Remove the selected entries from the list</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>Remove</translation>
+ </message>
+ <message>
+ <source>Copy URI</source>
+ <translation>Copy URI</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Copy label</translation>
+ </message>
+ <message>
+ <source>Copy message</source>
+ <translation>Copy message</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Copy amount</translation>
+ </message>
+</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>QR Code</source>
- <translation>QR কোড</translation>
+ <translation>QR Code</translation>
+ </message>
+ <message>
+ <source>Copy &amp;URI</source>
+ <translation>Copy &amp;URI</translation>
+ </message>
+ <message>
+ <source>Copy &amp;Address</source>
+ <translation>Copy &amp;Address</translation>
+ </message>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Save Image...</translation>
+ </message>
+ <message>
+ <source>Request payment to %1</source>
+ <translation>Request payment to %1</translation>
+ </message>
+ <message>
+ <source>Payment information</source>
+ <translation>Payment information</translation>
+ </message>
+ <message>
+ <source>URI</source>
+ <translation>URI</translation>
</message>
<message>
<source>Address</source>
- <translation>ঠিকানা </translation>
+ <translation>Address</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Amount</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Message</translation>
+ </message>
+ <message>
+ <source>Wallet</source>
+ <translation>Wallet</translation>
</message>
- </context>
+</context>
<context>
<name>RecentRequestsTableModel</name>
- </context>
+ <message>
+ <source>Date</source>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Message</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(no label)</translation>
+ </message>
+ <message>
+ <source>(no message)</source>
+ <translation>(no message)</translation>
+ </message>
+ <message>
+ <source>(no amount requested)</source>
+ <translation>(no amount requested)</translation>
+ </message>
+ <message>
+ <source>Requested</source>
+ <translation>Requested</translation>
+ </message>
+</context>
<context>
<name>SendCoinsDialog</name>
- </context>
+ <message>
+ <source>Send Coins</source>
+ <translation>Send Coins</translation>
+ </message>
+ <message>
+ <source>Coin Control Features</source>
+ <translation>Coin Control Features</translation>
+ </message>
+ <message>
+ <source>Inputs...</source>
+ <translation>Inputs...</translation>
+ </message>
+ <message>
+ <source>automatically selected</source>
+ <translation>automatically selected</translation>
+ </message>
+ <message>
+ <source>Insufficient funds!</source>
+ <translation>Insufficient funds!</translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>Quantity:</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>Bytes:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>Amount:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>Fee:</translation>
+ </message>
+ <message>
+ <source>After Fee:</source>
+ <translation>After Fee:</translation>
+ </message>
+ <message>
+ <source>Change:</source>
+ <translation>Change:</translation>
+ </message>
+ <message>
+ <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
+ <translation>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</translation>
+ </message>
+ <message>
+ <source>Custom change address</source>
+ <translation>Custom change address</translation>
+ </message>
+ <message>
+ <source>Transaction Fee:</source>
+ <translation>Transaction Fee:</translation>
+ </message>
+ <message>
+ <source>Choose...</source>
+ <translation>Choose...</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>Warning: Fee estimation is currently not possible.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>per kilobyte</source>
+ <translation>per kilobyte</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Hide</translation>
+ </message>
+ <message>
+ <source>Recommended:</source>
+ <translation>Recommended:</translation>
+ </message>
+ <message>
+ <source>Custom:</source>
+ <translation>Custom:</translation>
+ </message>
+ <message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(Smart fee not initialized yet. This usually takes a few blocks...)</translation>
+ </message>
+ <message>
+ <source>Send to multiple recipients at once</source>
+ <translation>Send to multiple recipients at once</translation>
+ </message>
+ <message>
+ <source>Add &amp;Recipient</source>
+ <translation>Add &amp;Recipient</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>Clear all fields of the form.</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Dust:</translation>
+ </message>
+ <message>
+ <source>Hide transaction fee settings</source>
+ <translation>Hide transaction fee settings</translation>
+ </message>
+ <message>
+ <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>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.</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>A too low fee might result in a never confirming transaction (read the tooltip)</translation>
+ </message>
+ <message>
+ <source>Confirmation time target:</source>
+ <translation>Confirmation time target:</translation>
+ </message>
+ <message>
+ <source>Enable Replace-By-Fee</source>
+ <translation>Enable Replace-By-Fee</translation>
+ </message>
+ <message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</translation>
+ </message>
+ <message>
+ <source>Clear &amp;All</source>
+ <translation>Clear &amp;All</translation>
+ </message>
+ <message>
+ <source>Balance:</source>
+ <translation>Balance:</translation>
+ </message>
+ <message>
+ <source>Confirm the send action</source>
+ <translation>Confirm the send action</translation>
+ </message>
+ <message>
+ <source>S&amp;end</source>
+ <translation>S&amp;end</translation>
+ </message>
+ <message>
+ <source>Copy quantity</source>
+ <translation>Copy quantity</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Copy amount</translation>
+ </message>
+ <message>
+ <source>Copy fee</source>
+ <translation>Copy fee</translation>
+ </message>
+ <message>
+ <source>Copy after fee</source>
+ <translation>Copy after fee</translation>
+ </message>
+ <message>
+ <source>Copy bytes</source>
+ <translation>Copy bytes</translation>
+ </message>
+ <message>
+ <source>Copy dust</source>
+ <translation>Copy dust</translation>
+ </message>
+ <message>
+ <source>Copy change</source>
+ <translation>Copy change</translation>
+ </message>
+ <message>
+ <source>%1 (%2 blocks)</source>
+ <translation>%1 (%2 blocks)</translation>
+ </message>
+ <message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Cr&amp;eate Unsigned</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
+ <source> from wallet '%1'</source>
+ <translation> from wallet '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1 to '%2'</translation>
+ </message>
+ <message>
+ <source>%1 to %2</source>
+ <translation>%1 to %2</translation>
+ </message>
+ <message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Do you want to draft this transaction?</translation>
+ </message>
+ <message>
+ <source>Are you sure you want to send?</source>
+ <translation>Are you sure you want to send?</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>or</translation>
+ </message>
+ <message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>You can increase the fee later (signals Replace-By-Fee, BIP-125).</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Please, review your transaction.</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>Transaction fee</translation>
+ </message>
+ <message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Not signalling Replace-By-Fee, BIP-125.</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Total Amount</translation>
+ </message>
+ <message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>To review recipient list click "Show Details..."</translation>
+ </message>
+ <message>
+ <source>Confirm send coins</source>
+ <translation>Confirm send coins</translation>
+ </message>
+ <message>
+ <source>Confirm transaction proposal</source>
+ <translation>Confirm transaction proposal</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Copy PSBT to clipboard</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Send</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copied</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Watch-only balance:</translation>
+ </message>
+ <message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>The recipient address is not valid. Please recheck.</translation>
+ </message>
+ <message>
+ <source>The amount to pay must be larger than 0.</source>
+ <translation>The amount to pay must be larger than 0.</translation>
+ </message>
+ <message>
+ <source>The amount exceeds your balance.</source>
+ <translation>The amount exceeds your balance.</translation>
+ </message>
+ <message>
+ <source>The total exceeds your balance when the %1 transaction fee is included.</source>
+ <translation>The total exceeds your balance when the %1 transaction fee is included.</translation>
+ </message>
+ <message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>Duplicate address found: addresses should only be used once each.</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>Transaction creation failed!</translation>
+ </message>
+ <message>
+ <source>A fee higher than %1 is considered an absurdly high fee.</source>
+ <translation>A fee higher than %1 is considered an absurdly high fee.</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>Payment request expired.</translation>
+ </message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>Estimated to begin confirmation within %n block.</numerusform><numerusform>Estimated to begin confirmation within %n blocks.</numerusform></translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Warning: Invalid Bitcoin address</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Warning: Unknown change address</translation>
+ </message>
+ <message>
+ <source>Confirm custom change address</source>
+ <translation>Confirm custom change address</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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?</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(no label)</translation>
+ </message>
+</context>
<context>
<name>SendCoinsEntry</name>
- </context>
+ <message>
+ <source>A&amp;mount:</source>
+ <translation>A&amp;mount:</translation>
+ </message>
+ <message>
+ <source>Pay &amp;To:</source>
+ <translation>Pay &amp;To:</translation>
+ </message>
+ <message>
+ <source>&amp;Label:</source>
+ <translation>&amp;Label:</translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>Choose previously used address</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>The Bitcoin address to send the payment to</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Paste address from clipboard</source>
+ <translation>Paste address from clipboard</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Remove this entry</source>
+ <translation>Remove this entry</translation>
+ </message>
+ <message>
+ <source>The amount to send in the selected unit</source>
+ <translation>The amount to send in the selected unit</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>S&amp;ubtract fee from amount</source>
+ <translation>S&amp;ubtract fee from amount</translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>Use available balance</translation>
+ </message>
+ <message>
+ <source>Message:</source>
+ <translation>Message:</translation>
+ </message>
+ <message>
+ <source>This is an unauthenticated payment request.</source>
+ <translation>This is an unauthenticated payment request.</translation>
+ </message>
+ <message>
+ <source>This is an authenticated payment request.</source>
+ <translation>This is an authenticated payment request.</translation>
+ </message>
+ <message>
+ <source>Enter a label for this address to add it to the list of used addresses</source>
+ <translation>Enter a label for this address to add it to the list of used addresses</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>Pay To:</source>
+ <translation>Pay To:</translation>
+ </message>
+ <message>
+ <source>Memo:</source>
+ <translation>Memo:</translation>
+ </message>
+</context>
<context>
<name>ShutdownWindow</name>
- </context>
+ <message>
+ <source>%1 is shutting down...</source>
+ <translation>%1 is shutting down...</translation>
+ </message>
+ <message>
+ <source>Do not shut down the computer until this window disappears.</source>
+ <translation>Do not shut down the computer until this window disappears.</translation>
+ </message>
+</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>Signatures - Sign / Verify a Message</translation>
+ </message>
+ <message>
+ <source>&amp;Sign Message</source>
+ <translation>&amp;Sign Message</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to sign the message with</source>
+ <translation>The Bitcoin address to sign the message with</translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>Choose previously used address</translation>
+ </message>
+ <message>
+ <source>Alt+A</source>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Paste address from clipboard</source>
+ <translation>Paste address from clipboard</translation>
+ </message>
+ <message>
+ <source>Alt+P</source>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Enter the message you want to sign here</source>
+ <translation>Enter the message you want to sign here</translation>
+ </message>
+ <message>
+ <source>Signature</source>
+ <translation>Signature</translation>
+ </message>
+ <message>
+ <source>Copy the current signature to the system clipboard</source>
+ <translation>Copy the current signature to the system clipboard</translation>
+ </message>
+ <message>
+ <source>Sign the message to prove you own this Bitcoin address</source>
+ <translation>Sign the message to prove you own this Bitcoin address</translation>
+ </message>
+ <message>
+ <source>Sign &amp;Message</source>
+ <translation>Sign &amp;Message</translation>
+ </message>
+ <message>
+ <source>Reset all sign message fields</source>
+ <translation>Reset all sign message fields</translation>
+ </message>
+ <message>
+ <source>Clear &amp;All</source>
+ <translation>Clear &amp;All</translation>
+ </message>
+ <message>
+ <source>&amp;Verify Message</source>
+ <translation>&amp;Verify Message</translation>
+ </message>
+ <message>
+ <source>Enter the receiver'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>
+ <translation>Enter the receiver'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!</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address the message was signed with</source>
+ <translation>The Bitcoin address the message was signed with</translation>
+ </message>
+ <message>
+ <source>The signed message to verify</source>
+ <translation>The signed message to verify</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>The signature given when the message was signed</translation>
+ </message>
+ <message>
+ <source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
+ <translation>Verify the message to ensure it was signed with the specified Bitcoin address</translation>
+ </message>
+ <message>
+ <source>Verify &amp;Message</source>
+ <translation>Verify &amp;Message</translation>
+ </message>
+ <message>
+ <source>Reset all verify message fields</source>
+ <translation>Reset all verify message fields</translation>
+ </message>
+ <message>
+ <source>Click "Sign Message" to generate signature</source>
+ <translation>Click "Sign Message" to generate signature</translation>
+ </message>
+ <message>
<source>The entered address is invalid.</source>
- <translation>প্রবেশকৃত ঠিকানাটি শুদ্ধ নয়।</translation>
+ <translation>The entered address is invalid.</translation>
+ </message>
+ <message>
+ <source>Please check the address and try again.</source>
+ <translation>Please check the address and try again.</translation>
+ </message>
+ <message>
+ <source>The entered address does not refer to a key.</source>
+ <translation>The entered address does not refer to a key.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock was cancelled.</source>
+ <translation>Wallet unlock was cancelled.</translation>
+ </message>
+ <message>
+ <source>No error</source>
+ <translation>No error</translation>
+ </message>
+ <message>
+ <source>Private key for the entered address is not available.</source>
+ <translation>Private key for the entered address is not available.</translation>
+ </message>
+ <message>
+ <source>Message signing failed.</source>
+ <translation>Message signing failed.</translation>
+ </message>
+ <message>
+ <source>Message signed.</source>
+ <translation>Message signed.</translation>
+ </message>
+ <message>
+ <source>The signature could not be decoded.</source>
+ <translation>The signature could not be decoded.</translation>
</message>
<message>
<source>Please check the signature and try again.</source>
- <translation>অনুগ্রহ করে স্বাক্ষরটি পুনরায় পরীক্ষা করে আবারও চেষ্টা করুন।</translation>
+ <translation>Please check the signature and try again.</translation>
+ </message>
+ <message>
+ <source>The signature did not match the message digest.</source>
+ <translation>The signature did not match the message digest.</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>Message verification failed.</translation>
+ </message>
+ <message>
+ <source>Message verified.</source>
+ <translation>Message verified.</translation>
</message>
- </context>
+</context>
<context>
<name>TrafficGraphWidget</name>
- </context>
+ <message>
+ <source>KB/s</source>
+ <translation>KB/s</translation>
+ </message>
+</context>
<context>
<name>TransactionDesc</name>
- </context>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
+ </message>
+ <message>
+ <source>Open until %1</source>
+ <translation>Open until %1</translation>
+ </message>
+ <message>
+ <source>conflicted with a transaction with %1 confirmations</source>
+ <translation>conflicted with a transaction with %1 confirmations</translation>
+ </message>
+ <message>
+ <source>0/unconfirmed, %1</source>
+ <translation>0/unconfirmed, %1</translation>
+ </message>
+ <message>
+ <source>in memory pool</source>
+ <translation>in memory pool</translation>
+ </message>
+ <message>
+ <source>not in memory pool</source>
+ <translation>not in memory pool</translation>
+ </message>
+ <message>
+ <source>abandoned</source>
+ <translation>abandoned</translation>
+ </message>
+ <message>
+ <source>%1/unconfirmed</source>
+ <translation>%1/unconfirmed</translation>
+ </message>
+ <message>
+ <source>%1 confirmations</source>
+ <translation>%1 confirmations</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>Source</translation>
+ </message>
+ <message>
+ <source>Generated</source>
+ <translation>Generated</translation>
+ </message>
+ <message>
+ <source>From</source>
+ <translation>From</translation>
+ </message>
+ <message>
+ <source>unknown</source>
+ <translation>unknown</translation>
+ </message>
+ <message>
+ <source>To</source>
+ <translation>To</translation>
+ </message>
+ <message>
+ <source>own address</source>
+ <translation>own address</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>watch-only</translation>
+ </message>
+ <message>
+ <source>label</source>
+ <translation>label</translation>
+ </message>
+ <message>
+ <source>Credit</source>
+ <translation>Credit</translation>
+ </message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>matures in %n more block</numerusform><numerusform>matures in %n more blocks</numerusform></translation>
+ </message>
+ <message>
+ <source>not accepted</source>
+ <translation>not accepted</translation>
+ </message>
+ <message>
+ <source>Debit</source>
+ <translation>Debit</translation>
+ </message>
+ <message>
+ <source>Total debit</source>
+ <translation>Total debit</translation>
+ </message>
+ <message>
+ <source>Total credit</source>
+ <translation>Total credit</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>Transaction fee</translation>
+ </message>
+ <message>
+ <source>Net amount</source>
+ <translation>Net amount</translation>
+ </message>
+ <message>
+ <source>Message</source>
+ <translation>Message</translation>
+ </message>
+ <message>
+ <source>Comment</source>
+ <translation>Comment</translation>
+ </message>
+ <message>
+ <source>Transaction ID</source>
+ <translation>Transaction ID</translation>
+ </message>
+ <message>
+ <source>Transaction total size</source>
+ <translation>Transaction total size</translation>
+ </message>
+ <message>
+ <source>Transaction virtual size</source>
+ <translation>Transaction virtual size</translation>
+ </message>
+ <message>
+ <source>Output index</source>
+ <translation>Output index</translation>
+ </message>
+ <message>
+ <source> (Certificate was not verified)</source>
+ <translation> (Certificate was not verified)</translation>
+ </message>
+ <message>
+ <source>Merchant</source>
+ <translation>Merchant</translation>
+ </message>
+ <message>
+ <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <translation>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</translation>
+ </message>
+ <message>
+ <source>Debug information</source>
+ <translation>Debug information</translation>
+ </message>
+ <message>
+ <source>Transaction</source>
+ <translation>Transaction</translation>
+ </message>
+ <message>
+ <source>Inputs</source>
+ <translation>Inputs</translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>Amount</translation>
+ </message>
+ <message>
+ <source>true</source>
+ <translation>true</translation>
+ </message>
+ <message>
+ <source>false</source>
+ <translation>false</translation>
+ </message>
+</context>
<context>
<name>TransactionDescDialog</name>
- </context>
+ <message>
+ <source>This pane shows a detailed description of the transaction</source>
+ <translation>This pane shows a detailed description of the transaction</translation>
+ </message>
+ <message>
+ <source>Details for %1</source>
+ <translation>Details for %1</translation>
+ </message>
+</context>
<context>
<name>TransactionTableModel</name>
- </context>
+ <message>
+ <source>Date</source>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>Type</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
+ </message>
+ <message>
+ <source>Open until %1</source>
+ <translation>Open until %1</translation>
+ </message>
+ <message>
+ <source>Unconfirmed</source>
+ <translation>Unconfirmed</translation>
+ </message>
+ <message>
+ <source>Abandoned</source>
+ <translation>Abandoned</translation>
+ </message>
+ <message>
+ <source>Confirming (%1 of %2 recommended confirmations)</source>
+ <translation>Confirming (%1 of %2 recommended confirmations)</translation>
+ </message>
+ <message>
+ <source>Confirmed (%1 confirmations)</source>
+ <translation>Confirmed (%1 confirmations)</translation>
+ </message>
+ <message>
+ <source>Conflicted</source>
+ <translation>Conflicted</translation>
+ </message>
+ <message>
+ <source>Immature (%1 confirmations, will be available after %2)</source>
+ <translation>Immature (%1 confirmations, will be available after %2)</translation>
+ </message>
+ <message>
+ <source>Generated but not accepted</source>
+ <translation>Generated but not accepted</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>Received with</translation>
+ </message>
+ <message>
+ <source>Received from</source>
+ <translation>Received from</translation>
+ </message>
+ <message>
+ <source>Sent to</source>
+ <translation>Sent to</translation>
+ </message>
+ <message>
+ <source>Payment to yourself</source>
+ <translation>Payment to yourself</translation>
+ </message>
+ <message>
+ <source>Mined</source>
+ <translation>Mined</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>watch-only</translation>
+ </message>
+ <message>
+ <source>(n/a)</source>
+ <translation>(n/a)</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(no label)</translation>
+ </message>
+ <message>
+ <source>Transaction status. Hover over this field to show number of confirmations.</source>
+ <translation>Transaction status. Hover over this field to show number of confirmations.</translation>
+ </message>
+ <message>
+ <source>Date and time that the transaction was received.</source>
+ <translation>Date and time that the transaction was received.</translation>
+ </message>
+ <message>
+ <source>Type of transaction.</source>
+ <translation>Type of transaction.</translation>
+ </message>
+ <message>
+ <source>Whether or not a watch-only address is involved in this transaction.</source>
+ <translation>Whether or not a watch-only address is involved in this transaction.</translation>
+ </message>
+ <message>
+ <source>User-defined intent/purpose of the transaction.</source>
+ <translation>User-defined intent/purpose of the transaction.</translation>
+ </message>
+ <message>
+ <source>Amount removed from or added to balance.</source>
+ <translation>Amount removed from or added to balance.</translation>
+ </message>
+</context>
<context>
<name>TransactionView</name>
<message>
+ <source>All</source>
+ <translation>All</translation>
+ </message>
+ <message>
+ <source>Today</source>
+ <translation>Today</translation>
+ </message>
+ <message>
+ <source>This week</source>
+ <translation>This week</translation>
+ </message>
+ <message>
+ <source>This month</source>
+ <translation>This month</translation>
+ </message>
+ <message>
+ <source>Last month</source>
+ <translation>Last month</translation>
+ </message>
+ <message>
+ <source>This year</source>
+ <translation>This year</translation>
+ </message>
+ <message>
+ <source>Range...</source>
+ <translation>Range...</translation>
+ </message>
+ <message>
+ <source>Received with</source>
+ <translation>Received with</translation>
+ </message>
+ <message>
+ <source>Sent to</source>
+ <translation>Sent to</translation>
+ </message>
+ <message>
+ <source>To yourself</source>
+ <translation>To yourself</translation>
+ </message>
+ <message>
+ <source>Mined</source>
+ <translation>Mined</translation>
+ </message>
+ <message>
+ <source>Other</source>
+ <translation>Other</translation>
+ </message>
+ <message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>Enter address, transaction id, or label to search</translation>
+ </message>
+ <message>
+ <source>Min amount</source>
+ <translation>Min amount</translation>
+ </message>
+ <message>
+ <source>Abandon transaction</source>
+ <translation>Abandon transaction</translation>
+ </message>
+ <message>
+ <source>Increase transaction fee</source>
+ <translation>Increase transaction fee</translation>
+ </message>
+ <message>
+ <source>Copy address</source>
+ <translation>Copy address</translation>
+ </message>
+ <message>
+ <source>Copy label</source>
+ <translation>Copy label</translation>
+ </message>
+ <message>
+ <source>Copy amount</source>
+ <translation>Copy amount</translation>
+ </message>
+ <message>
+ <source>Copy transaction ID</source>
+ <translation>Copy transaction ID</translation>
+ </message>
+ <message>
+ <source>Copy raw transaction</source>
+ <translation>Copy raw transaction</translation>
+ </message>
+ <message>
+ <source>Copy full transaction details</source>
+ <translation>Copy full transaction details</translation>
+ </message>
+ <message>
+ <source>Edit label</source>
+ <translation>Edit label</translation>
+ </message>
+ <message>
+ <source>Show transaction details</source>
+ <translation>Show transaction details</translation>
+ </message>
+ <message>
+ <source>Export Transaction History</source>
+ <translation>Export Transaction History</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Comma separated file (*.csv)</translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>Confirmed</translation>
+ </message>
+ <message>
+ <source>Watch-only</source>
+ <translation>Watch-only</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>Date</translation>
+ </message>
+ <message>
+ <source>Type</source>
+ <translation>Type</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Label</translation>
+ </message>
+ <message>
<source>Address</source>
- <translation>ঠিকানা </translation>
+ <translation>Address</translation>
+ </message>
+ <message>
+ <source>ID</source>
+ <translation>ID</translation>
</message>
- </context>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Exporting Failed</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the transaction history to %1.</source>
+ <translation>There was an error trying to save the transaction history to %1.</translation>
+ </message>
+ <message>
+ <source>Exporting Successful</source>
+ <translation>Exporting Successful</translation>
+ </message>
+ <message>
+ <source>The transaction history was successfully saved to %1.</source>
+ <translation>The transaction history was successfully saved to %1.</translation>
+ </message>
+ <message>
+ <source>Range:</source>
+ <translation>Range:</translation>
+ </message>
+ <message>
+ <source>to</source>
+ <translation>to</translation>
+ </message>
+</context>
<context>
<name>UnitDisplayStatusBarControl</name>
- </context>
+ <message>
+ <source>Unit to show amounts in. Click to select another unit.</source>
+ <translation>Unit to show amounts in. Click to select another unit.</translation>
+ </message>
+</context>
<context>
<name>WalletController</name>
- </context>
+ <message>
+ <source>Close wallet</source>
+ <translation>Close wallet</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
- </context>
+ <message>
+ <source>No wallet has been loaded.</source>
+ <translation>No wallet has been loaded.</translation>
+ </message>
+</context>
<context>
<name>WalletModel</name>
- </context>
+ <message>
+ <source>Send Coins</source>
+ <translation>Send Coins</translation>
+ </message>
+ <message>
+ <source>Fee bump error</source>
+ <translation>Fee bump error</translation>
+ </message>
+ <message>
+ <source>Increasing transaction fee failed</source>
+ <translation>Increasing transaction fee failed</translation>
+ </message>
+ <message>
+ <source>Do you want to increase the fee?</source>
+ <translation>Do you want to increase the fee?</translation>
+ </message>
+ <message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Do you want to draft a transaction with fee increase?</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>Current fee:</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>Increase:</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>New fee:</translation>
+ </message>
+ <message>
+ <source>Confirm fee bump</source>
+ <translation>Confirm fee bump</translation>
+ </message>
+ <message>
+ <source>Can't draft transaction.</source>
+ <translation>Can't draft transaction.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copied</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>Can't sign transaction.</translation>
+ </message>
+ <message>
+ <source>Could not commit transaction</source>
+ <translation>Could not commit transaction</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>default wallet</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
- </context>
+ <message>
+ <source>&amp;Export</source>
+ <translation>&amp;Export</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Export the data in the current tab to a file</translation>
+ </message>
+ <message>
+ <source>Backup Wallet</source>
+ <translation>Backup Wallet</translation>
+ </message>
+ <message>
+ <source>Wallet Data (*.dat)</source>
+ <translation>Wallet Data (*.dat)</translation>
+ </message>
+ <message>
+ <source>Backup Failed</source>
+ <translation>Backup Failed</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the wallet data to %1.</source>
+ <translation>There was an error trying to save the wallet data to %1.</translation>
+ </message>
+ <message>
+ <source>Backup Successful</source>
+ <translation>Backup Successful</translation>
+ </message>
+ <message>
+ <source>The wallet data was successfully saved to %1.</source>
+ <translation>The wallet data was successfully saved to %1.</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Cancel</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <translation>Distributed under the MIT software license, see the accompanying file %s or %s</translation>
+ </message>
+ <message>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <translation>Prune configured below the minimum of %d MiB. Please use a higher number.</translation>
+ </message>
+ <message>
+ <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>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</translation>
+ </message>
+ <message>
+ <source>Error: A fatal internal error occurred, see debug.log for details</source>
+ <translation>Error: A fatal internal error occurred, see debug.log for details</translation>
+ </message>
+ <message>
+ <source>Pruning blockstore...</source>
+ <translation>Pruning blockstore...</translation>
+ </message>
+ <message>
+ <source>Unable to start HTTP server. See debug log for details.</source>
+ <translation>Unable to start HTTP server. See debug log for details.</translation>
+ </message>
+ <message>
+ <source>The %s developers</source>
+ <translation>The %s developers</translation>
+ </message>
+ <message>
+ <source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
+ <translation>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</translation>
+ </message>
+ <message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Cannot obtain a lock on data directory %s. %s is probably already running.</translation>
+ </message>
+ <message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Cannot provide specific connections and have addrman find outgoing connections at the same.</translation>
+ </message>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation>
+ </message>
+ <message>
+ <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <translation>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</translation>
+ </message>
+ <message>
+ <source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <translation>Please contribute if you find %s useful. Visit %s for further information about the software.</translation>
+ </message>
+ <message>
+ <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
+ <translation>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</translation>
+ </message>
+ <message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <translation>This is the transaction fee you may discard if change is smaller than dust at this level</translation>
+ </message>
+ <message>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <translation>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</translation>
+ </message>
+ <message>
+ <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
+ <translation>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</translation>
+ </message>
+ <message>
+ <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>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</translation>
+ </message>
+ <message>
+ <source>%d of last 100 blocks have unexpected version</source>
+ <translation>%d of last 100 blocks have unexpected version</translation>
+ </message>
+ <message>
+ <source>%s corrupt, salvage failed</source>
+ <translation>%s corrupt, salvage failed</translation>
+ </message>
+ <message>
+ <source>-maxmempool must be at least %d MB</source>
+ <translation>-maxmempool must be at least %d MB</translation>
+ </message>
+ <message>
+ <source>Cannot resolve -%s address: '%s'</source>
+ <translation>Cannot resolve -%s address: '%s'</translation>
+ </message>
+ <message>
+ <source>Change index out of range</source>
+ <translation>Change index out of range</translation>
+ </message>
+ <message>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation>Config setting for %s only applied on %s network when in [%s] section.</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>Copyright (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Corrupted block database detected</source>
+ <translation>Corrupted block database detected</translation>
+ </message>
+ <message>
+ <source>Could not find asmap file %s</source>
+ <translation>Could not find asmap file %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Could not parse asmap file %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
- <translation>আপনি কি পুনরায় ব্লক ডাটাবেইজ এখনই তৈরি করতে চান?</translation>
+ <translation>Do you want to rebuild the block database now?</translation>
+ </message>
+ <message>
+ <source>Error initializing block database</source>
+ <translation>Error initializing block database</translation>
+ </message>
+ <message>
+ <source>Error initializing wallet database environment %s!</source>
+ <translation>Error initializing wallet database environment %s!</translation>
+ </message>
+ <message>
+ <source>Error loading %s</source>
+ <translation>Error loading %s</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Error loading %s: Private keys can only be disabled during creation</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>Error loading %s: Wallet corrupted</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>Error loading %s: Wallet requires newer version of %s</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>Error loading block database</translation>
+ </message>
+ <message>
+ <source>Error opening block database</source>
+ <translation>Error opening block database</translation>
+ </message>
+ <message>
+ <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>
+ </message>
+ <message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Failed to rescan the wallet during initialization</translation>
+ </message>
+ <message>
+ <source>Importing...</source>
+ <translation>Importing...</translation>
+ </message>
+ <message>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
+ </message>
+ <message>
+ <source>Initialization sanity check failed. %s is shutting down.</source>
+ <translation>Initialization sanity check failed. %s is shutting down.</translation>
+ </message>
+ <message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Invalid P2P permission: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -%s=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</source>
+ <translation>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Specified blocks directory "%s" does not exist.</translation>
+ </message>
+ <message>
+ <source>Unknown address type '%s'</source>
+ <translation>Unknown address type '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Unknown change type '%s'</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Upgrading txindex database</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>Loading P2P addresses...</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Error: Disk space is too low!</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Loading banlist...</translation>
+ </message>
+ <message>
+ <source>Not enough file descriptors available.</source>
+ <translation>Not enough file descriptors available.</translation>
+ </message>
+ <message>
+ <source>Prune cannot be configured with a negative value.</source>
+ <translation>Prune cannot be configured with a negative value.</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -txindex.</source>
+ <translation>Prune mode is incompatible with -txindex.</translation>
+ </message>
+ <message>
+ <source>Replaying blocks...</source>
+ <translation>Replaying blocks...</translation>
+ </message>
+ <message>
+ <source>Rewinding blocks...</source>
+ <translation>Rewinding blocks...</translation>
+ </message>
+ <message>
+ <source>The source code is available from %s.</source>
+ <translation>The source code is available from %s.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>Transaction fee and change calculation failed</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <translation>Unable to bind to %s on this computer. %s is probably already running.</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Unable to generate keys</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Unsupported logging category %s=%s.</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>Upgrading UTXO database</translation>
+ </message>
+ <message>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation>User Agent comment (%s) contains unsafe characters.</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>Verifying blocks...</translation>
+ </message>
+ <message>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation>Wallet needed to be rewritten: restart %s to complete</translation>
+ </message>
+ <message>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation>Error: Listening for incoming connections failed (listen returned error %s)</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>The transaction amount is too small to send after the fee has been deducted</translation>
+ </message>
+ <message>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Error reading from database, shutting down.</translation>
+ </message>
+ <message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Error upgrading chainstate database</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is low for %s</source>
+ <translation>Error: Disk space is low for %s</translation>
+ </message>
+ <message>
+ <source>Invalid -onion address or hostname: '%s'</source>
+ <translation>Invalid -onion address or hostname: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid -proxy address or hostname: '%s'</source>
+ <translation>Invalid -proxy address or hostname: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</source>
+ <translation>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</translation>
+ </message>
+ <message>
+ <source>Invalid netmask specified in -whitelist: '%s'</source>
+ <translation>Invalid netmask specified in -whitelist: '%s'</translation>
+ </message>
+ <message>
+ <source>Need to specify a port with -whitebind: '%s'</source>
+ <translation>Need to specify a port with -whitebind: '%s'</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Prune mode is incompatible with -blockfilterindex.</translation>
+ </message>
+ <message>
+ <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <translation>Reducing -maxconnections from %d to %d, because of system limitations.</translation>
+ </message>
+ <message>
+ <source>Section [%s] is not recognized.</source>
+ <translation>Section [%s] is not recognized.</translation>
+ </message>
+ <message>
+ <source>Signing transaction failed</source>
+ <translation>Signing transaction failed</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Specified -walletdir "%s" does not exist</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Specified -walletdir "%s" is a relative path</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>Specified -walletdir "%s" is not a directory</translation>
+ </message>
+ <message>
+ <source>The specified config file %s does not exist
+</source>
+ <translation>The specified config file %s does not exist
+</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>The transaction amount is too small to pay the fee</translation>
</message>
<message>
<source>This is experimental software.</source>
- <translation>এটি পরীক্ষামূলক সফটওয়্যার।</translation>
+ <translation>This is experimental software.</translation>
</message>
<message>
<source>Transaction amount too small</source>
- <translation>লেনদেনের পরিমান অনেক ছোট</translation>
+ <translation>Transaction amount too small</translation>
</message>
<message>
<source>Transaction too large</source>
- <translation>লেনদেনর অংক অনেক বড়</translation>
+ <translation>Transaction too large</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer (bind returned error %s)</source>
+ <translation>Unable to bind to %s on this computer (bind returned error %s)</translation>
+ </message>
+ <message>
+ <source>Unable to create the PID file '%s': %s</source>
+ <translation>Unable to create the PID file '%s': %s</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>Unable to generate initial keys</translation>
+ </message>
+ <message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Unknown -blockfilterindex value %s.</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Verifying wallet(s)...</translation>
+ </message>
+ <message>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation>Warning: unknown new rules activated (versionbit %i)</translation>
+ </message>
+ <message>
+ <source>Zapping all transactions from wallet...</source>
+ <translation>Zapping all transactions from wallet...</translation>
+ </message>
+ <message>
+ <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <translation>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>This is the transaction fee you may pay when fee estimates are not available.</translation>
+ </message>
+ <message>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <translation>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</translation>
+ </message>
+ <message>
+ <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
+ <translation>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</translation>
+ </message>
+ <message>
+ <source>%s is set very high!</source>
+ <translation>%s is set very high!</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Error loading wallet %s. Duplicate -wallet filename specified.</translation>
+ </message>
+ <message>
+ <source>Starting network threads...</source>
+ <translation>Starting network threads...</translation>
+ </message>
+ <message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>The wallet will avoid paying less than the minimum relay fee.</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>This is the minimum transaction fee you pay on every transaction.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <translation>This is the transaction fee you will pay if you send a transaction.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>Transaction amounts must not be negative</translation>
+ </message>
+ <message>
+ <source>Transaction has too long of a mempool chain</source>
+ <translation>Transaction has too long of a mempool chain</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>Transaction must have at least one recipient</translation>
+ </message>
+ <message>
+ <source>Unknown network specified in -onlynet: '%s'</source>
+ <translation>Unknown network specified in -onlynet: '%s'</translation>
+ </message>
+ <message>
+ <source>Insufficient funds</source>
+ <translation>Insufficient funds</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Warning: Private keys detected in wallet {%s} with disabled private keys</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Cannot write to data directory '%s'; check permissions.</translation>
+ </message>
+ <message>
+ <source>Loading block index...</source>
+ <translation>Loading block index...</translation>
+ </message>
+ <message>
+ <source>Loading wallet...</source>
+ <translation>Loading wallet...</translation>
+ </message>
+ <message>
+ <source>Cannot downgrade wallet</source>
+ <translation>Cannot downgrade wallet</translation>
+ </message>
+ <message>
+ <source>Rescanning...</source>
+ <translation>Rescanning...</translation>
+ </message>
+ <message>
+ <source>Done loading</source>
+ <translation>Done loading</translation>
</message>
- </context>
+</context>
</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts
index e14fc3097e..c5a0d398ff 100644
--- a/src/qt/locale/bitcoin_ca.ts
+++ b/src/qt/locale/bitcoin_ca.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Feu clic dret per a editar l'adreça o l'etiqueta</translation>
+ <translation>Feu clic al botó dret per a editar l'adreça o l'etiqueta</translation>
</message>
<message>
<source>Create a new address</source>
@@ -70,6 +70,10 @@
<translation>Aquestes són les vostres adreces de Bitcoin per enviar els pagaments. Sempre reviseu l'import i l'adreça del destinatari abans de transferir monedes.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Aquestes són les teves adreces Bitcoin per rebre pagaments. Utilitza el botó "Crear nova adreça de recepció" de la pestanya de recepció per crear noves adreces.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Copia l'adreça</translation>
</message>
@@ -184,6 +188,10 @@
<translation>Introduïu la contrasenya antiga i la contrasenya nova a la cartera.</translation>
</message>
<message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Recorda que tot i xifrant la teva cartera, els teus bitcoins no estan completament protegits de robatori a través de programari maliciós que estigui infectant el teu ordinador.</translation>
+ </message>
+ <message>
<source>Wallet to be encrypted</source>
<translation>Cartera per ser encriptada</translation>
</message>
@@ -283,7 +291,7 @@
</message>
<message>
<source>Show information about %1</source>
- <translation>Mosta informació sobre el %1</translation>
+ <translation>Mostra informació sobre el %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
@@ -318,6 +326,14 @@
<translation>Obre un &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Crear Cartera...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Crear una nova cartera</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Moneder:</translation>
</message>
@@ -421,6 +437,10 @@
<source>&amp;Command-line options</source>
<translation>Opcions de la &amp;línia d'ordres</translation>
</message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>Una connexió activa a la xarxa de Bitcoin</numerusform><numerusform>%n connexions actives a la xarxa de Bitcoin</numerusform></translation>
+ </message>
<message>
<source>Indexing blocks on disk...</source>
<translation>S'estan indexant els blocs al disc...</translation>
@@ -429,6 +449,10 @@
<source>Processing blocks on disk...</source>
<translation>S'estan processant els blocs al disc...</translation>
</message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>Processat un bloc de l'historial de transaccions.</numerusform><numerusform>Processat %n blocs de l'historial de transaccions.</numerusform></translation>
+ </message>
<message>
<source>%1 behind</source>
<translation>%1 darrere</translation>
@@ -458,6 +482,14 @@
<translation>Actualitzat</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Finestra node</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Obrir depurador de node i consola de diagnosi.</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>Adreces d'&amp;enviament</translation>
</message>
@@ -466,12 +498,16 @@
<translation>Adreces de &amp;recepció</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Obrir un bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Obre la cartera</translation>
</message>
<message>
<source>Open a wallet</source>
- <translation>Obre la cartera</translation>
+ <translation>Obre una cartera</translation>
</message>
<message>
<source>Close Wallet...</source>
@@ -526,6 +562,10 @@
<translation>Avís: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>Avís: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Data: %1
@@ -747,10 +787,59 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Creant cartera &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>La creació de cartera ha fallat</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Avís en la creació de la cartera</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Crear cartera</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Nom de la cartera</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Xifra la cartera. La cartera serà xifrada amb la contrasenya que escullis.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Xifrar la cartera</translation>
+ </message>
+ <message>
+ <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>Deshabilita les claus privades per a aquesta cartera. Carteres amb claus privades deshabilitades no tindran cap clau privada i no podran tenir cap llavor HD o importar claus privades.
+Això és ideal per a carteres de mode només lectura.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Deshabilitar claus privades</translation>
+ </message>
+ <message>
+ <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>Crea una cartera en blanc. Carteres en blanc no tenen claus privades inicialment o scripts. Claus privades i adreces poden ser importades, o una llavor HD, més endavant.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Fes cartera en blanc</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Crear</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -856,13 +945,17 @@
</message>
<message>
<source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
- <translation>Com és la primera vegada que s'executa el programa, podeu triar on %1 emmagatzemarà les dades.</translation>
+ <translation>Com és la primera vegada que s'executa el programa, podeu triar on %1 emmagatzemaran les dades.</translation>
</message>
<message>
<source>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>
<translation>Quan feu clic a D'acord, %1 començarà a descarregar i processar la cadena de blocs %4 completa (%2 GB) començant per les primeres transaccions de %3, any de llençament inicial de %4.</translation>
</message>
<message>
+ <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>Desfer aquest canvi requereix tornar-se a descarregar el blockchain sencer. És més ràpid descarregar la cadena completa primer i després podar. Deshabilita algunes de les característiques avançades.</translation>
+ </message>
+ <message>
<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>Aquesta sincronització inicial és molt exigent i pot exposar problemes de maquinari amb l'equip que anteriorment havien passat desapercebuts. Cada vegada que executeu %1, continuarà descarregant des del punt on es va deixar.</translation>
</message>
@@ -883,6 +976,10 @@
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Descarta blocs després de la verificació, excepte el més recent %1 GB (podar)</translation>
+ </message>
+ <message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation>Almenys %1 GB de dades s'emmagatzemaran en aquest directori, i creixerà amb el temps.</translation>
</message>
@@ -906,7 +1003,19 @@
<source>Error</source>
<translation>Error</translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>Un GB d'espai lliure disponible.</numerusform><numerusform>%n GB d'espai lliure disponibles</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(Un GB necessari)</numerusform><numerusform>(de %n GB necessàris)</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(Un GB necessari per a la cadena completa)</numerusform><numerusform>(Un GB necessari per a la cadena completa)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -954,6 +1063,14 @@
<translation>Amaga</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
+ <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>%1 sincronitzant ara mateix. Es descarregaran capçaleres i blocs d'altres peers i es validaran fins a obtenir la punta de la cadena de blocs. </translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Desconegut. Sincronització de les capçaleres (%1, %2%)...</translation>
</message>
@@ -961,6 +1078,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Obre Bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -968,6 +1089,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Ha fallat l'obertura de la cartera</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Avís en l'obertura de la cartera</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>moneder per defecte</translation>
</message>
@@ -1195,6 +1324,10 @@
<translation>URL de transaccions de tercers</translation>
</message>
<message>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation>Opcions configurades en aquest diàleg són sobreescrites per la línia de comandes o el fitxer de configuració:</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>&amp;D'acord</translation>
</message>
@@ -1341,6 +1474,18 @@
<translation>'bitcoin://' no és una URI vàlida. Usi 'bitcoin:' en lloc seu.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>No es pot processar la petició de pagament perquè BIP70 no està suportat.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>A causa dels defectes generalitzats en el BIP70 és altament recomanable que qualsevol instrucció comerciant per canviar carteres sigui ignorada.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Si estàs rebent aquest error, hauries de demanar al comerciant que et doni una URI compatible amb el BIP21.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Adreça de pagament no vàlida %1</translation>
</message>
@@ -1418,10 +1563,34 @@
<source>%1 ms</source>
<translation>%1 ms</translation>
</message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>Un segon</numerusform><numerusform>%n segons</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>Un minut</numerusform><numerusform>%n minuts</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>Una hora</numerusform><numerusform>%n hores</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>Un dia</numerusform><numerusform>%n dies</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>Una setmana</numerusform><numerusform>%n setmanes</numerusform></translation>
+ </message>
<message>
<source>%1 and %2</source>
<translation>%1 i %2</translation>
</message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>Un any</numerusform><numerusform>%n anys</numerusform></translation>
+ </message>
<message>
<source>%1 B</source>
<translation>%1 B</translation>
@@ -1478,6 +1647,10 @@
<translation>Error en codificar l'URI en un codi QR.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>Suport de codi QR no disponible.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>Desa el codi QR</translation>
</message>
@@ -1513,10 +1686,18 @@
<translation>Datadir</translation>
</message>
<message>
+ <source>To specify a non-default location of the data directory use the '%1' option.</source>
+ <translation>Per tal d'especificar una ubicació que no és per defecte del directori de dades utilitza la '%1' opció.</translation>
+ </message>
+ <message>
<source>Blocksdir</source>
<translation>Directori de blocs</translation>
</message>
<message>
+ <source>To specify a non-default location of the blocks directory use the '%1' option.</source>
+ <translation>Per tal d'especificar una ubicació que no és per defecte del directori de blocs utilitza la '%1' opció.</translation>
+ </message>
+ <message>
<source>Startup time</source>
<translation>&amp;Temps d'inici</translation>
</message>
@@ -1609,10 +1790,22 @@
<translation>Blocs sincronitzats</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>El sistema autònom de mapat utilitzat per diversificar la selecció entre iguals.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapat com</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Agent d'usuari</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Finestra node</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Obre el fitxer de registre de depuració %1 del directori de dades actual. Això pot trigar uns segons en fitxers de registre grans.</translation>
</message>
@@ -1824,6 +2017,18 @@
<translation>Un import opcional per sol·licitar. Deixeu-ho en blanc o zero per no sol·licitar cap import específic.</translation>
</message>
<message>
+ <source>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>
+ <translation>Una etiqueta opcional per associar-se a la nova adreça de recepció (usada per vostè per identificar una factura). També s’adjunta a la sol·licitud de pagament.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Un missatge opcional adjunt a la sol·licitud de pagament i que es pot mostrar al remitent.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Creeu una nova adreça de recepció</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>Neteja tots els camps del formulari.</translation>
</message>
@@ -2073,6 +2278,18 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Polsim:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Amagueu la configuració de les tarifes de transacció</translation>
+ </message>
+ <message>
+ <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>Quan no hi ha prou espai en els blocs per encabir totes les transaccions, els miners i així mateix els nodes repetidors poden exigir una taxa mínima. És acceptable pagar únicament la taxa mínima, però tingueu present que pot resultar que la vostra transacció no sigui mai confirmada mentre hi hagi més demanda de transaccions bitcoin de les que la xarxa pot processar.</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>Una taxa massa baixa pot resultar en una transacció que no es confirmi mai (llegiu el consell)</translation>
+ </message>
+ <message>
<source>Confirmation time target:</source>
<translation>Temps de confirmació objectiu:</translation>
</message>
@@ -2133,18 +2350,38 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>%1 (%2 blocs)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Creació sense firmar</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Crea una transacció bitcoin parcialment signada (PSBT) per a utilitzar, per exemple, amb una cartera %1 fora de línia o amb una cartera compatible amb PSBT.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation>de la cartera "%1"</translation>
</message>
<message>
+ <source>%1 to '%2'</source>
+ <translation>%1 a '%2'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 a %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Voleu redactar aquesta transacció?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Esteu segur que ho voleu enviar?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Revisa la teva proposta de transacció. Es produirà una transacció de Bitcoin amb signatura parcial (PSBT) que podeu copiar i després signar, per exemple, amb una cartera %1 de tipus fora de línia o una cartera física compatible amb PSBT.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>o</translation>
</message>
@@ -2161,14 +2398,42 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Comissió de transacció</translation>
</message>
<message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Substitució per tarifa sense senyalització, BIP-125</translation>
+ </message>
+ <message>
<source>Total Amount</source>
<translation>Import total</translation>
</message>
<message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>Per revisar la llista de destinataris, feu clic a "Mostra els detalls ..."</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Confirma l'enviament de monedes</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Confirmeu la proposta de transacció</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Copiar PSBT al porta-retalls.</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Enviar</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copiada</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Saldo només de vigilància:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>L'adreça del destinatari no és vàlida. Torneu-la a comprovar.</translation>
</message>
@@ -2200,6 +2465,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<source>Payment request expired.</source>
<translation>La sol·licitud de pagament ha vençut.</translation>
</message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>S’estima que comenci la confirmació dintre d'un bloc.</numerusform><numerusform>S’estima que comenci la confirmació dintre de %n blocs.</numerusform></translation>
+ </message>
<message>
<source>Warning: Invalid Bitcoin address</source>
<translation>Avís: adreça Bitcoin no vàlida</translation>
@@ -2260,6 +2529,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Elimina aquesta entrada</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>L’import a enviar a la unitat seleccionada</translation>
+ </message>
+ <message>
<source>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>
<translation>La comissió es deduirà de l'import que s'enviarà. El destinatari rebrà menys bitcoins que les que introduïu al camp d'import. Si se seleccionen múltiples destinataris, la comissió es dividirà per igual.</translation>
</message>
@@ -2386,6 +2659,14 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>L'adreça Bitcoin amb què va ser signat el missatge</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>El missatge signat per verificar</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>La signatura donada quan es va signar el missatge</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verificar el missatge per assegurar-se que ha estat signat amb una adreça Bitcoin específica</translation>
</message>
@@ -2418,6 +2699,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>S'ha cancel·lat el desblocatge de la cartera.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Cap error</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>La clau privada per a la adreça introduïda no està disponible.</translation>
</message>
@@ -2459,6 +2744,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Obre per un bloc més</numerusform><numerusform>Obre per %n blocs més</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Obert fins %1</translation>
@@ -2535,6 +2824,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<source>Credit</source>
<translation>Crèdit</translation>
</message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>madura en un bloc més</numerusform><numerusform>madura en %n blocs més</numerusform></translation>
+ </message>
<message>
<source>not accepted</source>
<translation>no acceptat</translation>
@@ -2584,6 +2877,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Índex de resultats</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(El certificat no s'ha verificat)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Mercader</translation>
</message>
@@ -2641,6 +2938,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<source>Label</source>
<translation>Etiqueta</translation>
</message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Obre per un bloc més</numerusform><numerusform>Obre per %n blocs més</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Obert fins %1</translation>
@@ -2902,7 +3203,15 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<source>Close wallet</source>
<translation>Tanca la cartera</translation>
</message>
- </context>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Segur que voleu tancar la cartera &lt;i&gt;%1 &lt;/i&gt;?</translation>
+ </message>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Si tanqueu la cartera durant massa temps, es pot haver de tornar a sincronitzar tota la cadena si teniu el sistema de poda habilitat.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
<message>
@@ -2929,6 +3238,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Voleu augmentar la tarifa?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Voleu redactar una transacció amb augment de tarifes?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>tarifa actual:</translation>
</message>
@@ -2945,6 +3258,14 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Confirmeu el recàrrec de tarifes</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>No es pot redactar la transacció.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT copiada</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>No es pot signar la transacció.</translation>
</message>
@@ -3027,10 +3348,18 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Els desenvolupadors %s</translation>
</message>
<message>
+ <source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
+ <translation>No es pot generar una clau d’adreça de canvi. No hi ha claus al keypool intern i no es pot generar cap clau.</translation>
+ </message>
+ <message>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation>No es pot obtenir un bloqueig al directori de dades %s. %s probablement ja s'estigui executant.</translation>
</message>
<message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>No es poden proporcionar connexions específiques i no es poden trobar connexions sortint al mateix temps.</translation>
+ </message>
+ <message>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation>S'ha produït un error en llegir %s. Totes les claus es llegeixen correctament, però les dades de la transacció o les entrades de la llibreta d'adreces podrien faltar o ser incorrectes.</translation>
</message>
@@ -3091,6 +3420,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Canvieu l'índex fora de l'abast</translation>
</message>
<message>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation>Configuració per a %s únicament aplicada a %s de la xarxa quan es troba a la secció [%s].</translation>
+ </message>
+ <message>
<source>Copyright (C) %i-%i</source>
<translation>Copyright (C) %i-%i</translation>
</message>
@@ -3099,6 +3432,14 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>S'ha detectat una base de dades de blocs corrupta</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>No s'ha pogut trobar el fitxer asmap %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>No s'ha pogut analitzar el fitxer asmap %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Voleu reconstruir la base de dades de blocs ara?</translation>
</message>
@@ -3115,6 +3456,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Error carregant %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Error carregant %s: les claus privades només es poden desactivar durant la creació</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>S'ha produït un error en carregar %s: la cartera és corrupta</translation>
</message>
@@ -3135,6 +3480,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Ha fallat escoltar a qualsevol port. Feu servir -listen=0 si voleu fer això.</translation>
</message>
<message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>No s'ha pogut escanejar novament la cartera durant la inicialització</translation>
+ </message>
+ <message>
<source>Importing...</source>
<translation>S'està important...</translation>
</message>
@@ -3147,6 +3496,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>S'ha produït un error en la verificació de sanejament d'inicialització. S'està tancant %s.</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Permís P2P no vàlid: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>Import invàlid per -%s=&lt;amount&gt;: '%s'</translation>
</message>
@@ -3159,10 +3512,30 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Import invàlid per -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>El directori de blocs especificat "%s" no existeix.</translation>
+ </message>
+ <message>
+ <source>Unknown address type '%s'</source>
+ <translation>Tipus d'adreça desconegut '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Tipus de canvi desconegut '%s'</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Actualitzant txindex de la base de dades</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>S'estan carregant les adreces P2P ...</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Error: l'espai del disc és insuficient.</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>Carregant banlist ...</translation>
</message>
@@ -3247,6 +3620,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>S'ha produït un error en actualitzar la base de dades de chainstate</translation>
</message>
<message>
+ <source>Error: Disk space is low for %s</source>
+ <translation>Error: l'espai del disc és insuficient per a %s</translation>
+ </message>
+ <message>
<source>Invalid -onion address or hostname: '%s'</source>
<translation>Adreça o nom de l'ordinador -onion no vàlida: '%s'</translation>
</message>
@@ -3267,6 +3644,10 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Cal especificar un port amb -whitebind: «%s»</translation>
</message>
<message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>El mode de poda no és compatible amb -blockfilterindex.</translation>
+ </message>
+ <message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation>Reducció de -maxconnections de %d a %d, a causa de les limitacions del sistema.</translation>
</message>
@@ -3279,6 +3660,24 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Ha fallat la signatura de la transacció</translation>
</message>
<message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>-Walletdir especificat "%s" no existeix</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>-Walletdir especificat "%s" és una ruta relativa</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>-Walletdir especificat "%s" no és un directori</translation>
+ </message>
+ <message>
+ <source>The specified config file %s does not exist
+</source>
+ <translation>El fitxer de configuració especificat %s no existeix
+</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to pay the fee</source>
<translation>L'import de la transacció és massa petit per pagar-ne una comissió</translation>
</message>
@@ -3299,10 +3698,18 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>No s'ha pogut vincular a %s en aquest ordinador (la vinculació ha retornat l'error %s)</translation>
</message>
<message>
+ <source>Unable to create the PID file '%s': %s</source>
+ <translation>No es pot crear el fitxer PID '%s': %s</translation>
+ </message>
+ <message>
<source>Unable to generate initial keys</source>
<translation>No s'han pogut generar les claus inicials</translation>
</message>
<message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Valor %s -blockfilterindex desconegut</translation>
+ </message>
+ <message>
<source>Verifying wallet(s)...</source>
<translation>S'estan verificant les carteres...</translation>
</message>
@@ -3375,6 +3782,18 @@ Nota: Com que la comissió es calcula en funció dels bytes, una comissió de "1
<translation>Balanç insuficient</translation>
</message>
<message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>No es pot actualitzar una cartera de tipus dividida que no sigui física sense haver d'actualitzar per tal de suportar keypool pre dividida. Si us plau, utilitzeu -upgradewallet = 169900 o -upgradewallet sense cap versió especificada.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>L'estimació de la quota ha fallat. Fallbackfee està desactivat. Espereu uns quants blocs o activeu -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Avís: Claus privades detectades en la cartera {%s} amb claus privades deshabilitades</translation>
+ </message>
+ <message>
<source>Cannot write to data directory '%s'; check permissions.</source>
<translation>No es pot escriure en el directori de dades "%s". Reviseu-ne els permisos.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts
index 70334087e8..7d6ba6772b 100644
--- a/src/qt/locale/bitcoin_cs.ts
+++ b/src/qt/locale/bitcoin_cs.ts
@@ -482,6 +482,14 @@
<translation>Aktuální</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Okno uzlu</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Otevřít konzolu pro ladění a diagnostiku uzlů</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>Odesílací adresy</translation>
</message>
@@ -490,6 +498,10 @@
<translation>Přijímací adresy</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Načíst Bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Otevřít peněženku</translation>
</message>
@@ -550,6 +562,10 @@
<translation>Chyba: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>Varování: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Datum: %1
@@ -771,10 +787,58 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Vytvářím peněženku &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Vytvoření peněženky selhalo</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Vytvořit varování peněženky</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Vytvořit peněženku</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Název peněženky</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Zašifrovat peněženku. Peněženka bude zašifrována pomocí vašeho hesla.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Zašifrovat peněženku</translation>
+ </message>
+ <message>
+ <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>Vypnout soukromé klíče pro tuto peněženku. Peněženky s vypnutými soukromými klíči nebudou mít soukromé klíče a nemohou mít HD inicializaci ani importované soukromé klíče. Tohle je ideální pro peněženky pouze na sledování.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Zrušit soukromé klíče</translation>
+ </message>
+ <message>
+ <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>Vytvořit prázdnou peněženku. Prázdné peněženky na začátku nemají žádné soukromé klíče ani skripty. Později mohou být importovány soukromé klíče a adresy nebo nastavená HD inicializace.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Vytvořit prázdnou peněženku</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Vytvořit</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -887,6 +951,10 @@
<translation>Jakmile stiskneš OK, %1 začne stahovat a zpracovávat celý %4ový blockchain (%2 GB), počínaje nejstaršími transakcemi z roku %3, kdy byl %4 spuštěn.</translation>
</message>
<message>
+ <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>Vrácení tohoto nastavení vyžaduje opětovné stažení celého blockchainu. Je rychlejší stáhnout celý řetězec nejprve a prořezat jej později. Některé pokročilé funkce budou zakázány, dokud celý blockchain nebude stažen nanovo.</translation>
+ </message>
+ <message>
<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>Prvotní synchronizace je velice náročná, a mohou se tak díky ní začít na tvém počítači projevovat dosud skryté hardwarové problémy. Pokaždé, když spustíš %1, bude stahování pokračovat tam, kde skončilo.</translation>
</message>
@@ -907,6 +975,10 @@
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Zahodit bloky po ověření, s výjimkou posledních %1 GB (prořezat)</translation>
+ </message>
+ <message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation>Bude proto potřebovat do tohoto adresáře uložit nejméně %1 GB dat – tohle číslo navíc bude v průběhu času růst.</translation>
</message>
@@ -938,7 +1010,11 @@
<source>(of %n GB needed)</source>
<translation><numerusform>(z potřebného %n GB)</numerusform><numerusform>(z potřebných %n GB)</numerusform><numerusform>(z potřebných %n GB)</numerusform><numerusform>(z potřebných %n GB)</numerusform></translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n GB potřeba pre plný řetězec)</numerusform><numerusform>(%n GB potřeba pre plný řetězec) </numerusform><numerusform>(%n GB potřeba pre plný řetězec) </numerusform><numerusform>(%n GB potřeba pre plný řetězec) </numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -986,6 +1062,14 @@
<translation>Skryj</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc - úniková klávesa</translation>
+ </message>
+ <message>
+ <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>%1 se právě synchronizuje. Stáhnou se hlavičky a bloky od protějsků. Ty se budou se ověřovat až se kompletně ověří celý řetězec bloků.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Neznámé. Synchronizace hlaviček (%1, %2)...</translation>
</message>
@@ -993,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Otevřít bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1000,6 +1088,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Otevření peněženky selhalo</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Varování otevření peněženky</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>výchozí peněženka</translation>
</message>
@@ -1377,6 +1473,18 @@
<translation>'bitcoin://' není platné URI. Místo toho použij 'bitcoin:'.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Nelze zpracovat žádost o platbu, protože podpora pro BIP70 není podporována.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Vzhledem k rozšířeným bezpečnostním nedostatkům v BIP70 se důrazně doporučuje, aby byly ignorovány veškeré obchodní pokyny pro přepínání peněženek.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Pokud obdržíte tuto chybu, měli byste požádat obchodníka, aby poskytl URI kompatibilní s BIP21.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Neplatná platební adresa %1</translation>
</message>
@@ -1538,6 +1646,10 @@
<translation>Chyba při kódování URI do QR kódu.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>Podpora QR kódu není k dispozici.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>Ulož QR kód</translation>
</message>
@@ -1677,10 +1789,22 @@
<translation>Aktuálně bloků</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Mapovaný nezávislý - Autonomní Systém používaný pro rozšírení vzájemného výběru protějsků.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapovaný AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Typ klienta</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Okno uzlu</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Otevři soubor s ladicími záznamy %1 z aktuálního datového adresáře. U velkých žurnálů to může pár vteřin zabrat.</translation>
</message>
@@ -1892,6 +2016,18 @@
<translation>Volitelná částka, kterou požaduješ. Nech prázdné nebo nulové, pokud nepožaduješ konkrétní částku.</translation>
</message>
<message>
+ <source>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>
+ <translation>Volitelný popis který sa přidá k téjo nové přijímací adrese (pro jednoduchší identifikaci). Tenhle popis bude také přidán do výzvy k platbě.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Volitelná zpráva která se přidá k téjo platební výzvě a může být zobrazena odesílateli.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Vytvořit novou přijímací adresu</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>Promaž obsah ze všech formulářových políček.</translation>
</message>
@@ -1905,7 +2041,7 @@
</message>
<message>
<source>Generate native segwit (Bech32) address</source>
- <translation>Generovat nativní segwit adresu (Bench32)</translation>
+ <translation>Generovat nativní segwit adresu (Bech32)</translation>
</message>
<message>
<source>Requested payments history</source>
@@ -2141,6 +2277,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Prach:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Schovat nastavení poplatků transakce - transaction fee</translation>
+ </message>
+ <message>
<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>Když je zde měně transakcí než místa na bloky, mineři stejně tak relay-e mohou nasadit minimální poplatky. Zaplacením pouze minimálního poplatku je v pohodě, ale mějte na paměti že toto může mít za následek nikdy neověřenou transakci pokud zde bude více bitcoinových transakcí než může síť zvládnout.</translation>
</message>
@@ -2209,14 +2349,38 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>%1 (%2 bloků)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Vytvořit bez podpisu</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Vytvořit částečně podepsanou Bitcoin transakci (Partially Signed Bitcoin Transaction - PSBT) k použtí kupříkladu s offline %1 peněženkou nebo s jinou kompatibilní PSBT hardware peněženkou.</translation>
+ </message>
+ <message>
+ <source> from wallet '%1'</source>
+ <translation>z peněženky '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1 do '%2'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
- <translation>%1 pro %2</translation>
+ <translation>%1 do %2</translation>
+ </message>
+ <message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Chcete naplánovat tuhle transakci?</translation>
</message>
<message>
<source>Are you sure you want to send?</source>
<translation>Jsi si jistý, že tuhle transakci chceš poslat?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Zkontrolujte prosím svůj návrh transakce. Výsledkem bude částečně podepsaná bitcoinová transakce (PSBT), kterou můžete kopírovat a poté podepsat např. pomocí offline %1 peněženky nebo hardwarové peněženky kompatibilní s PSBT.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>nebo</translation>
</message>
@@ -2241,12 +2405,32 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Celková částka</translation>
</message>
<message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>Chcete-li zkontrolovat seznam příjemců, klikněte na „Zobrazit podrobnosti ...“</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Potvrď odeslání mincí</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Potvrdit návrh transakce</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Zkopírovat PSBT do schránky</translation>
+ </message>
+ <message>
<source>Send</source>
- <translation>Pošli</translation>
+ <translation>Odeslat</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT zkopírován</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Pouze sledovaný zůstatek:</translation>
</message>
<message>
<source>The recipient address is not valid. Please recheck.</source>
@@ -2344,6 +2528,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Smaž tento záznam</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Částka k odeslání ve vybrané měně</translation>
+ </message>
+ <message>
<source>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>
<translation>Poplatek se odečte od posílané částky. Příjemce tak dostane méně bitcoinů, než zadáš do pole Částka. Pokud vybereš více příjemců, tak se poplatek rovnoměrně rozloží.</translation>
</message>
@@ -2470,6 +2658,14 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Bitcoinová adresa, kterou je zpráva podepsána</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Podepsaná zpráva na ověření</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Podpis daný při podpisu zprávy</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Ověř zprávu, aby ses ujistil, že byla podepsána danou bitcoinovou adresou</translation>
</message>
@@ -2502,6 +2698,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Odemčení peněženky bylo zrušeno.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Bez chyby</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Soukromý klíč pro zadanou adresu není dostupný.</translation>
</message>
@@ -2676,6 +2876,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Pořadí výstupu</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Certifikát nebyl ověřen)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Obchodník</translation>
</message>
@@ -2999,6 +3203,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Zavřít peněženku</translation>
</message>
<message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Opravdu chcete zavřít peněženku &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<translation>Zavření peněženky na příliš dlouhou dobu může vyústit v potřebu resynchronizace celého blockchainu pokud je zapnuté prořezávání.</translation>
</message>
@@ -3026,7 +3234,11 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
</message>
<message>
<source>Do you want to increase the fee?</source>
- <translation>Chceš poplatek navýšit?</translation>
+ <translation>Chcete navýšit poplatek?</translation>
+ </message>
+ <message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Chcete naplánovat tuhle transakci s navýšením poplatku?</translation>
</message>
<message>
<source>Current fee:</source>
@@ -3045,6 +3257,14 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Potvrď navýšení poplatku</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Nelze navrhnout transakci.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT zkopírováno</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Nemůžu podepsat transakci.</translation>
</message>
@@ -3211,6 +3431,14 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Bylo zjištěno poškození databáze bloků</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Soubor asmap nelze najít %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Soubor asmap nelze analyzovat %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Chceš přestavět databázi bloků hned teď?</translation>
</message>
@@ -3267,6 +3495,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Selhala úvodní zevrubná prověrka. %s se ukončuje.</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Neplatné oprávnenie P2P: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>Neplatná částka pro -%s=&lt;částka&gt;: '%s'</translation>
</message>
@@ -3283,6 +3515,14 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Zadaný adresář bloků "%s" neexistuje.</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Neznámý typ adresy '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Neznámý typ změny '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Aktualizuje se txindex databáze</translation>
</message>
@@ -3291,6 +3531,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Načítám P2P adresy…</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Chyba: Místo na disku je příliš malé!</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>Načítám seznam klateb...</translation>
</message>
@@ -3399,6 +3643,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>V rámci -whitebind je třeba specifikovat i port: '%s'</translation>
</message>
<message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Režim prořezávání není kompatibilní s -blockfilterindex.</translation>
+ </message>
+ <message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation>Omezuji -maxconnections z %d na %d kvůli systémovým omezením.</translation>
</message>
@@ -3457,6 +3705,10 @@ Poznámka: Jelikož je poplatek počítaný za bajt, poplatek o hodnotě "100 sa
<translation>Nepodařilo se mi vygenerovat počáteční klíče</translation>
</message>
<message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Neznámá -blockfilterindex hodnota %s.</translation>
+ </message>
+ <message>
<source>Verifying wallet(s)...</source>
<translation>Kontroluji peněženku/y…</translation>
</message>
diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts
index 0fb4d104aa..a087ebe522 100644
--- a/src/qt/locale/bitcoin_da.ts
+++ b/src/qt/locale/bitcoin_da.ts
@@ -1789,6 +1789,14 @@
<translation>Synkroniserede blokke</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Afbildning fra Autonome Systemer (et Internet-Protocol-rutefindingsprefiks) til IP-adresser som bruges til at diversificere knudeforbindelser. Den engelske betegnelse er "asmap".</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Autonomt-System-afbildning</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Brugeragent</translation>
</message>
diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts
index ff763b002c..ff272af443 100644
--- a/src/qt/locale/bitcoin_de.ts
+++ b/src/qt/locale/bitcoin_de.ts
@@ -658,7 +658,7 @@
</message>
<message>
<source>Dust:</source>
- <translation>"Dust":</translation>
+ <translation>"Staub":</translation>
</message>
<message>
<source>After Fee:</source>
@@ -808,7 +808,7 @@
</message>
<message>
<source>Wallet Name</source>
- <translation>Wallet Name</translation>
+ <translation>Wallet-Name</translation>
</message>
<message>
<source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
@@ -1012,7 +1012,7 @@
</message>
<message numerus="yes">
<source>(%n GB needed for full chain)</source>
- <translation><numerusform>(%n GB benötigt für komplette Blockchain)</numerusform><numerusform>(%n GB benötigt für komplette Blockchain)</numerusform></translation>
+ <translation><numerusform>(%n GB benötigt für komplette Blockchain)</numerusform><numerusform>(%n GB wird die komplette Blockchain benötigen)</numerusform></translation>
</message>
</context>
<context>
@@ -1067,7 +1067,7 @@
</message>
<message>
<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>%1 synchronisiert gerade. Es lädt Header und Blöcke von anderen Nodes und validiert sie bis zum Erreichen der Spitze der Blockkette.</translation>
+ <translation>%1 synchronisiert gerade. Es lädt Header und Blöcke von Gegenstellen und validiert sie bis zum Erreichen der Spitze der Blockkette.</translation>
</message>
<message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
@@ -1097,7 +1097,7 @@
</message>
<message>
<source>default wallet</source>
- <translation>Standard Wallet</translation>
+ <translation>Standard-Wallet</translation>
</message>
<message>
<source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
@@ -1124,7 +1124,7 @@
</message>
<message>
<source>Size of &amp;database cache</source>
- <translation>Größe des &amp;Datenbankcaches</translation>
+ <translation>Größe des &amp;Datenbankpufferspeichers</translation>
</message>
<message>
<source>Number of script &amp;verification threads</source>
@@ -1758,7 +1758,7 @@
</message>
<message>
<source>Banned peers</source>
- <translation>Gesperrte Peers</translation>
+ <translation>Gesperrte Gegenstellen</translation>
</message>
<message>
<source>Select a peer to view detailed information.</source>
@@ -1789,6 +1789,14 @@
<translation>Synchronisierte Blöcke</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Das zugeordnete autonome System zur Diversifizierung der Gegenstellen-Auswahl.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Zugeordnetes AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User-Agent</translation>
</message>
@@ -2266,7 +2274,7 @@ Hinweis: Eine Gebühr von "100 Satoshis pro kB" bei einer Größe der Transaktio
</message>
<message>
<source>Dust:</source>
- <translation>"Dust":</translation>
+ <translation>"Staub":</translation>
</message>
<message>
<source>Hide transaction fee settings</source>
diff --git a/src/qt/locale/bitcoin_el.ts b/src/qt/locale/bitcoin_el.ts
index d903e2f85d..d4787e6218 100644
--- a/src/qt/locale/bitcoin_el.ts
+++ b/src/qt/locale/bitcoin_el.ts
@@ -1774,6 +1774,14 @@
<translation>Συγχρονισμένα Μπλοκς</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Το χαρτογραφημένο Αυτόνομο Σύστημα που χρησιμοποιείται για τη διαφοροποίηση της επιλογής ομοτίμων.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Χαρτογραφημένο ως</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Agent χρήστη</translation>
</message>
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 6b4174f313..71467a915b 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -90,7 +90,8 @@
</message>
<message>
<location line="+5"/>
- <source>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.</source>
+ <source>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>
<translation type="unfinished"></translation>
</message>
<message>
@@ -109,7 +110,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+177"/>
+ <location line="+179"/>
<source>Export Address List</source>
<translation type="unfinished"></translation>
</message>
@@ -132,7 +133,7 @@
<context>
<name>AddressTableModel</name>
<message>
- <location filename="../addresstablemodel.cpp" line="+165"/>
+ <location filename="../addresstablemodel.cpp" line="+168"/>
<source>Label</source>
<translation type="unfinished"></translation>
</message>
@@ -175,7 +176,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../askpassphrasedialog.cpp" line="+50"/>
+ <location filename="../askpassphrasedialog.cpp" line="+51"/>
<source>Encrypt wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -205,7 +206,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+46"/>
+ <location line="+48"/>
<source>Confirm wallet encryption</source>
<translation type="unfinished"></translation>
</message>
@@ -226,7 +227,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-145"/>
+ <location line="-147"/>
<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>
@@ -236,7 +237,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+53"/>
+ <location line="+55"/>
<source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<translation type="unfinished"></translation>
</message>
@@ -325,17 +326,17 @@
<context>
<name>BitcoinGUI</name>
<message>
- <location filename="../bitcoingui.cpp" line="+316"/>
+ <location filename="../bitcoingui.cpp" line="+322"/>
<source>Sign &amp;message...</source>
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+630"/>
+ <location line="+668"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-708"/>
+ <location line="-746"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -410,7 +411,7 @@
<translation>&amp;Change Passphrase...</translation>
</message>
<message>
- <location line="+18"/>
+ <location line="+22"/>
<source>Open &amp;URI...</source>
<translation type="unfinished"></translation>
</message>
@@ -425,12 +426,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+191"/>
+ <location line="+210"/>
<source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+339"/>
+ <location line="+350"/>
<source>Click to disable network activity.</source>
<translation type="unfinished"></translation>
</message>
@@ -450,17 +451,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+53"/>
+ <location line="+57"/>
<source>Reindexing blocks on disk...</source>
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="+317"/>
+ <location line="+315"/>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1028"/>
+ <location line="-1064"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
@@ -515,17 +516,17 @@
<translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation>
</message>
<message>
- <location line="+111"/>
+ <location line="+129"/>
<source>&amp;File</source>
<translation>&amp;File</translation>
</message>
<message>
- <location line="+15"/>
+ <location line="+18"/>
<source>&amp;Settings</source>
<translation>&amp;Settings</translation>
</message>
<message>
- <location line="+59"/>
+ <location line="+61"/>
<source>&amp;Help</source>
<translation>&amp;Help</translation>
</message>
@@ -535,12 +536,12 @@
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="-258"/>
+ <location line="-281"/>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+71"/>
+ <location line="+75"/>
<source>Show the list of used sending addresses and labels</source>
<translation type="unfinished"></translation>
</message>
@@ -550,12 +551,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+20"/>
<source>&amp;Command-line options</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
- <location line="+528"/>
+ <location line="+555"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -563,7 +564,7 @@
</translation>
</message>
<message>
- <location line="+76"/>
+ <location line="+80"/>
<source>Indexing blocks on disk...</source>
<translation type="unfinished"></translation>
</message>
@@ -596,7 +597,7 @@
<translation>Transactions after this will not yet be visible.</translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+25"/>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -611,12 +612,32 @@
<translation>Information</translation>
</message>
<message>
- <location line="-81"/>
+ <location line="-78"/>
<source>Up to date</source>
<translation>Up to date</translation>
</message>
<message>
- <location line="-655"/>
+ <location line="-694"/>
+ <source>&amp;Load PSBT from file...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <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"/>
+ <source>Load Partially Signed Bitcoin Transaction from clipboard</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Node window</source>
<translation type="unfinished"></translation>
</message>
@@ -661,12 +682,32 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+6"/>
+ <source>Close All Wallets...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Close all wallets</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+29"/>
+ <location line="+2"/>
+ <source>&amp;Mask values</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Mask the values in the Overview tab</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+32"/>
<source>default wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -676,7 +717,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+55"/>
+ <location line="+64"/>
<source>&amp;Window</source>
<translation type="unfinished">&amp;Window</translation>
</message>
@@ -696,12 +737,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+238"/>
+ <location line="+245"/>
<source>%1 client</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+241"/>
+ <location line="+249"/>
<source>Connecting to peers...</source>
<translation type="unfinished"></translation>
</message>
@@ -711,7 +752,7 @@
<translation>Catching up...</translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+47"/>
<source>Error: %1</source>
<translation type="unfinished"></translation>
</message>
@@ -721,7 +762,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+99"/>
+ <location line="+100"/>
<source>Date: %1
</source>
<translation type="unfinished"></translation>
@@ -792,8 +833,13 @@
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+384"/>
- <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <location line="+129"/>
+ <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>
@@ -885,7 +931,7 @@
<translation type="unfinished">Confirmed</translation>
</message>
<message>
- <location filename="../coincontroldialog.cpp" line="+53"/>
+ <location filename="../coincontroldialog.cpp" line="+54"/>
<source>Copy address</source>
<translation type="unfinished"></translation>
</message>
@@ -946,12 +992,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+313"/>
+ <location line="+302"/>
<source>(%1 locked)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+157"/>
+ <location line="+155"/>
<source>yes</source>
<translation type="unfinished"></translation>
</message>
@@ -971,7 +1017,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+45"/>
+ <location line="+38"/>
<location line="+54"/>
<source>(no label)</source>
<translation type="unfinished"></translation>
@@ -990,12 +1036,12 @@
<context>
<name>CreateWalletActivity</name>
<message>
- <location filename="../walletcontroller.cpp" line="+209"/>
+ <location filename="../walletcontroller.cpp" line="+241"/>
<source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+28"/>
<source>Create wallet failed</source>
<translation type="unfinished"></translation>
</message>
@@ -1048,6 +1094,16 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+13"/>
+ <source>Use descriptors for scriptPubKey management</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Descriptor Wallet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location filename="../createwalletdialog.cpp" line="+19"/>
<source>Create</source>
<translation type="unfinished"></translation>
@@ -1096,7 +1152,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+75"/>
+ <location line="+77"/>
<source>The entered address &quot;%1&quot; is not a valid Bitcoin address.</source>
<translation type="unfinished"></translation>
</message>
@@ -1124,7 +1180,7 @@
<context>
<name>FreespaceChecker</name>
<message>
- <location filename="../intro.cpp" line="+71"/>
+ <location filename="../intro.cpp" line="+72"/>
<source>A new data directory will be created.</source>
<translation>A new data directory will be created.</translation>
</message>
@@ -1152,7 +1208,7 @@
<context>
<name>HelpMessageDialog</name>
<message>
- <location filename="../utilitydialog.cpp" line="+35"/>
+ <location filename="../utilitydialog.cpp" line="+37"/>
<source>version</source>
<translation type="unfinished">version</translation>
</message>
@@ -1304,7 +1360,7 @@
<message>
<location line="+7"/>
<location line="+26"/>
- <location filename="../modaloverlay.cpp" line="+145"/>
+ <location filename="../modaloverlay.cpp" line="+153"/>
<source>Unknown...</source>
<translation type="unfinished"></translation>
</message>
@@ -1345,12 +1401,12 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../modaloverlay.cpp" line="-111"/>
+ <location filename="../modaloverlay.cpp" line="-119"/>
<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="+117"/>
+ <location line="+125"/>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation type="unfinished"></translation>
</message>
@@ -1437,12 +1493,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+38"/>
- <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+108"/>
+ <location line="+146"/>
<source>Hide the icon from the system tray.</source>
<translation type="unfinished"></translation>
</message>
@@ -1611,12 +1662,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+25"/>
- <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+105"/>
+ <location line="+130"/>
<source>&amp;Window</source>
<translation>&amp;Window</translation>
</message>
@@ -1666,7 +1712,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+464"/>
+ <location line="+250"/>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor onion services:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+211"/>
<source>&amp;Third party transaction URLs</source>
<translation type="unfinished"></translation>
</message>
@@ -1691,7 +1747,7 @@
<translation>default</translation>
</message>
<message>
- <location line="+65"/>
+ <location line="+67"/>
<source>none</source>
<translation type="unfinished"></translation>
</message>
@@ -1751,12 +1807,12 @@
</message>
<message>
<location line="+62"/>
- <location line="+386"/>
+ <location line="+394"/>
<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="-139"/>
+ <location line="-141"/>
<source>Watch-only:</source>
<translation type="unfinished"></translation>
</message>
@@ -1766,22 +1822,22 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+16"/>
+ <location line="+17"/>
<source>Your current spendable balance</source>
<translation>Your current spendable balance</translation>
</message>
<message>
- <location line="+41"/>
+ <location line="+42"/>
<source>Pending:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-236"/>
+ <location line="-242"/>
<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="+112"/>
+ <location line="+114"/>
<source>Immature:</source>
<translation>Immature:</translation>
</message>
@@ -1791,22 +1847,22 @@
<translation>Mined balance that has not yet matured</translation>
</message>
<message>
- <location line="-177"/>
+ <location line="-181"/>
<source>Balances</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+161"/>
+ <location line="+164"/>
<source>Total:</source>
<translation>Total:</translation>
</message>
<message>
- <location line="+61"/>
+ <location line="+63"/>
<source>Your current total balance</source>
<translation>Your current total balance</translation>
</message>
<message>
- <location line="+92"/>
+ <location line="+95"/>
<source>Your current balance in watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
@@ -1821,20 +1877,178 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-317"/>
+ <location line="-324"/>
<source>Unconfirmed transactions to watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+50"/>
+ <location line="+52"/>
<source>Mined balance in watch-only addresses that has not yet matured</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+128"/>
+ <location line="+131"/>
<source>Current total balance in watch-only addresses</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../overviewpage.cpp" line="+166"/>
+ <source>Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings-&gt;Mask values.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PSBTOperationsDialog</name>
+ <message>
+ <location filename="../forms/psbtoperationsdialog.ui" line="+14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+72"/>
+ <source>Sign Tx</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Broadcast Tx</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Copy to Clipboard</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Save...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../psbtoperationsdialog.cpp" line="+55"/>
+ <source>Failed to load transaction: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+18"/>
+ <source>Failed to sign transaction: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Could not sign any more inputs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Signed %1 inputs, but more signatures are still required.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Signed transaction successfully. Transaction is ready to broadcast.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Unknown error processing transaction.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Transaction broadcast successfully! Transaction ID: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Transaction broadcast failed: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>PSBT copied to clipboard.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>Save Transaction Data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Partially Signed Transaction (Binary) (*.psbt)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>PSBT saved to disk.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source> * Sends %1 to %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Unable to calculate transaction fee or total transaction amount.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Pays transaction fee: </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Total Amount</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>or</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Transaction has %1 unsigned inputs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+42"/>
+ <source>Transaction is missing some information about inputs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Transaction still needs signature(s).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>(But this wallet cannot sign transactions.)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>(But this wallet does not have the right keys.)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Transaction is fully signed and ready for broadcast.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>Transaction status is unknown.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>PaymentServer</name>
@@ -1931,17 +2145,17 @@
<context>
<name>QObject</name>
<message>
- <location filename="../bitcoinunits.cpp" line="+195"/>
+ <location filename="../bitcoinunits.cpp" line="+209"/>
<source>Amount</source>
<translation type="unfinished">Amount</translation>
</message>
<message>
- <location filename="../guiutil.cpp" line="+111"/>
+ <location filename="../guiutil.cpp" line="+108"/>
<source>Enter a Bitcoin address (e.g. %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+618"/>
+ <location line="+652"/>
<source>%1 d</source>
<translation type="unfinished"></translation>
</message>
@@ -1957,7 +2171,7 @@
</message>
<message>
<location line="+2"/>
- <location line="+48"/>
+ <location line="+26"/>
<source>%1 s</source>
<translation type="unfinished"></translation>
</message>
@@ -2051,7 +2265,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+114"/>
+ <location filename="../bitcoin.cpp" line="+102"/>
<source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
@@ -2066,6 +2280,11 @@
<translation type="unfinished"></translation>
</message>
<message>
+ <location line="+9"/>
+ <source>Error initializing settings: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+65"/>
<source>%1 didn&apos;t yet exit safely...</source>
<translation type="unfinished"></translation>
@@ -2129,8 +2348,7 @@
<location line="+23"/>
<location line="+36"/>
<location line="+23"/>
- <location line="+716"/>
- <location line="+23"/>
+ <location line="+710"/>
<location line="+23"/>
<location line="+23"/>
<location line="+23"/>
@@ -2148,12 +2366,13 @@
<location line="+23"/>
<location line="+23"/>
<location line="+26"/>
- <location filename="../rpcconsole.cpp" line="+1121"/>
+ <location filename="../rpcconsole.cpp" line="+1127"/>
+ <location line="+8"/>
<source>N/A</source>
<translation>N/A</translation>
</message>
<message>
- <location line="-1456"/>
+ <location line="-1427"/>
<source>Client version</source>
<translation>Client version</translation>
</message>
@@ -2218,12 +2437,7 @@
<translation>Block chain</translation>
</message>
<message>
- <location line="+7"/>
- <source>Current number of blocks</source>
- <translation>Current number of blocks</translation>
- </message>
- <message>
- <location line="+52"/>
+ <location line="+59"/>
<source>Memory Pool</source>
<translation type="unfinished"></translation>
</message>
@@ -2254,18 +2468,18 @@
</message>
<message>
<location line="+80"/>
- <location line="+589"/>
+ <location line="+560"/>
<source>Received</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-509"/>
- <location line="+486"/>
+ <location line="-480"/>
+ <location line="+457"/>
<source>Sent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-445"/>
+ <location line="-416"/>
<source>&amp;Peers</source>
<translation type="unfinished"></translation>
</message>
@@ -2276,18 +2490,13 @@
</message>
<message>
<location line="+65"/>
- <location filename="../rpcconsole.cpp" line="-624"/>
- <location line="+756"/>
+ <location filename="../rpcconsole.cpp" line="-637"/>
+ <location line="+766"/>
<source>Select a peer to view detailed information.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+37"/>
- <source>Whitelisted</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+23"/>
+ <location line="+54"/>
<source>Direction</source>
<translation type="unfinished"></translation>
</message>
@@ -2312,7 +2521,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+256"/>
+ <location line="+233"/>
<source>The mapped Autonomous System used for diversifying peer selection.</source>
<translation type="unfinished"></translation>
</message>
@@ -2322,18 +2531,23 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1423"/>
- <location line="+1072"/>
+ <location line="-1394"/>
+ <location line="+1066"/>
<source>User Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1146"/>
+ <location line="-1140"/>
<source>Node window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+409"/>
+ <location line="+279"/>
+ <source>Current block height</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+130"/>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation type="unfinished"></translation>
</message>
@@ -2348,17 +2562,17 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+644"/>
- <source>Services</source>
+ <location line="+546"/>
+ <source>Permissions</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+92"/>
- <source>Ban Score</source>
+ <source>Services</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
+ <location line="+92"/>
<source>Connection Time</source>
<translation type="unfinished"></translation>
</message>
@@ -2398,7 +2612,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-1169"/>
+ <location line="-1140"/>
<source>Last block time</source>
<translation>Last block time</translation>
</message>
@@ -2423,7 +2637,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-408"/>
+ <location filename="../rpcconsole.cpp" line="-416"/>
<source>In:</source>
<translation type="unfinished"></translation>
</message>
@@ -2521,7 +2735,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+189"/>
+ <location line="+192"/>
<source>(node id: %1)</source>
<translation type="unfinished"></translation>
</message>
@@ -2547,17 +2761,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Yes</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+0"/>
- <source>No</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+13"/>
+ <location line="+20"/>
<location line="+6"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
@@ -2597,12 +2801,12 @@
</message>
<message>
<location line="-39"/>
- <location line="+153"/>
+ <location line="+159"/>
<source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-121"/>
+ <location line="-127"/>
<source>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>
<translation type="unfinished"></translation>
</message>
@@ -2617,7 +2821,7 @@
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+17"/>
+ <location line="+23"/>
<source>Clear all fields of the form.</source>
<translation type="unfinished"></translation>
</message>
@@ -2681,68 +2885,73 @@
<source>Copy amount</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+131"/>
+ <source>Could not unlock wallet.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Could not generate new %1 address</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
- <location filename="../forms/receiverequestdialog.ui" line="+29"/>
- <source>QR Code</source>
+ <location filename="../forms/receiverequestdialog.ui" line="+14"/>
+ <source>Request payment to ...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+46"/>
- <source>Copy &amp;URI</source>
+ <location line="+76"/>
+ <source>Address:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
- <source>Copy &amp;Address</source>
+ <location line="+29"/>
+ <source>Amount:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+10"/>
- <source>&amp;Save Image...</source>
+ <location line="+29"/>
+ <source>Label:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../receiverequestdialog.cpp" line="+64"/>
- <source>Request payment to %1</source>
+ <location line="+32"/>
+ <source>Message:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>Payment information</source>
+ <location line="+32"/>
+ <source>Wallet:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+1"/>
- <source>URI</source>
+ <location line="+28"/>
+ <source>Copy &amp;URI</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Address</source>
+ <location line="+10"/>
+ <source>Copy &amp;Address</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Amount</source>
- <translation type="unfinished">Amount</translation>
- </message>
- <message>
- <location line="+2"/>
- <source>Label</source>
+ <location line="+10"/>
+ <source>&amp;Save Image...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Message</source>
+ <location filename="../receiverequestdialog.cpp" line="+49"/>
+ <source>Request payment to %1</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
- <source>Wallet</source>
- <translation type="unfinished">Wallet</translation>
+ <location filename="../forms/receiverequestdialog.ui" line="-221"/>
+ <source>Payment information</source>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -2787,7 +2996,7 @@
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+622"/>
+ <location filename="../sendcoinsdialog.cpp" line="+662"/>
<source>Send Coins</source>
<translation>Send Coins</translation>
</message>
@@ -2974,7 +3183,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="-533"/>
+ <location filename="../sendcoinsdialog.cpp" line="-572"/>
<source>Copy quantity</source>
<translation type="unfinished"></translation>
</message>
@@ -3024,7 +3233,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="+100"/>
+ <location line="+90"/>
<source> from wallet &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -3039,7 +3248,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="+8"/>
+ <location line="+7"/>
<source>Do you want to draft this transaction?</source>
<translation type="unfinished"></translation>
</message>
@@ -3049,12 +3258,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
- <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <location line="+71"/>
+ <source>Create Unsigned</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+43"/>
+ <location line="+44"/>
+ <source>Save Transaction Data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Partially Signed Transaction (Binary) (*.psbt)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>PSBT saved</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="-75"/>
<source>or</source>
<translation type="unfinished"></translation>
</message>
@@ -3064,7 +3288,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="-22"/>
+ <location line="-24"/>
+ <source>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>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Please, review your transaction.</source>
<translation type="unfinished"></translation>
</message>
@@ -3084,12 +3313,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="+9"/>
+ <location line="+7"/>
<source>To review recipient list click &quot;Show Details...&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+18"/>
<source>Confirm send coins</source>
<translation type="unfinished"></translation>
</message>
@@ -3100,21 +3329,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</message>
<message>
<location line="+1"/>
- <source>Copy PSBT to clipboard</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+0"/>
<source>Send</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+23"/>
- <source>PSBT copied</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+166"/>
+ <location line="+228"/>
<source>Watch-only balance:</source>
<translation type="unfinished"></translation>
</message>
@@ -3159,7 +3378,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="+125"/>
+ <location line="+118"/>
<source>Estimated to begin confirmation within %n block(s).</source>
<translation>
<numerusform>Estimated to begin confirmation within %n block.</numerusform>
@@ -3167,7 +3386,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
</translation>
</message>
<message>
- <location line="+101"/>
+ <location line="+100"/>
<source>Warning: Invalid Bitcoin address</source>
<translation type="unfinished"></translation>
</message>
@@ -3305,7 +3524,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>ShutdownWindow</name>
<message>
- <location filename="../utilitydialog.cpp" line="+83"/>
+ <location filename="../utilitydialog.cpp" line="+85"/>
<source>%1 is shutting down...</source>
<translation type="unfinished"></translation>
</message>
@@ -3444,7 +3663,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 filename="../signverifymessagedialog.cpp" line="+117"/>
+ <location filename="../signverifymessagedialog.cpp" line="+120"/>
<location line="+99"/>
<source>The entered address is invalid.</source>
<translation type="unfinished"></translation>
@@ -3518,7 +3737,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>TrafficGraphWidget</name>
<message>
- <location filename="../trafficgraphwidget.cpp" line="+81"/>
+ <location filename="../trafficgraphwidget.cpp" line="+82"/>
<source>KB/s</source>
<translation type="unfinished"></translation>
</message>
@@ -3764,7 +3983,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>This pane shows a detailed description of the transaction</translation>
</message>
<message>
- <location filename="../transactiondescdialog.cpp" line="+17"/>
+ <location filename="../transactiondescdialog.cpp" line="+18"/>
<source>Details for %1</source>
<translation type="unfinished"></translation>
</message>
@@ -3772,7 +3991,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="+225"/>
+ <location filename="../transactiontablemodel.cpp" line="+221"/>
<source>Date</source>
<translation type="unfinished">Date</translation>
</message>
@@ -4094,7 +4313,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="+171"/>
+ <location line="+172"/>
<source>Range:</source>
<translation type="unfinished"></translation>
</message>
@@ -4107,7 +4326,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="+156"/>
+ <location filename="../bitcoingui.cpp" line="+40"/>
<source>Unit to show amounts in. Click to select another unit.</source>
<translation type="unfinished"></translation>
</message>
@@ -4115,7 +4334,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="-211"/>
+ <location filename="../walletcontroller.cpp" line="-238"/>
<source>Close wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4129,24 +4348,41 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location line="+13"/>
+ <source>Close all wallets</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Are you sure you wish to close all wallets?</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
<message>
- <location filename="../walletframe.cpp" line="+28"/>
- <source>No wallet has been loaded.</source>
+ <location filename="../walletframe.cpp" line="+37"/>
+ <source>No wallet has been loaded.
+Go to File &gt; Open Wallet to load a wallet.
+- OR -</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Create a new wallet</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WalletModel</name>
<message>
- <location filename="../walletmodel.cpp" line="+195"/>
+ <location filename="../walletmodel.cpp" line="+214"/>
<source>Send Coins</source>
<translation type="unfinished">Send Coins</translation>
</message>
<message>
- <location line="+288"/>
+ <location line="+282"/>
<location line="+45"/>
<location line="+13"/>
<location line="+5"/>
@@ -4217,7 +4453,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>WalletView</name>
<message>
- <location filename="../walletview.cpp" line="+46"/>
+ <location filename="../walletview.cpp" line="+51"/>
<source>&amp;Export</source>
<translation type="unfinished">&amp;Export</translation>
</message>
@@ -4227,7 +4463,39 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished">Export the data in the current tab to a file</translation>
</message>
<message>
- <location line="+182"/>
+ <location line="+165"/>
+ <location line="+9"/>
+ <location line="+10"/>
+ <source>Error</source>
+ <translation type="unfinished">Error</translation>
+ </message>
+ <message>
+ <location line="-19"/>
+ <source>Unable to decode PSBT from clipboard (invalid base64)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Load Transaction Data</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Partially Signed Transaction (*.psbt)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>PSBT file must be smaller than 100 MiB</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Unable to decode PSBT</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+39"/>
<source>Backup Wallet</source>
<translation type="unfinished"></translation>
</message>
@@ -4265,7 +4533,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+28"/>
+ <location filename="../bitcoinstrings.cpp" line="+27"/>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
<translation type="unfinished"></translation>
</message>
@@ -4280,12 +4548,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="+68"/>
- <source>Error: A fatal internal error occurred, see debug.log for details</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+26"/>
+ <location line="+102"/>
<source>Pruning blockstore...</source>
<translation type="unfinished"></translation>
</message>
@@ -4295,17 +4558,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="-162"/>
+ <location line="-169"/>
<source>The %s developers</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
- <source>Can&apos;t generate a change-address key. No keys in the internal keypool and can&apos;t generate any keys.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+3"/>
+ <location line="+7"/>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation type="unfinished"></translation>
</message>
@@ -4315,7 +4573,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="+10"/>
+ <location line="+9"/>
<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>
@@ -4335,17 +4593,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="+7"/>
+ <location line="+11"/>
<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="+3"/>
+ <location line="+6"/>
<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="+8"/>
+ <location line="+11"/>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
<translation type="unfinished"></translation>
</message>
@@ -4360,32 +4618,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+7"/>
+ <location line="+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>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+6"/>
- <source>%d of last 100 blocks have unexpected version</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+1"/>
- <source>%s corrupt, salvage failed</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="+7"/>
<source>-maxmempool must be at least %d MB</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+3"/>
<source>Cannot resolve -%s address: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+2"/>
+ <location line="+3"/>
<source>Change index out of range</source>
<translation type="unfinished"></translation>
</message>
@@ -4415,7 +4663,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"/>
+ <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>
</message>
@@ -4460,7 +4708,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Error opening block database</translation>
</message>
<message>
- <location line="+6"/>
+ <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>
</message>
@@ -4470,7 +4718,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"/>
+ <location line="+2"/>
<source>Importing...</source>
<translation type="unfinished"></translation>
</message>
@@ -4505,7 +4753,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="+22"/>
+ <location line="+23"/>
<source>Specified blocks directory &quot;%s&quot; does not exist.</source>
<translation type="unfinished"></translation>
</message>
@@ -4525,22 +4773,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="-46"/>
+ <location line="-47"/>
<source>Loading P2P addresses...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="-15"/>
- <source>Error: Disk space is too low!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+16"/>
+ <location line="+1"/>
<source>Loading banlist...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>Not enough file descriptors available.</source>
<translation>Not enough file descriptors available.</translation>
</message>
@@ -4610,12 +4853,22 @@ 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="-151"/>
+ <location line="-159"/>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+5"/>
+ <location line="-20"/>
+ <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="+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>
+ <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>
<translation type="unfinished"></translation>
</message>
@@ -4625,12 +4878,42 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+31"/>
+ <location line="+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="+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>
+ <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>
+ <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>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+27"/>
+ <location line="+5"/>
+ <source>A fatal internal error occurred, see debug.log for details</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Cannot set -peerblockfilters without -blockfilterindex.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Disk space is too low!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+12"/>
<source>Error reading from database, shutting down.</source>
<translation type="unfinished"></translation>
</message>
@@ -4640,12 +4923,22 @@ 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="+1"/>
<source>Error: Disk space is low for %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+1"/>
+ <source>Error: Keypool ran out, please call keypoolrefill first</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Fee rate (%s) is lower than the minimum fee rate setting (%s)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location line="+5"/>
<source>Invalid -onion address or hostname: &apos;%s&apos;</source>
<translation type="unfinished"></translation>
</message>
@@ -4670,6 +4963,11 @@ 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>No proxy server specified. Use -proxy=&lt;ip&gt; or -proxy=&lt;ip:port&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<location line="+3"/>
<source>Prune mode is incompatible with -blockfilterindex.</source>
<translation type="unfinished"></translation>
@@ -4761,17 +5059,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="+1"/>
- <source>Zapping all transactions from wallet...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="-174"/>
+ <location line="-177"/>
<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="+53"/>
+ <location line="+56"/>
<source>This is the transaction fee you may pay when fee estimates are not available.</source>
<translation type="unfinished"></translation>
</message>
@@ -4781,22 +5074,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="+14"/>
- <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+12"/>
+ <location line="+23"/>
<source>%s is set very high!</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+20"/>
+ <location line="+23"/>
<source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+41"/>
+ <location line="+42"/>
<source>Starting network threads...</source>
<translation type="unfinished"></translation>
</message>
@@ -4836,32 +5124,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
<message>
- <location line="-52"/>
+ <location line="-53"/>
<source>Insufficient funds</source>
<translation>Insufficient funds</translation>
</message>
<message>
- <location line="-102"/>
- <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location line="+12"/>
+ <location line="-97"/>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+41"/>
+ <location line="+51"/>
<source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+21"/>
+ <location line="+17"/>
<source>Cannot write to data directory &apos;%s&apos;; check permissions.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location line="+39"/>
+ <location line="+40"/>
<source>Loading block index...</source>
<translation>Loading block index...</translation>
</message>
@@ -4871,17 +5154,17 @@ Note: Since the fee is calculated on a per-byte basis, a fee of &quot;100 satos
<translation>Loading wallet...</translation>
</message>
<message>
- <location line="-42"/>
+ <location line="-44"/>
<source>Cannot downgrade wallet</source>
<translation>Cannot downgrade wallet</translation>
</message>
<message>
- <location line="+51"/>
+ <location line="+54"/>
<source>Rescanning...</source>
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-41"/>
+ <location line="-42"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
diff --git a/src/qt/locale/bitcoin_en_GB.ts b/src/qt/locale/bitcoin_en_GB.ts
index ab674f0c7d..4d943fd532 100644
--- a/src/qt/locale/bitcoin_en_GB.ts
+++ b/src/qt/locale/bitcoin_en_GB.ts
@@ -189,7 +189,7 @@
</message>
<message>
<source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
- <translation>Remember that encrypting your wallet cannot fully protect your Bitcoins from being stolen by malware infecting your computer.</translation>
+ <translation>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</translation>
</message>
<message>
<source>Wallet to be encrypted</source>
@@ -255,11 +255,11 @@
<name>BitcoinGUI</name>
<message>
<source>Sign &amp;message...</source>
- <translation>Sign &amp;message ...</translation>
+ <translation>Sign &amp;message...</translation>
</message>
<message>
<source>Synchronizing with network...</source>
- <translation>Synchronising with network ...</translation>
+ <translation>Synchronizing with network...</translation>
</message>
<message>
<source>&amp;Overview</source>
@@ -303,7 +303,7 @@
</message>
<message>
<source>&amp;Options...</source>
- <translation>&amp;Options ...</translation>
+ <translation>&amp;Options...</translation>
</message>
<message>
<source>Modify configuration options for %1</source>
@@ -311,19 +311,19 @@
</message>
<message>
<source>&amp;Encrypt Wallet...</source>
- <translation>&amp;Encrypt Wallet ...</translation>
+ <translation>&amp;Encrypt Wallet...</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
- <translation>&amp;Backup Wallet ...</translation>
+ <translation>&amp;Backup Wallet...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
- <translation>&amp;Change Passphrase ...</translation>
+ <translation>&amp;Change Passphrase...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
- <translation>Open &amp;URI ...</translation>
+ <translation>Open &amp;URI...</translation>
</message>
<message>
<source>Create Wallet...</source>
@@ -351,11 +351,11 @@
</message>
<message>
<source>Syncing Headers (%1%)...</source>
- <translation>Syncing Headers (%1%) ...</translation>
+ <translation>Syncing Headers (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>Reindexing blocks on disk ...</translation>
+ <translation>Reindexing blocks on disk...</translation>
</message>
<message>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
@@ -375,7 +375,7 @@
</message>
<message>
<source>&amp;Verify message...</source>
- <translation>&amp;Verify message ...</translation>
+ <translation>&amp;Verify message...</translation>
</message>
<message>
<source>&amp;Send</source>
@@ -443,11 +443,11 @@
</message>
<message>
<source>Indexing blocks on disk...</source>
- <translation>Indexing blocks on disk ...</translation>
+ <translation>Indexing blocks on disk...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
- <translation>Processing blocks on disk ...</translation>
+ <translation>Processing blocks on disk...</translation>
</message>
<message numerus="yes">
<source>Processed %n block(s) of transaction history.</source>
@@ -499,7 +499,7 @@
</message>
<message>
<source>Open a bitcoin: URI</source>
- <translation>Open a Bitcoin: URI</translation>
+ <translation>Open a bitcoin: URI</translation>
</message>
<message>
<source>Open Wallet</source>
@@ -511,7 +511,7 @@
</message>
<message>
<source>Close Wallet...</source>
- <translation>Close Wallet ...</translation>
+ <translation>Close Wallet...</translation>
</message>
<message>
<source>Close wallet</source>
@@ -535,7 +535,7 @@
</message>
<message>
<source>Minimize</source>
- <translation>Minimise</translation>
+ <translation>Minimize</translation>
</message>
<message>
<source>Zoom</source>
@@ -551,11 +551,11 @@
</message>
<message>
<source>Connecting to peers...</source>
- <translation>Connecting to peers ...</translation>
+ <translation>Connecting to peers...</translation>
</message>
<message>
<source>Catching up...</source>
- <translation>Catching up ...</translation>
+ <translation>Catching up...</translation>
</message>
<message>
<source>Error: %1</source>
@@ -952,7 +952,7 @@
</message>
<message>
<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>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.</translation>
+ <translation>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.</translation>
</message>
<message>
<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>
@@ -960,7 +960,7 @@
</message>
<message>
<source>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>
- <translation>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterwards to keep your disk usage low.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Use the default data directory</source>
@@ -1023,11 +1023,11 @@
</message>
<message>
<source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
- <translation>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronising with the Bitcoin network, as detailed below.</translation>
+ <translation>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</translation>
</message>
<message>
<source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
- <translation>Attempting to spend Bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</translation>
+ <translation>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</translation>
</message>
<message>
<source>Number of blocks left</source>
@@ -1035,7 +1035,7 @@
</message>
<message>
<source>Unknown...</source>
- <translation>Unknown ...</translation>
+ <translation>Unknown...</translation>
</message>
<message>
<source>Last block time</source>
@@ -1051,7 +1051,7 @@
</message>
<message>
<source>calculating...</source>
- <translation>calculating ...</translation>
+ <translation>calculating...</translation>
</message>
<message>
<source>Estimated time left until synced</source>
@@ -1071,14 +1071,14 @@
</message>
<message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
- <translation>Unknown. Syncing Headers (%1, %2%) ...</translation>
+ <translation>Unknown. Syncing Headers (%1, %2%)...</translation>
</message>
</context>
<context>
<name>OpenURIDialog</name>
<message>
<source>Open bitcoin URI</source>
- <translation>Open Bitcoin URI</translation>
+ <translation>Open bitcoin URI</translation>
</message>
<message>
<source>URI:</source>
@@ -1101,7 +1101,7 @@
</message>
<message>
<source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
- <translation>Opening Wallet &lt;b&gt;%1&lt;/b&gt; ...</translation>
+ <translation>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
</message>
</context>
<context>
@@ -1152,7 +1152,7 @@
</message>
<message>
<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>Minimise 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.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>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>
@@ -1284,15 +1284,15 @@
</message>
<message>
<source>Show only a tray icon after minimizing the window.</source>
- <translation>Show on a tray icon after minimising the window.</translation>
+ <translation>Show only a tray icon after minimizing the window.</translation>
</message>
<message>
<source>&amp;Minimize to the tray instead of the taskbar</source>
- <translation>&amp;Minimise to the tray instead of the task bar</translation>
+ <translation>&amp;Minimize to the tray instead of the taskbar</translation>
</message>
<message>
<source>M&amp;inimize on close</source>
- <translation>M&amp;inimise on close</translation>
+ <translation>M&amp;inimize on close</translation>
</message>
<message>
<source>&amp;Display</source>
@@ -1387,7 +1387,7 @@
</message>
<message>
<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 synchronises with the Bitcoin Network after a connection is established, but this process has not been completed yet.</translation>
+ <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>
<source>Watch-only:</source>
@@ -1462,7 +1462,7 @@
</message>
<message>
<source>Cannot start bitcoin: click-to-pay handler</source>
- <translation>Cannot start Bitcoin: click-to-pay handler</translation>
+ <translation>Cannot start bitcoin: click-to-pay handler</translation>
</message>
<message>
<source>URI handling</source>
@@ -1620,7 +1620,7 @@
</message>
<message>
<source>%1 didn't yet exit safely...</source>
- <translation>%1 didn't exit safely yet ...</translation>
+ <translation>%1 didn't yet exit safely...</translation>
</message>
<message>
<source>unknown</source>
@@ -1631,7 +1631,7 @@
<name>QRImageWidget</name>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Save Image ...</translation>
+ <translation>&amp;Save Image...</translation>
</message>
<message>
<source>&amp;Copy Image</source>
@@ -1698,7 +1698,7 @@
</message>
<message>
<source>Startup time</source>
- <translation>Start up time</translation>
+ <translation>Startup time</translation>
</message>
<message>
<source>Network</source>
@@ -1734,7 +1734,7 @@
</message>
<message>
<source>Wallet: </source>
- <translation>Wallet:</translation>
+ <translation>Wallet: </translation>
</message>
<message>
<source>(none)</source>
@@ -1789,6 +1789,14 @@
<translation>Synced Blocks</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>The mapped Autonomous System used for diversifying peer selection.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapped AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User Agent</translation>
</message>
@@ -1930,7 +1938,7 @@
</message>
<message>
<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>
- <translation>WARNING: Scammers have been active, telling users to type commands here and stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Network activity disabled</source>
@@ -2088,7 +2096,7 @@
</message>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Save Image ...</translation>
+ <translation>&amp;Save Image...</translation>
</message>
<message>
<source>Request payment to %1</source>
@@ -2166,7 +2174,7 @@
</message>
<message>
<source>Inputs...</source>
- <translation>Inputs ...</translation>
+ <translation>Inputs...</translation>
</message>
<message>
<source>automatically selected</source>
@@ -2214,11 +2222,11 @@
</message>
<message>
<source>Choose...</source>
- <translation>Choose ...</translation>
+ <translation>Choose...</translation>
</message>
<message>
<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>Using the fallback fee 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.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Warning: Fee estimation is currently not possible.</source>
@@ -2250,7 +2258,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
- <translation>(Smart fee not initialised yet. This usually takes a few blocks ...)</translation>
+ <translation>(Smart fee not initialized yet. This usually takes a few blocks...)</translation>
</message>
<message>
<source>Send to multiple recipients at once</source>
@@ -2278,7 +2286,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
- <translation>A too low fee might result in a never-confirming transaction (read the tooltip)</translation>
+ <translation>A too low fee might result in a never confirming transaction (read the tooltip)</translation>
</message>
<message>
<source>Confirmation time target:</source>
@@ -2568,7 +2576,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<name>ShutdownWindow</name>
<message>
<source>%1 is shutting down...</source>
- <translation>%1 is shutting down ...</translation>
+ <translation>%1 is shutting down...</translation>
</message>
<message>
<source>Do not shut down the computer until this window disappears.</source>
@@ -3050,7 +3058,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Range...</source>
- <translation>Range ...</translation>
+ <translation>Range...</translation>
</message>
<message>
<source>Received with</source>
@@ -3074,7 +3082,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Enter address, transaction id, or label to search</source>
- <translation>Enter address, transaction id or label to search.</translation>
+ <translation>Enter address, transaction id, or label to search</translation>
</message>
<message>
<source>Min amount</source>
@@ -3222,7 +3230,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Increasing transaction fee failed</source>
- <translation>Increasing transaction fee failed.</translation>
+ <translation>Increasing transaction fee failed</translation>
</message>
<message>
<source>Do you want to increase the fee?</source>
@@ -3262,7 +3270,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Could not commit transaction</source>
- <translation>Could not commit transaction.</translation>
+ <translation>Could not commit transaction</translation>
</message>
<message>
<source>default wallet</source>
@@ -3312,7 +3320,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<name>bitcoin-core</name>
<message>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
- <translation>Distributed under the MIT software license, see the accompanying file %s or %s.</translation>
+ <translation>Distributed under the MIT software license, see the accompanying file %s or %s</translation>
</message>
<message>
<source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
@@ -3328,7 +3336,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Pruning blockstore...</source>
- <translation>Pruning blockstore ...</translation>
+ <translation>Pruning blockstore...</translation>
</message>
<message>
<source>Unable to start HTTP server. See debug log for details.</source>
@@ -3372,7 +3380,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
- <translation>This is the transaction fee you may discard if change is smaller than dust at this level.</translation>
+ <translation>This is the transaction fee you may discard if change is smaller than dust at this level</translation>
</message>
<message>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
@@ -3380,7 +3388,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
- <translation>Unable to rewind the database to a pre-fork state. You will need to re-download the blockchain</translation>
+ <translation>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</translation>
</message>
<message>
<source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
@@ -3392,7 +3400,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>%d of last 100 blocks have unexpected version</source>
- <translation>%d of last 100 blocks have unexpected version.</translation>
+ <translation>%d of last 100 blocks have unexpected version</translation>
</message>
<message>
<source>%s corrupt, salvage failed</source>
@@ -3436,11 +3444,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Error initializing block database</source>
- <translation>Error initialising block database</translation>
+ <translation>Error initializing block database</translation>
</message>
<message>
<source>Error initializing wallet database environment %s!</source>
- <translation>Error initialising wallet database environment %s!</translation>
+ <translation>Error initializing wallet database environment %s!</translation>
</message>
<message>
<source>Error loading %s</source>
@@ -3472,11 +3480,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Failed to rescan the wallet during initialization</source>
- <translation>Failed to rescan the wallet during initialisation.</translation>
+ <translation>Failed to rescan the wallet during initialization</translation>
</message>
<message>
<source>Importing...</source>
- <translation>Importing ...</translation>
+ <translation>Importing...</translation>
</message>
<message>
<source>Incorrect or no genesis block found. Wrong datadir for network?</source>
@@ -3484,7 +3492,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Initialization sanity check failed. %s is shutting down.</source>
- <translation>Initialisation sanity check failed. %s is shutting down.</translation>
+ <translation>Initialization sanity check failed. %s is shutting down.</translation>
</message>
<message>
<source>Invalid P2P permission: '%s'</source>
@@ -3520,7 +3528,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Loading P2P addresses...</source>
- <translation>Loading P2P addresses ...</translation>
+ <translation>Loading P2P addresses...</translation>
</message>
<message>
<source>Error: Disk space is too low!</source>
@@ -3528,7 +3536,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Loading banlist...</source>
- <translation>Loading banlist ...</translation>
+ <translation>Loading banlist...</translation>
</message>
<message>
<source>Not enough file descriptors available.</source>
@@ -3544,11 +3552,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Replaying blocks...</source>
- <translation>Replaying blocks ...</translation>
+ <translation>Replaying blocks...</translation>
</message>
<message>
<source>Rewinding blocks...</source>
- <translation>Rewinding blocks ...</translation>
+ <translation>Rewinding blocks...</translation>
</message>
<message>
<source>The source code is available from %s.</source>
@@ -3556,7 +3564,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Transaction fee and change calculation failed</source>
- <translation>Transaction fee and change calculation failed.</translation>
+ <translation>Transaction fee and change calculation failed</translation>
</message>
<message>
<source>Unable to bind to %s on this computer. %s is probably already running.</source>
@@ -3580,7 +3588,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Verifying blocks...</source>
- <translation>Verifying blocks ...</translation>
+ <translation>Verifying blocks...</translation>
</message>
<message>
<source>Wallet needed to be rewritten: restart %s to complete</source>
@@ -3608,7 +3616,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Error upgrading chainstate database</source>
- <translation>Error upgrading chainstate database.</translation>
+ <translation>Error upgrading chainstate database</translation>
</message>
<message>
<source>Error: Disk space is low for %s</source>
@@ -3652,15 +3660,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Specified -walletdir "%s" does not exist</source>
- <translation>Specified -walletdir "%s" does not exist.</translation>
+ <translation>Specified -walletdir "%s" does not exist</translation>
</message>
<message>
<source>Specified -walletdir "%s" is a relative path</source>
- <translation>Specified -walletdir "%s" is a relative path.</translation>
+ <translation>Specified -walletdir "%s" is a relative path</translation>
</message>
<message>
<source>Specified -walletdir "%s" is not a directory</source>
- <translation>Specified -walletdir "%s" is not a directory.</translation>
+ <translation>Specified -walletdir "%s" is not a directory</translation>
</message>
<message>
<source>The specified config file %s does not exist
@@ -3694,7 +3702,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unable to generate initial keys</source>
- <translation>Unable to generate initial keys.</translation>
+ <translation>Unable to generate initial keys</translation>
</message>
<message>
<source>Unknown -blockfilterindex value %s.</source>
@@ -3702,7 +3710,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Verifying wallet(s)...</source>
- <translation>Verifying wallet(s) ...</translation>
+ <translation>Verifying wallet(s)...</translation>
</message>
<message>
<source>Warning: unknown new rules activated (versionbit %i)</source>
@@ -3710,7 +3718,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Zapping all transactions from wallet...</source>
- <translation>Zapping all transactions from wallet ...</translation>
+ <translation>Zapping all transactions from wallet...</translation>
</message>
<message>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
@@ -3738,7 +3746,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Starting network threads...</source>
- <translation>Starting network threads ...</translation>
+ <translation>Starting network threads...</translation>
</message>
<message>
<source>The wallet will avoid paying less than the minimum relay fee.</source>
@@ -3754,15 +3762,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Transaction amounts must not be negative</source>
- <translation>Transaction amounts must not be negative.</translation>
+ <translation>Transaction amounts must not be negative</translation>
</message>
<message>
<source>Transaction has too long of a mempool chain</source>
- <translation>Transaction has too long of a mempool chain.</translation>
+ <translation>Transaction has too long of a mempool chain</translation>
</message>
<message>
<source>Transaction must have at least one recipient</source>
- <translation>Transaction must have at least one recipient.</translation>
+ <translation>Transaction must have at least one recipient</translation>
</message>
<message>
<source>Unknown network specified in -onlynet: '%s'</source>
@@ -3790,11 +3798,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Loading block index...</source>
- <translation>Loading block index ...</translation>
+ <translation>Loading block index...</translation>
</message>
<message>
<source>Loading wallet...</source>
- <translation>Loading wallet ...</translation>
+ <translation>Loading wallet...</translation>
</message>
<message>
<source>Cannot downgrade wallet</source>
@@ -3802,7 +3810,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Rescanning...</source>
- <translation>Rescanning ...</translation>
+ <translation>Rescanning...</translation>
</message>
<message>
<source>Done loading</source>
diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts
index b681cff011..e4c28c4704 100644
--- a/src/qt/locale/bitcoin_es.ts
+++ b/src/qt/locale/bitcoin_es.ts
@@ -395,7 +395,7 @@
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
- <translation>Cifrar las claves privadas de su monedero</translation>
+ <translation>Encriptar las claves privadas que pertenecen a su billetera</translation>
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
@@ -471,7 +471,7 @@
</message>
<message>
<source>Warning</source>
- <translation>Aviso</translation>
+ <translation>Advertencia</translation>
</message>
<message>
<source>Information</source>
@@ -519,7 +519,7 @@
</message>
<message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
- <translation>Muestre el mensaje de ayuda %1 para obtener una lista con posibles opciones de línea de comandos de Bitcoin</translation>
+ <translation>Muestra el mensaje de ayuda %1 para obtener una lista con posibles opciones de línea de comandos de Bitcoin.</translation>
</message>
<message>
<source>default wallet</source>
@@ -812,7 +812,7 @@
</message>
<message>
<source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
- <translation>Encriptar monedero. El monedero será cifrado con la contraseña que elija.</translation>
+ <translation>Cifrar monedero. El monedero será cifrado con la contraseña que elija.</translation>
</message>
<message>
<source>Encrypt Wallet</source>
@@ -1093,7 +1093,7 @@
</message>
<message>
<source>Open wallet warning</source>
- <translation>Advertencia sobre apertura de monedero</translation>
+ <translation>Aviso de apertura de monedero</translation>
</message>
<message>
<source>default wallet</source>
@@ -1115,6 +1115,10 @@
<translation>&amp;Principal</translation>
</message>
<message>
+ <source>Automatically start %1 after logging in to the system.</source>
+ <translation>Iniciar automaticamente %1 al encender el sistema.</translation>
+ </message>
+ <message>
<source>&amp;Start %1 on system login</source>
<translation>&amp; Comience %1 en el inicio de sesión del sistema</translation>
</message>
@@ -1164,7 +1168,7 @@
</message>
<message>
<source>Reset all client options to default.</source>
- <translation>Restablecer todas las opciones del cliente a las predeterminadas.</translation>
+ <translation>Restablecer todas las opciones predeterminadas del cliente.</translation>
</message>
<message>
<source>&amp;Reset Options</source>
@@ -1220,7 +1224,7 @@
</message>
<message>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
- <translation>Abrir automáticamente el puerto del cliente Bitcoin en el router. Esta opción solo funciona si el router admite UPnP y está activado.</translation>
+ <translation>Abrir automáticamente el puerto del cliente Bitcoin en el router. Esta opción solo funciona cuando el router admite UPnP y está activado.</translation>
</message>
<message>
<source>Map port using &amp;UPnP</source>
@@ -1236,7 +1240,7 @@
</message>
<message>
<source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
- <translation>Conéctese a la red de Bitcoin a través de un proxy SOCKS5.</translation>
+ <translation>Conectar a la red de Bitcoin a través de un proxy SOCKS5.</translation>
</message>
<message>
<source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
@@ -1280,7 +1284,7 @@
</message>
<message>
<source>Show only a tray icon after minimizing the window.</source>
- <translation>Minimizar la ventana a la bandeja de iconos del sistema.</translation>
+ <translation>Mostrar solo un icono de sistema después de minimizar la ventana</translation>
</message>
<message>
<source>&amp;Minimize to the tray instead of the taskbar</source>
@@ -1379,7 +1383,7 @@
<name>OverviewPage</name>
<message>
<source>Form</source>
- <translation>Desde</translation>
+ <translation>Formulario</translation>
</message>
<message>
<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>
@@ -1395,7 +1399,7 @@
</message>
<message>
<source>Your current spendable balance</source>
- <translation>Su balance actual gastable</translation>
+ <translation>Su saldo actual gastable</translation>
</message>
<message>
<source>Pending:</source>
@@ -1403,7 +1407,7 @@
</message>
<message>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
- <translation>Total de transacciones que deben ser confirmadas y que no cuentan aun con el balance gastable necesario.</translation>
+ <translation>Total de transacciones que deben ser confirmadas y que no cuentan con el saldo disponible necesario.</translation>
</message>
<message>
<source>Immature:</source>
@@ -1785,6 +1789,14 @@
<translation>Bloques sincronizados</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>El Sistema Autónomo mapeado utilizado para la selección diversificada de participantes.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>SA Mapeado</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Agente de usuario</translation>
</message>
@@ -2004,6 +2016,14 @@
<translation>Monto opcional a solicitar. Dejarlo vacío o en cero para no solicitar un monto específico.</translation>
</message>
<message>
+ <source>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>
+ <translation>Etiqueta opcional para asociar con la nueva dirección de recepción (utilizado por ti para identificar una factura). También esta asociado a la solicitud de pago.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Mensaje opcional asociado a la solicitud de pago que podría ser presentado al remitente </translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
<translation>Crear nueva dirección para recepción</translation>
</message>
@@ -2190,11 +2210,11 @@
</message>
<message>
<source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
- <translation>Al activarse, si la dirección de cambio está vacía o es inválida, las monedas serán enviadas a una nueva dirección generada.</translation>
+ <translation>Si se activa, pero la dirección de cambio está vacía o es inválida, las monedas serán enviadas a una nueva dirección generada.</translation>
</message>
<message>
<source>Custom change address</source>
- <translation>Dirección propia</translation>
+ <translation>Dirección de cambio personalizada.</translation>
</message>
<message>
<source>Transaction Fee:</source>
@@ -2257,6 +2277,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Polvo:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Esconder ajustes de tarifas de transacción</translation>
+ </message>
+ <message>
<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>Cuando hay menos volumen de transacciones que espacio en los bloques, los mineros y los nodos de retransmisión pueden imponer una comisión mínima. Pagar solo esta comisión mínima está bien, pero tenga en cuenta que esto puede resultar en una transacción nunca confirmada una vez que haya más demanda de transacciones de Bitcoin de la que la red puede procesar.</translation>
</message>
@@ -2330,7 +2354,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source> from wallet '%1'</source>
- <translation>de monedero %1</translation>
+ <translation>desde el monedero %1</translation>
</message>
<message>
<source>%1 to '%2'</source>
@@ -2341,6 +2365,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>%1 a %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>¿Desea preparar esta transacción?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>¿Seguro que quiere enviar?</translation>
</message>
@@ -2377,10 +2405,26 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Confirmar el envío de monedas</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Confirme la propuesta de transaccion</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Copiar la TBPF al portapapeles</translation>
+ </message>
+ <message>
<source>Send</source>
<translation>Enviar</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>TBPF copiada</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Visualización unicamente balance:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>La dirección de envío no es válida. Por favor, revísela.</translation>
</message>
@@ -2398,7 +2442,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Duplicate address found: addresses should only be used once each.</source>
- <translation>Dirección duplicada encontrada: la dirección sólo debería ser utilizada una vez por cada uso.</translation>
+ <translation>Dirección duplicada encontrada: las direcciones sólo deberían ser utilizadas una vez.</translation>
</message>
<message>
<source>Transaction creation failed!</source>
@@ -2418,11 +2462,11 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Warning: Invalid Bitcoin address</source>
- <translation>Peligro: Dirección de Bitcoin inválida</translation>
+ <translation>Advertencia: Dirección de Bitcoin inválida.</translation>
</message>
<message>
<source>Warning: Unknown change address</source>
- <translation>Peligro: Dirección de cambio desconocida</translation>
+ <translation>Advertencia: Dirección de cambio desconocida.</translation>
</message>
<message>
<source>Confirm custom change address</source>
@@ -2430,7 +2474,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>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>
- <translation>La dirección de cambio seleccionada no es parte de su monedero. Parte de sus fondos serán enviados a esta dirección. ¿Está seguro?</translation>
+ <translation>La dirección que ha seleccionado para el cambio no es parte de su monedero. Parte o todos sus fondos pueden ser enviados a esta dirección. ¿Está seguro?</translation>
</message>
<message>
<source>(no label)</source>
@@ -2476,8 +2520,12 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Eliminar esta entrada.</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>El monto a enviar en las unidades seleccionadas</translation>
+ </message>
+ <message>
<source>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>
- <translation>La comisión será deducida de la cantidad que sea enviada. El destinatario recibirá menos bitcoins que la cantidad introducida en el campo Cantidad. Si hay varios destinatarios seleccionados, la comisión será distribuida a partes iguales.</translation>
+ <translation>La comisión será deducida de la cantidad enviada. El destinatario recibirá menos bitcoins que la cantidad introducida en el campo Cantidad. Si hay varios destinatarios seleccionados, la comisión será distribuida a partes iguales.</translation>
</message>
<message>
<source>S&amp;ubtract fee from amount</source>
@@ -2509,7 +2557,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Pay To:</source>
- <translation>Paga a:</translation>
+ <translation>Pagar a:</translation>
</message>
<message>
<source>Memo:</source>
@@ -2602,6 +2650,14 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>La dirección Bitcoin con la que se firmó el mensaje</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>El mensaje firmado para verificar</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>La firma proporcionada cuando el mensaje fue firmado</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verificar el mensaje para comprobar que fue firmado con la dirección Bitcoin indicada</translation>
</message>
@@ -2634,6 +2690,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>El desbloqueo del monedero fue cancelado.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Sin error </translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>La llave privada para la dirección introducida no está disponible.</translation>
</message>
@@ -2817,7 +2877,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
- <translation>Los bitcoins generados deben madurar %1 bloques antes de que puedan gastarse. Cuando generó este bloque, se transmitió a la red para que se añadiera a la cadena de bloques. Si no consigue entrar en la cadena, su estado cambiará a "no aceptado" y ya no se podrá gastar. Esto puede ocurrir ocasionalmente si otro nodo genera un bloque a pocos segundos del suyo.</translation>
+ <translation>Las monedas generadas deben madurar %1 bloques antes de que puedan gastarse. Cuando generó este bloque, fue retransmitido a la red para que se añadiera a la cadena de bloques. Si no consigue entrar en la cadena, su estado cambiará a "no aceptado" y ya no se podrá gastar. Esto puede ocurrir ocasionalmente si otro nodo genera un bloque a pocos segundos del suyo.</translation>
</message>
<message>
<source>Debug information</source>
@@ -2899,7 +2959,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Immature (%1 confirmations, will be available after %2)</source>
- <translation>Inmaduro (%1 confirmación(es), Estarán disponibles después de %2)</translation>
+ <translation>Inmaduro (%1 confirmaciones, Estará disponible después de %2)</translation>
</message>
<message>
<source>Generated but not accepted</source>
@@ -3169,6 +3229,10 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>¿Desea incrementar la comisión?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>¿Desea preparar una transacción con aumento de comisión ?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Comisión actual:</translation>
</message>
@@ -3185,6 +3249,14 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Confirmar incremento de comisión</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>No se pudo preparar la transacción.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>TBPF copiada</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>No se ha podido firmar la transacción.</translation>
</message>
@@ -3272,7 +3344,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
- <translation>No se puede bloquear el directorio %s. %s ya se está ejecutando.</translation>
+ <translation>No se puede bloquear el directorio %s. %s probablemente ya se está ejecutando.</translation>
</message>
<message>
<source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
@@ -3300,7 +3372,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
- <translation>Esta es la cuota de transacción que puede descartar si el cambio es más pequeño que el polvo a este nivel.</translation>
+ <translation>Esta es la comisión por transacción que puede descartar si el cambio es más pequeño que el polvo a este nivel.</translation>
</message>
<message>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
@@ -3332,7 +3404,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Cannot resolve -%s address: '%s'</source>
- <translation>No se puede resolver -%s direccion: '%s'</translation>
+ <translation>No se puede resolver -%s dirección: '%s'</translation>
</message>
<message>
<source>Change index out of range</source>
@@ -3351,6 +3423,14 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
<translation>Corrupción de base de datos de bloques detectada.</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>No se pudo encontrar el archivo asmap %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>No se pudo analizar el archivo asmap %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>¿Quiere reconstruir la base de datos de bloques ahora?</translation>
</message>
@@ -3368,7 +3448,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Error loading %s: Private keys can only be disabled during creation</source>
- <translation>Error cargando %s: Las claves privadas solo pueden ser deshabilitadas durante la creación</translation>
+ <translation>Error cargando %s: Las llaves privadas solo pueden ser deshabilitadas durante la creación.</translation>
</message>
<message>
<source>Error loading %s: Wallet corrupted</source>
@@ -3512,7 +3592,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
- <translation>Cantidad no válida para -maxtxfee=&lt;amount&gt;: '%s' (debe ser por lo menos la comisión mínima de %s para prevenir transacciones atascadas)</translation>
+ <translation>Cantidad no válida para -maxtxfee=&lt;amount&gt;: '%s' (debe ser al menos la comisión mínima de %s para prevenir transacciones atascadas)</translation>
</message>
<message>
<source>The transaction amount is too small to send after the fee has been deducted</source>
@@ -3622,7 +3702,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Verifying wallet(s)...</source>
- <translation>Verificando monedero...</translation>
+ <translation>Verificando monedero(s)...</translation>
</message>
<message>
<source>Warning: unknown new rules activated (versionbit %i)</source>
@@ -3666,7 +3746,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
- <translation>Esta es la tarifa mínima a pagar en cada transacción.</translation>
+ <translation>Esta es la comisión por transacción mínima a pagar en cada transacción.</translation>
</message>
<message>
<source>This is the transaction fee you will pay if you send a transaction.</source>
@@ -3686,7 +3766,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Unknown network specified in -onlynet: '%s'</source>
- <translation>La red especificada en -onlynet '%s' es desconocida</translation>
+ <translation>Red desconocida especificada en -onlynet '%s'</translation>
</message>
<message>
<source>Insufficient funds</source>
@@ -3718,7 +3798,7 @@ Nota: Dado que la comisión se calcula por byte, una comisión de "100 satoshis
</message>
<message>
<source>Cannot downgrade wallet</source>
- <translation>No se puede rebajar el monedero</translation>
+ <translation>No se puede actualizar a una versión mas antigua el monedero.</translation>
</message>
<message>
<source>Rescanning...</source>
diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts
index 4b864f4e74..265eeeb2f7 100644
--- a/src/qt/locale/bitcoin_es_CL.ts
+++ b/src/qt/locale/bitcoin_es_CL.ts
@@ -997,7 +997,7 @@ Exportar los datos en la pestaña actual a un archivo</translation>
</message>
<message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
- <translation>(0 = auto, &lt;0 = deja muchos núcleos gratis)</translation>
+ <translation>(0 = auto, &lt;0 = deja esta cantidad de núcleos libres)</translation>
</message>
<message>
<source>W&amp;allet</source>
diff --git a/src/qt/locale/bitcoin_es_CO.ts b/src/qt/locale/bitcoin_es_CO.ts
index 5c83f25286..751dd5ca5b 100644
--- a/src/qt/locale/bitcoin_es_CO.ts
+++ b/src/qt/locale/bitcoin_es_CO.ts
@@ -699,6 +699,10 @@
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <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>Crea una billetera en blanco. Las billeteras en blanco inicialmente no tienen llaves privadas o texto. Las llaves privadas y las direcciones pueden ser importadas, o se puede establecer una semilla HD, más tarde.</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -804,6 +808,10 @@
<translation>Al hacer clic OK, %1 iniciará el proceso de descarga y procesará el blockchain completo de %4 (%2 GB), iniciando desde el la transacción más antigua %3 cuando %4 se ejecutó inicialmente.</translation>
</message>
<message>
+ <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>Revertir esta configuración requiere descargar la blockchain completa nuevamente. Es más rápido descargar la cadena completa y podarla después. Desactiva algunas funciones avanzadas.</translation>
+ </message>
+ <message>
<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>El primer proceso de sincronización consume muchos recursos, y es posible que puedan ocurrir problemas de hardware que anteriormente no hayas notado. Cada vez que ejecutes %1 automáticamente se reiniciará el proceso de sincronización desde el punto que lo dejaste anteriormente.</translation>
</message>
@@ -993,7 +1001,7 @@
</message>
<message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
- <translation>(0 = auto, &lt;0 = deja muchos núcleos gratis)</translation>
+ <translation>(0 = auto, &lt;0 = deja esta cantidad de núcleos libres)</translation>
</message>
<message>
<source>W&amp;allet</source>
diff --git a/src/qt/locale/bitcoin_es_MX.ts b/src/qt/locale/bitcoin_es_MX.ts
index acb7f0dc3d..2c0028debc 100644
--- a/src/qt/locale/bitcoin_es_MX.ts
+++ b/src/qt/locale/bitcoin_es_MX.ts
@@ -70,6 +70,10 @@
<translation>Estas son tus direcciones de Bitcoin para enviar pagos. Siempre revisa el monto y la dirección de envío antes de enviar monedas.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Estas son tus direcciones Bitcoin para recibir pagos. Usa el botón "Crear Nueva Dirección de Recepción" en la pestaña de recepciones para crear nuevas direcciones.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Copiar dirección</translation>
</message>
@@ -132,6 +136,10 @@
<translation>Repita la nueva contraseña</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Mostrar contraseña</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Encriptar cartera</translation>
</message>
@@ -172,6 +180,30 @@
<translation>Cartera encriptada</translation>
</message>
<message>
+ <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>Ingresa la nueva frase contraseña para la billetera &lt;br/&gt;Por favor usa una frase contraseña de &lt;b&gt;diez o mas caracteres aleatorios &lt;/b&gt;, o &lt;b&gt;ocho o mas palabras&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Ingresa la antigua frase de contraseña y la nueva frase de contraseña para la billetera.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Recuerda que encriptar tu billetera no puede proteger completamente tus bitcoins de ser robadas por malware que haya infectado tu computadora.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Billetera para ser encriptada</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Tu billetera está por ser encriptada</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Tu billetera ha sido encriptada</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>IMPORTANTE: cualquier copia de seguridad anterior que haya hecho de su archivo de cartera debe ser reemplazada por el archivo de cartera encriptado y recién generado. Por razones de seguridad, las copias de seguridad anteriores del archivo de cartera sin cifrar serán inútiles tan pronto como empieces a usar la nueva billetera encriptada.</translation>
</message>
@@ -294,6 +326,14 @@
<translation>Abrir &amp;URL...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Crear cartera</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Crear una nueva cartera</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Cartera:</translation>
</message>
@@ -389,10 +429,30 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
 </translation>
</message>
<message>
+ <source>Show the list of used sending addresses and labels</source>
+ <translation>Mostrar la lista de direcciones y etiquetas de envío usadas</translation>
+ </message>
+ <message>
+ <source>Show the list of used receiving addresses and labels</source>
+ <translation>Mostrar la lista de direcciones y etiquetas de recepción usadas</translation>
+ </message>
+ <message>
<source>&amp;Command-line options</source>
<translation>opciones de la &amp;Linea de comandos</translation>
</message>
<message>
+ <source>Indexing blocks on disk...</source>
+ <translation>Indexando bloques en el disco...</translation>
+ </message>
+ <message>
+ <source>Processing blocks on disk...</source>
+ <translation>Procesando bloques en el disco...</translation>
+ </message>
+ <message>
+ <source>Transactions after this will not yet be visible.</source>
+ <translation>Las transacciones después de esto todavía no serán visibles.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -409,10 +469,64 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Actualizado al dia </translation>
</message>
<message>
+ <source>Open Wallet</source>
+ <translation>Abrir Cartera</translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>Abrir una cartera</translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>Cerrar Cartera...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>Cerrar cartera</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>cartera predeterminada</translation>
+ </message>
+ <message>
+ <source>No wallets available</source>
+ <translation>No hay carteras disponibles</translation>
+ </message>
+ <message>
+ <source>&amp;Window</source>
+ <translation>&amp;Ventana</translation>
+ </message>
+ <message>
+ <source>Minimize</source>
+ <translation>Minimizar</translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>Ventana Principal</translation>
+ </message>
+ <message>
+ <source>Connecting to peers...</source>
+ <translation>Conectando con los compañeros...</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>Recibiendo...</translation>
</message>
<message>
+ <source>Error: %1</source>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
+ <source>Warning: %1</source>
+ <translation>Alerta: %1</translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>Fecha: %1
+</translation>
+ </message>
+ <message>
<source>Sent transaction</source>
<translation>Enviar Transacción</translation>
</message>
@@ -428,10 +542,18 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<translation>La cartera esta &lt;b&gt;encriptada&lt;/b&gt; y &lt;b&gt;bloqueada&lt;/b&gt; actualmente </translation>
</message>
- </context>
+ <message>
+ <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
+ <translation>Se produjo un error fatal. Bitcoin ya no puede continuar de forma segura y va a renunciar.</translation>
+ </message>
+</context>
<context>
<name>CoinControlDialog</name>
<message>
+ <source>Coin Selection</source>
+ <translation>Selección de moneda</translation>
+ </message>
+ <message>
<source>Quantity:</source>
<translation>Cantidad</translation>
</message>
@@ -448,6 +570,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Cuota:</translation>
</message>
<message>
+ <source>Dust:</source>
+ <translation>Remanente monetario:</translation>
+ </message>
+ <message>
<source>After Fee:</source>
<translation>Después de los cargos por comisión. </translation>
</message>
@@ -456,14 +582,38 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Cambio</translation>
</message>
<message>
+ <source>(un)select all</source>
+ <translation>(De)seleccionar todo</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Modo árbol </translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Modo lista </translation>
+ </message>
+ <message>
<source>Amount</source>
<translation>Monto</translation>
</message>
<message>
+ <source>Received with label</source>
+ <translation>Recibido con etiqueta</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>recibido con dirección</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>Fecha</translation>
</message>
<message>
+ <source>Confirmations</source>
+ <translation>Confirmaciones</translation>
+ </message>
+ <message>
<source>Confirmed</source>
<translation>Confirmado </translation>
</message>
@@ -512,6 +662,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>no</translation>
</message>
<message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Esta capa se vuelve roja si algún destinatario recibe un monto menor al actual limite del remanente monetario </translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(sin etiqueta)</translation>
</message>
@@ -522,10 +676,46 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>La creación de la cartera falló</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Crear advertencia de cartera</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Crear una cartera </translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Nombre de la cartera </translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Encriptar la cartera. La cartera será encriptada con una frase de contraseña de tu elección.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Encripta la cartera</translation>
+ </message>
+ <message>
+ <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>Desactivar las llaves privadas de esta cartera. Las carteras con las llaves privadas desactivadas no tendrán llaves privadas y no podrán tener una semilla HD o llaves privadas importadas. Esto es ideal para las carteras "watch-only".</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Desactivar las claves privadas</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Crear</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -537,6 +727,14 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>&amp;Etiqueta</translation>
</message>
<message>
+ <source>The label associated with this address list entry</source>
+ <translation>La etiqueta asociada a esta entrada de la lista de direcciones</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>La dirección asociada a esta entrada de la lista de direcciones. Esto sólo puede ser modificado para las direcciones de envío.</translation>
+ </message>
+ <message>
<source>&amp;Address</source>
<translation>&amp;Dirección</translation>
</message>
@@ -564,10 +762,22 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<context>
<name>FreespaceChecker</name>
<message>
+ <source>A new data directory will be created.</source>
+ <translation>Un nuevo directorio de datos será creado.</translation>
+ </message>
+ <message>
<source>name</source>
<translation>nombre</translation>
</message>
- </context>
+ <message>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>El camino ya existe, y no es un directorio.</translation>
+ </message>
+ <message>
+ <source>Cannot create data directory here.</source>
+ <translation>No se puede crear un directorio de datos aquí.</translation>
+ </message>
+</context>
<context>
<name>HelpMessageDialog</name>
<message>
@@ -582,10 +792,34 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<context>
<name>Intro</name>
<message>
+ <source>Welcome</source>
+ <translation>Bienvenido</translation>
+ </message>
+ <message>
+ <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>Revertir esta configuración requiere descargar nuevamente la cadena de bloques en su totalidad. es mas eficaz descargar la cadena de bloques completa y después reducirla. Desabilitará algunas funciones avanzadas.</translation>
+ </message>
+ <message>
+ <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>La sincronización inicial es muy demandante, por lo que algunos problemas en su equipo de computo que no hayan sido detectados pueden verse reflejados. Cada vez que corra al %1, continuará descargando donde se le dejó.</translation>
+ </message>
+ <message>
+ <source>Use the default data directory</source>
+ <translation>Usar el directorio de datos predeterminado</translation>
+ </message>
+ <message>
+ <source>Use a custom data directory:</source>
+ <translation>Usar un directorio de datos customizado:</translation>
+ </message>
+ <message>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>La cartera también se almacenará en este directorio.</translation>
+ </message>
+ <message>
<source>Error</source>
<translation>Error</translation>
</message>
@@ -596,12 +830,68 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<source>Form</source>
<translation>Formulario</translation>
</message>
+ <message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Las transacciones recientes pueden no ser visibles todavía, y por lo tanto el saldo de su cartera podría ser incorrecto. Esta información será correcta una vez que su cartera haya terminado de sincronizarse con la red de bitcoin, como se detalla abajo.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Los intentos de gastar bitcoins que se vean afectados por transacciones aún no mostradas no serán aceptados por la red.</translation>
+ </message>
+ <message>
+ <source>Number of blocks left</source>
+ <translation>Número de bloques restantes</translation>
+ </message>
+ <message>
+ <source>Unknown...</source>
+ <translation>Desconocido...</translation>
+ </message>
+ <message>
+ <source>Progress</source>
+ <translation>Progreso </translation>
+ </message>
+ <message>
+ <source>Progress increase per hour</source>
+ <translation>Aumento del progreso por hora</translation>
+ </message>
+ <message>
+ <source>calculating...</source>
+ <translation>calculando...</translation>
+ </message>
+ <message>
+ <source>Estimated time left until synced</source>
+ <translation>Tiempo estimado restante hasta la sincronización</translation>
+ </message>
+ <message>
+ <source>Hide</source>
+ <translation>Ocultar </translation>
+ </message>
+ <message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
</context>
<context>
<name>OpenURIDialog</name>
- </context>
+ <message>
+ <source>Open bitcoin URI</source>
+ <translation>Abrir la URI de bitcoin</translation>
+ </message>
+ <message>
+ <source>URI:</source>
+ <translation>URI:</translation>
+ </message>
+</context>
<context>
<name>OpenWalletActivity</name>
+ <message>
+ <source>Open wallet failed</source>
+ <translation>Abrir la cartera falló</translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>cartera predeterminada</translation>
+ </message>
</context>
<context>
<name>OptionsDialog</name>
@@ -610,10 +900,38 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Opciones</translation>
</message>
<message>
+ <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>Minimizar en lugar de salir de la aplicación cuando la ventana se cierra. Cuando esta opción está activada, la aplicación se cerrará sólo después de seleccionar Salir en el menú.</translation>
+ </message>
+ <message>
+ <source>Open Configuration File</source>
+ <translation>Abrir Configuración de Archivo</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>Cartera</translation>
</message>
<message>
+ <source>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>
+ <translation>Si usted desactiva el gasto de cambio no confirmado, el cambio de una transacción no puede ser utilizado hasta que esa transacción tenga al menos una confirmación. Esto también afecta la manera en que se calcula su saldo.</translation>
+ </message>
+ <message>
+ <source>&amp;Spend unconfirmed change</source>
+ <translation>&amp;Gastar el cambio no confirmado</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>Aceptar las conexiones del exterior.</translation>
+ </message>
+ <message>
+ <source>&amp;Window</source>
+ <translation>&amp;Ventana</translation>
+ </message>
+ <message>
+ <source>User Interface &amp;language:</source>
+ <translation>Idioma de la interfaz de usuario:</translation>
+ </message>
+ <message>
<source>none</source>
<translation>Ninguno </translation>
</message>
@@ -621,6 +939,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<source>Error</source>
<translation>Error</translation>
</message>
+ <message>
+ <source>This change would require a client restart.</source>
+ <translation>Este cambio requeriría un reinicio del cliente.</translation>
+ </message>
</context>
<context>
<name>OverviewPage</name>
@@ -634,7 +956,15 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
</context>
<context>
<name>PeerTableModel</name>
- </context>
+ <message>
+ <source>Sent</source>
+ <translation>Enviado</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Recibido</translation>
+ </message>
+</context>
<context>
<name>QObject</name>
<message>
@@ -642,16 +972,144 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Monto</translation>
</message>
<message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>Error: %1</translation>
+ </message>
+ <message>
<source>unknown</source>
<translation>desconocido</translation>
</message>
</context>
<context>
<name>QRImageWidget</name>
- </context>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Guardar imagen...</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;Copiar Imagen</translation>
+ </message>
+ <message>
+ <source>Error encoding URI into QR Code.</source>
+ <translation>Error codificando la URI en el Código QR.</translation>
+ </message>
+ <message>
+ <source>QR code support not available.</source>
+ <translation>El soporte del código QR no está disponible.</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>Guardar Código QR</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG imagen (*.png)</translation>
+ </message>
+</context>
<context>
<name>RPCConsole</name>
- </context>
+ <message>
+ <source>N/A</source>
+ <translation>N/A</translation>
+ </message>
+ <message>
+ <source>Client version</source>
+ <translation>Versión cliente </translation>
+ </message>
+ <message>
+ <source>&amp;Information</source>
+ <translation>&amp;Información</translation>
+ </message>
+ <message>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <source>Network</source>
+ <translation>Red</translation>
+ </message>
+ <message>
+ <source>Name</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Cartera:</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(ninguno)</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Recibido</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>Enviado</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>Reducir el tamaño de la letra</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Aumentar el tamaño de la letra</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Servicios</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>Tiempo de conexión</translation>
+ </message>
+ <message>
+ <source>Last Send</source>
+ <translation>Último envío</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>Última recepción</translation>
+ </message>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>Actividad de la red desactivada</translation>
+ </message>
+ <message>
+ <source>Executing command without any wallet</source>
+ <translation>Ejecutando el comando sin ninguna cartera</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>nunca</translation>
+ </message>
+ <message>
+ <source>Inbound</source>
+ <translation>Entrada</translation>
+ </message>
+ <message>
+ <source>Outbound</source>
+ <translation>Salida</translation>
+ </message>
+ <message>
+ <source>Yes</source>
+ <translation>Sí</translation>
+ </message>
+ <message>
+ <source>No</source>
+ <translation>No</translation>
+ </message>
+ <message>
+ <source>Unknown</source>
+ <translation>Desconocido</translation>
+ </message>
+</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
@@ -671,6 +1129,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Mensaje opcional para agregar a la solicitud de pago, el cual será mostrado cuando la solicitud este abierta. Nota: El mensaje no se manda con el pago a travéz de la red de Bitcoin.</translation>
</message>
<message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>Una etiqueta opcional para asociar a la nueva dirección de recepción.</translation>
+ </message>
+ <message>
<source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
<translation>Use este formulario para la solicitud de pagos. Todos los campos son &lt;b&gt;opcionales&lt;/b&gt;</translation>
</message>
@@ -679,6 +1141,46 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Monto opcional a solicitar. Dejarlo vacion o en cero no solicita un monto especifico.</translation>
</message>
<message>
+ <source>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>
+ <translation>Una etiqueta opcional para asociar a la nueva dirección de recepción (utilizada por usted para identificar una factura). También se adjunta a la solicitud de pago.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Un mensaje opcional que se adjunta a la solicitud de pago y que puede mostrarse al remitente.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Crear una nueva dirección de recepción</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>Borrar todos los campos del formulario.</translation>
+ </message>
+ <message>
+ <source>Clear</source>
+ <translation>Borrar </translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>Historial de pagos solicitados</translation>
+ </message>
+ <message>
+ <source>Show the selected request (does the same as double clicking an entry)</source>
+ <translation>Mostrar la solicitud seleccionada (hace lo mismo que hacer doble clic en una entrada)</translation>
+ </message>
+ <message>
+ <source>Show</source>
+ <translation>Mostrar</translation>
+ </message>
+ <message>
+ <source>Remove the selected entries from the list</source>
+ <translation>Eliminar las entradas seleccionadas de la lista</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>Eliminar </translation>
+ </message>
+ <message>
<source>Copy label</source>
<translation>Copiar capa </translation>
</message>
@@ -694,6 +1196,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>&amp;Copiar dirección</translation>
</message>
<message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Guardar imagen...</translation>
+ </message>
+ <message>
<source>Address</source>
<translation>Dirección</translation>
</message>
@@ -764,10 +1270,22 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Cambio</translation>
</message>
<message>
+ <source>Hide</source>
+ <translation>Ocultar </translation>
+ </message>
+ <message>
<source>Send to multiple recipients at once</source>
<translation>Enviar a múltiples receptores a la vez</translation>
</message>
<message>
+ <source>Clear all fields of the form.</source>
+ <translation>Despeja todos los campos del formulario.</translation>
+ </message>
+ <message>
+ <source>Dust:</source>
+ <translation>Remanente monetario:</translation>
+ </message>
+ <message>
<source>Balance:</source>
<translation>Saldo:</translation>
</message>
@@ -800,22 +1318,70 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Copiar cambio</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>¿Quiere redactar esta transacción?</translation>
+ </message>
+ <message>
+ <source>Are you sure you want to send?</source>
+ <translation>¿Está seguro de que quiere enviar?</translation>
+ </message>
+ <message>
<source>or</source>
<translation>o</translation>
</message>
<message>
+ <source>Please, review your transaction.</source>
+ <translation>Por favor, revise su transacción.</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>Cuota de transacción</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Cantidad total</translation>
+ </message>
+ <message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>Para revisar la lista de destinatarios haga clic en "Mostrar detalles..."</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Confirme para enviar monedas</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Confirmar la propuesta de transacción</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Enviar</translation>
+ </message>
+ <message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>La dirección del destinatario no es válida. Por favor, vuelva a verificarla.</translation>
+ </message>
+ <message>
<source>The amount to pay must be larger than 0.</source>
<translation>El monto a pagar debe ser mayor a 0</translation>
</message>
<message>
+ <source>The amount exceeds your balance.</source>
+ <translation>La cantidad excede su saldo.</translation>
+ </message>
+ <message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>Duplicado de la dirección encontrada: las direcciones sólo deben ser utilizadas una vez cada una.</translation>
+ </message>
+ <message>
<source>Transaction creation failed!</source>
<translation>¡La creación de la transación falló!</translation>
</message>
<message>
+ <source>Payment request expired.</source>
+ <translation>La solicitud de pago expiró.</translation>
+ </message>
+ <message>
<source>Warning: Invalid Bitcoin address</source>
<translation>Advertencia: Dirección de Bitcoin invalida</translation>
</message>
@@ -824,6 +1390,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Advertencia: Cambio de dirección desconocido</translation>
</message>
<message>
+ <source>Confirm custom change address</source>
+ <translation>Confirmar la dirección de cambio personalizada</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(sin etiqueta)</translation>
</message>
@@ -843,6 +1413,14 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>&amp;Etiqueta</translation>
</message>
<message>
+ <source>Choose previously used address</source>
+ <translation>Elegir la dirección utilizada anteriormente</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>La dirección de Bitcoin para enviar el pago a</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
@@ -859,10 +1437,26 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>Quitar esta entrada</translation>
</message>
<message>
+ <source>Use available balance</source>
+ <translation>Usar el saldo disponible</translation>
+ </message>
+ <message>
<source>Message:</source>
<translation>Mensaje:</translation>
</message>
<message>
+ <source>This is an unauthenticated payment request.</source>
+ <translation>Esta es una solicitud de pago no autentificada.</translation>
+ </message>
+ <message>
+ <source>This is an authenticated payment request.</source>
+ <translation>Esta es una solicitud de pago autentificada.</translation>
+ </message>
+ <message>
+ <source>Enter a label for this address to add it to the list of used addresses</source>
+ <translation>Introducir una etiqueta para esta dirección para añadirla a la lista de direcciones utilizadas</translation>
+ </message>
+ <message>
<source>Pay To:</source>
<translation>Pago para:</translation>
</message>
@@ -877,6 +1471,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Choose previously used address</source>
+ <translation>Elegir la dirección utilizada anteriormente</translation>
+ </message>
+ <message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
@@ -935,6 +1533,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<translation>etiqueta</translation>
</message>
<message>
+ <source>Transaction fee</source>
+ <translation>Cuota de transacción</translation>
+ </message>
+ <message>
<source>Message</source>
<translation>Mensaje</translation>
</message>
@@ -1153,6 +1755,10 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
</context>
<context>
<name>WalletController</name>
+ <message>
+ <source>Close wallet</source>
+ <translation>Cerrar cartera</translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
@@ -1167,7 +1773,11 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<source>Send Coins</source>
<translation>Enviar monedas</translation>
</message>
- </context>
+ <message>
+ <source>default wallet</source>
+ <translation>cartera predeterminada</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
<message>
@@ -1190,10 +1800,94 @@ Solicitar pagos (genera códigos QR y bitcoin: URI)
<context>
<name>bitcoin-core</name>
<message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Falló al volver a escanear la cartera durante la inicialización</translation>
+ </message>
+ <message>
+ <source>Importing...</source>
+ <translation>Importando...</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Cargando la lista de anuncios...</translation>
+ </message>
+ <message>
+ <source>Not enough file descriptors available.</source>
+ <translation>No hay suficientes descriptores de archivos disponibles.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>La tarifa de la transacción y el cálculo del cambio fallaron</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Incapaz de generar claves</translation>
+ </message>
+ <message>
<source>Verifying blocks...</source>
<translation>Verificando bloques...</translation>
</message>
<message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>La cantidad de la transacción es demasiado pequeña para enviarla después de que se haya deducido la tarifa</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Error de lectura de la base de datos, apagando.</translation>
+ </message>
+ <message>
+ <source>Signing transaction failed</source>
+ <translation>La transacción de firma falló</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>El monto de la transacción es demasiado pequeño para pagar la tarifa</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>Este es un software experimental.</translation>
+ </message>
+ <message>
+ <source>Transaction amount too small</source>
+ <translation>El monto de la transacción es demasiado pequeño</translation>
+ </message>
+ <message>
+ <source>Transaction too large</source>
+ <translation>La transacción es demasiado grande</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>Incapaz de generar claves iniciales</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Verificando la(s) cartera(s)...</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>Esta es la tarifa de transacción que puede pagar cuando no se dispone de estimaciones de tarifas.</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>Esta es la tarifa de transacción mínima que se paga en cada transacción.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <translation>Esta es la tarifa de transacción que pagará si envía una transacción.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>Los montos de las transacciones no deben ser negativos</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>La transacción debe tener al menos un destinatario</translation>
+ </message>
+ <message>
+ <source>Insufficient funds</source>
+ <translation>Fondos insuficientes</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Cargando indice de bloques... </translation>
</message>
diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts
index f055141fc3..025527b680 100644
--- a/src/qt/locale/bitcoin_et.ts
+++ b/src/qt/locale/bitcoin_et.ts
@@ -132,6 +132,10 @@
<translation>Korda uut parooli</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Näita salafraasi</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Krüpteeri rahakott</translation>
</message>
@@ -172,6 +176,30 @@
<translation>Rahakott krüpteeritud</translation>
</message>
<message>
+ <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>Sisesta rahakotile uus salafraas.&lt;br/&gt;Kasuta salafraasi millles on&lt;b&gt;kümme või rohkem juhuslikku sümbolit&lt;b&gt;,või&lt;b&gt;kaheksa või rohkem sõna&lt;b/&gt;.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Sisesta rahakoti vana salafraas ja uus salafraas.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Pea meeles, et rahakoti krüpteerimine ei välista bitcoinide vargust, kui sinu arvuti on nakatunud pahavaraga.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Krüpteeritav rahakott</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Rahakott krüpteeritakse.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Rahakott krüpteeritud.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>TÄHTIS: Kõik varasemad rahakoti varundfailid tuleks üle kirjutada äsja loodud krüpteeritud rahakoti failiga. Turvakaalutlustel tühistatakse krüpteerimata rahakoti failid alates uue, krüpteeritud rahakoti, kasutusele võtust.</translation>
</message>
@@ -214,7 +242,11 @@
<source>IP/Netmask</source>
<translation>IP/Võrgumask</translation>
</message>
- </context>
+ <message>
+ <source>Banned Until</source>
+ <translation>Blokeeritud kuni</translation>
+ </message>
+</context>
<context>
<name>BitcoinGUI</name>
<message>
@@ -254,6 +286,10 @@
<translation>&amp;Teave %1</translation>
</message>
<message>
+ <source>Show information about %1</source>
+ <translation>Näita informatsiooni %1 kohta</translation>
+ </message>
+ <message>
<source>About &amp;Qt</source>
<translation>Teave &amp;Qt kohta</translation>
</message>
@@ -266,6 +302,10 @@
<translation>&amp;Valikud...</translation>
</message>
<message>
+ <source>Modify configuration options for %1</source>
+ <translation>Muuda %1 seadeid</translation>
+ </message>
+ <message>
<source>&amp;Encrypt Wallet...</source>
<translation>&amp;Krüpteeri Rahakott</translation>
</message>
@@ -282,6 +322,18 @@
<translation>Ava &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Loo rahakott</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Loo uus rahakott</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>Rahakott:</translation>
+ </message>
+ <message>
<source>Reindexing blocks on disk...</source>
<translation>Kõvakettal olevate plokkide reindekseerimine...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_eu.ts b/src/qt/locale/bitcoin_eu.ts
index 7aca604eeb..2881754ac9 100644
--- a/src/qt/locale/bitcoin_eu.ts
+++ b/src/qt/locale/bitcoin_eu.ts
@@ -358,6 +358,10 @@
<translation>Blokeak diskoan berriro zerrendatzen...</translation>
</message>
<message>
+ <source>Send coins to a Bitcoin address</source>
+ <translation>Bidali txanponak Bitcoin helbide batera</translation>
+ </message>
+ <message>
<source>Change the passphrase used for wallet encryption</source>
<translation>Diruzorroa enkriptatzeko erabilitako pasahitza aldatu</translation>
</message>
@@ -382,6 +386,10 @@
<translation>Lehio nagusia erakutsi edo izkutatu</translation>
</message>
<message>
+ <source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
+ <translation>Egiaztatu mesua Bitcoin helbide espezifikoarekin erregistratu direla ziurtatzeko</translation>
+ </message>
+ <message>
<source>&amp;File</source>
<translation>&amp;Artxiboa</translation>
</message>
@@ -406,10 +414,22 @@
<translation>Akatsa</translation>
</message>
<message>
+ <source>Information</source>
+ <translation>Informazioa</translation>
+ </message>
+ <message>
<source>Up to date</source>
<translation>Eguneratua</translation>
</message>
<message>
+ <source>&amp;Sending addresses</source>
+ <translation>&amp;Helbideak bidaltzen</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses</source>
+ <translation>&amp;Helbideak jasotzen</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Diruzorroa zabaldu</translation>
</message>
@@ -426,10 +446,18 @@
<translation>Diruzorroa itxi</translation>
</message>
<message>
+ <source>default wallet</source>
+ <translation>Diruzorro lehenetsia</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Lehioa</translation>
</message>
<message>
+ <source>Minimize</source>
+ <translation>Txikitu</translation>
+ </message>
+ <message>
<source>Zoom</source>
<translation>Gerturatu</translation>
</message>
@@ -701,6 +729,10 @@
<source>Open wallet warning</source>
<translation>Diruzorroa irekitzen abisua</translation>
</message>
+ <message>
+ <source>default wallet</source>
+ <translation>Diruzorro lehenetsia</translation>
+ </message>
</context>
<context>
<name>OptionsDialog</name>
@@ -1194,7 +1226,11 @@
<source>Send Coins</source>
<translation>Txanponak bidali</translation>
</message>
- </context>
+ <message>
+ <source>default wallet</source>
+ <translation>Diruzorro lehenetsia</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
<message>
diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts
index b19bb00160..33833baed4 100644
--- a/src/qt/locale/bitcoin_fa.ts
+++ b/src/qt/locale/bitcoin_fa.ts
@@ -7,7 +7,7 @@
</message>
<message>
<source>Create a new address</source>
- <translation>گشایش آدرس جدید</translation>
+ <translation>ساخت یک آدرس جدید</translation>
</message>
<message>
<source>&amp;New</source>
@@ -15,7 +15,7 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>کپی کردن حساب انتخاب شده به حافظه سیستم - کلیپ بورد</translation>
+ <translation>کپی کردن آدرس انتخاب شده به حافظه کلیپ بورد سیستم</translation>
</message>
<message>
<source>&amp;Copy</source>
@@ -70,6 +70,10 @@
<translation>اینها آدرس‌های شما برای ارسال وجوه هستند. همیشه قبل از ارسال، مقدار و آدرس گیرنده را بررسی کنید.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>اینها آدرس بیت کوین های دریافتی شما می باشدند. در تب دریافت از دکمه 'ایحاد آدرس جدید' برای ساخت آدرس جدید استفاده نمائید.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>کپی آدرس</translation>
</message>
@@ -132,16 +136,20 @@
<translation>رمز/پَس فرِیز را دوباره وارد کنید</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>نمایش رمز</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>رمزگذاری کیف پول</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>برای انجام این عملیات، باید رمز کیف‌پول را وارد کنید.</translation>
+ <translation>این عملیات نیاز به رمز کیف ‌پول شما دارد تا کیف پول باز شود</translation>
</message>
<message>
<source>Unlock wallet</source>
- <translation>بازکردن کیف‌پول</translation>
+ <translation>بازکردن کیف ‌پول</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
@@ -165,18 +173,38 @@
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
- <translation>آیا از رمزگذاری کیف‌پول خود اطمینان دارید؟</translation>
+ <translation>آیا از رمزگذاری کیف ‌پول خود اطمینان دارید؟</translation>
</message>
<message>
<source>Wallet encrypted</source>
<translation>کیف پول رمزگذاری شده است</translation>
</message>
<message>
+ <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>برای کیف پول خود یک رمز جدید وارد نمائید&lt;br/&gt;لطفاً رمز کیف پول انتخابی را بدین گونه بسازید&lt;b&gt;انتخاب ده ویا بیشتر کاراکتر تصادفی&lt;/b&gt; یا &lt;b&gt; حداقل هشت کلمه&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>رمز عبور قدیمی و رمز عبور جدید کیف پول خود را وارد کنید.</translation>
+ </message>
+ <message>
<source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<translation>والت رمز بندی شد .
یاد داشته باشید که پنجره رمز شده نمی تواند کلا از سرقت نرم افزارهای مخرب محافظ کند</translation>
</message>
<message>
+ <source>Wallet to be encrypted</source>
+ <translation>کیف پول رمز نگاری شده است</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>کیف پول شما در حال رمز نگاری می باشد.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>کیف پول شما اکنون رمزنگاری گردیده است.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>مهم: هر بک‌آپ قبلی که از کیف‌پول خود گرفته‌اید، با نسخه‌ی جدید رمزنگاری‌شده جایگزین خواهد شد. به دلایل امنیتی، پس از رمزنگاری کیف‌پول، بک‌آپ‌های قدیمی شما بلااستفاده خواهد شد.</translation>
</message>
@@ -299,6 +327,14 @@
<translation>بازکردن آدرس...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>ایجاد کیف پول</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>ساخت کیف پول جدید</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>کیف پول:</translation>
</message>
@@ -447,6 +483,14 @@
<translation>به روز</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>پنجره گره</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>باز کردن کنسول دی باگ و تشخیص گره</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>ادرس ارسال</translation>
</message>
@@ -455,6 +499,10 @@
<translation>ادرس درسافت</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>بارک کردن یک بیت‌کوین: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>باز کردن حساب</translation>
</message>
@@ -463,14 +511,30 @@
<translation>باز کردن یک حساب</translation>
</message>
<message>
+ <source>Close Wallet...</source>
+ <translation>بستن کیف پول...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>کیف پول را ببندید</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>کیف پول پیش‌فرض</translation>
</message>
<message>
+ <source>No wallets available</source>
+ <translation>هیچ کیف پولی در دسترس نمی باشد</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>پنجره</translation>
</message>
<message>
+ <source>Minimize</source>
+ <translation>به حداقل رساندن</translation>
+ </message>
+ <message>
<source>Zoom</source>
<translation>بزرگنمایی</translation>
</message>
@@ -495,6 +559,10 @@
<translation>خطا: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>هشدار: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>تاریخ: %1
@@ -539,6 +607,18 @@
<translation>تراکنش دریافتی</translation>
</message>
<message>
+ <source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
+ <translation>تولید کلید HD &lt;b&gt;فعال است&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>تولید کلید HD &lt;b&gt; غیر فعال است&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>کلید خصوصی &lt;b&gt;غیر فعال &lt;/b&gt;</translation>
+ </message>
+ <message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<translation>wallet رمزگذاری شد و در حال حاضر از حالت قفل در آمده است</translation>
</message>
@@ -682,6 +762,10 @@
<translation>خیر</translation>
</message>
<message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>اگر هر گیرنده مقداری کمتر آستانه فعلی دریافت کند از این لیبل قرمز می‌شود.</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(برچسب ندارد)</translation>
</message>
@@ -696,10 +780,58 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>در حال ایجاد کیف پول &lt;b&gt; %1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>کیف پول ایجاد نگردید</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>هشدار ایجاد کیف پول</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>ایجاد کیف پول</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>نام کیف پول</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>کیف پول را رمز نگاری نمائید. کیف پول با کلمات رمز انتخاب خودتان رمز نگاری خواهد شد</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>رمز نگاری کیف پول</translation>
+ </message>
+ <message>
+ <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>غیر فعال کردن کلیدهای خصوصی برای این کیف پول. کیف پول هایی با کلید های خصوصی غیر فعال هیچ کلید خصوصی نداشته و نمیتوانند HD داشته باشند و یا کلید های خصوصی دارد شدنی داشته باشند. این کیف پول ها صرفاً برای رصد مناسب هستند.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>غیر فعال کردن کلیدهای خصوصی</translation>
+ </message>
+ <message>
+ <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>یک کیف پول خالی درست کنید. کیف پول های خالی در ابتدا کلید یا اسکریپت خصوصی ندارند. کلیدها و آدرسهای خصوصی می توانند وارد شوند یا بذر HD را می توان بعداً تنظیم نمود.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>ساخت کیف پول خالی</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>ایجاد</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -711,6 +843,14 @@
<translation>برچسب</translation>
</message>
<message>
+ <source>The label associated with this address list entry</source>
+ <translation>برچسب مرتبط با لیست آدرس ورودی</translation>
+ </message>
+ <message>
+ <source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
+ <translation>برچسب مرتبط با لیست آدرس ورودی می باشد. این می تواند فقط برای آدرس های ارسالی اصلاح شود.</translation>
+ </message>
+ <message>
<source>&amp;Address</source>
<translation>آدرس</translation>
</message>
@@ -881,6 +1021,10 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet warning</source>
+ <translation>هشدار باز کردن کیف پول</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>کیف پول پیش‌فرض</translation>
</message>
@@ -929,6 +1073,10 @@
<translation>گیگابایت</translation>
</message>
<message>
+ <source>MiB</source>
+ <translation>MiB</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
<translation>کیف پول</translation>
</message>
@@ -1166,6 +1314,10 @@
<context>
<name>PeerTableModel</name>
<message>
+ <source>User Agent</source>
+ <translation>نماینده کاربر</translation>
+ </message>
+ <message>
<source>Node/Service</source>
<translation>گره/خدمت</translation>
</message>
@@ -1277,6 +1429,10 @@
<translation>خطا: %1</translation>
</message>
<message>
+ <source>%1 didn't yet exit safely...</source>
+ <translation>%1 به درستی بسته نشد</translation>
+ </message>
+ <message>
<source>unknown</source>
<translation>ناشناس</translation>
</message>
@@ -1300,6 +1456,10 @@
<translation>خطا در تبدیل نشانی اینترنتی به صورت کد QR.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>پستیبانی از QR کد در دسترس نیست.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>ذحیره کردن Qr Code</translation>
</message>
@@ -1327,10 +1487,18 @@
<translation>عمومی</translation>
</message>
<message>
+ <source>Using BerkeleyDB version</source>
+ <translation>استفاده از نسخه پایگاه‌داده برکلی</translation>
+ </message>
+ <message>
<source>Datadir</source>
<translation>پوشه داده Datadir</translation>
</message>
<message>
+ <source>Blocksdir</source>
+ <translation>فولدر بلاکها</translation>
+ </message>
+ <message>
<source>Startup time</source>
<translation>زمان آغاز به کار</translation>
</message>
@@ -1411,10 +1579,22 @@
<translation>نسخه</translation>
</message>
<message>
+ <source>Starting Block</source>
+ <translation>بلاک اولیه</translation>
+ </message>
+ <message>
<source>Synced Blocks</source>
<translation>بلاک‌های همگام‌سازی‌ شده</translation>
</message>
<message>
+ <source>User Agent</source>
+ <translation>نماینده کاربر</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>پنجره گره</translation>
+ </message>
+ <message>
<source>Decrease font size</source>
<translation>کاهش دادن اندازه فونت</translation>
</message>
@@ -1879,6 +2059,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>تایید کردن ارسال کوین ها</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>PSBT کپی شد</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>آدرس گیرنده نامعتبر است.لطفا دوباره چک یا بررسی کنید.</translation>
</message>
@@ -2408,6 +2592,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>حداقل میزان وجه</translation>
</message>
<message>
+ <source>Abandon transaction</source>
+ <translation>تراکنش را رها نمائید.</translation>
+ </message>
+ <message>
<source>Increase transaction fee</source>
<translation>افزایش کارمزد تراکنش</translation>
</message>
@@ -2428,6 +2616,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>کپی شناسه تراکنش</translation>
</message>
<message>
+ <source>Copy raw transaction</source>
+ <translation>معامله اولیه را کپی نمائید.</translation>
+ </message>
+ <message>
<source>Copy full transaction details</source>
<translation>کپی کردن تمامی اطلاعات تراکنش</translation>
</message>
@@ -2452,6 +2644,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>تایید شده</translation>
</message>
<message>
+ <source>Watch-only</source>
+ <translation>رصد</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>تاریخ</translation>
</message>
@@ -2493,6 +2689,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</context>
<context>
<name>WalletController</name>
+ <message>
+ <source>Close wallet</source>
+ <translation>کیف پول را ببندید</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>آیا برای بستن کیف پول مطمئن هستید&lt;i&gt; %1 &lt;/i&gt; ؟</translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
@@ -2528,6 +2732,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>کارمزد جدید:</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>PSBT کپی شد</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>نمیتوان تراکنش را ثبت کرد</translation>
</message>
diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts
index 28b455d13c..5af9f5f055 100644
--- a/src/qt/locale/bitcoin_fi.ts
+++ b/src/qt/locale/bitcoin_fi.ts
@@ -482,6 +482,14 @@
<translation>Rahansiirtohistoria on ajan tasalla</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Solmu ikkuna</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Avaa solmun diagnostiikka- ja vianmäärityskonsoli </translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Lähetysosoitteet</translation>
</message>
@@ -490,6 +498,10 @@
<translation>&amp;Vastaanotto-osoitteet</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Avaa bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Avaa lompakko</translation>
</message>
@@ -783,7 +795,11 @@
<source>Create wallet failed</source>
<translation>Lompakon luonti epäonnistui</translation>
</message>
- </context>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Luo lompakkovaroitus</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
<message>
@@ -803,6 +819,10 @@
<translation>Salaa lompakko</translation>
</message>
<message>
+ <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>Poista tämän lompakon yksityiset avaimet käytöstä. Lompakot, joissa yksityiset avaimet on poistettu käytöstä, eivät sisällä yksityisiä avaimia, eikä niissä voi olla HD-juurisanoja tai tuotuja yksityisiä avaimia. Tämä on ihanteellinen katselulompakkoihin.</translation>
+ </message>
+ <message>
<source>Disable Private Keys</source>
<translation>Poista yksityisavaimet käytöstä</translation>
</message>
@@ -1042,6 +1062,14 @@
<translation>Piilota</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Poistu</translation>
+ </message>
+ <message>
+ <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>%1 synkronoidaan parhaillaan. Se lataa tunnisteet ja lohkot vertaisilta ja vahvistaa ne, kunnes ne saavuttavat lohkon ketjun kärjen.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Tuntematon. Synkronoidaan tunnisteita (%1, %2%)...</translation>
</message>
@@ -1049,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Avaa bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1060,6 +1092,10 @@
<translation>Lompakon avaaminen epäonnistui</translation>
</message>
<message>
+ <source>Open wallet warning</source>
+ <translation>Avoimen lompakon varoitus</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>oletuslompakko</translation>
</message>
@@ -1437,6 +1473,14 @@
<translation>'bitcoin://' ei ole kelvollinen URI. Käytä 'bitcoin:' sen sijaan.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Maksupyyntöä ei voida käsitellä, koska BIP70:tä ei tueta.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>BIP70:n laajalle levinneiden tietoturvavirheiden vuoksi on erittäin suositeltavaa, että kaikki kauppiaan ohjeet lompakkojen vaihtamiseksi jätetään huomioimatta.</translation>
+ </message>
+ <message>
<source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
<translation>Tämän virheen saadessasi tulee sinun pyytää kauppiaalta BIP21 -yhteensopivaa URI-osoitetta.</translation>
</message>
@@ -1745,10 +1789,22 @@
<translation>Synkronoidut lohkot</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Kartoitettu autonominen järjestelmä, jota käytetään monipuolistamaan solmuvalikoimaa</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Kartoitettu AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Käyttöliittymä</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Solmun näkymä</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Avaa %1 -debug-loki tämänhetkisestä data-hakemistosta. Tämä voi viedä muutaman sekunnin suurille lokitiedostoille.</translation>
</message>
@@ -1960,6 +2016,18 @@
<translation>Valinnainen pyyntömäärä. Jätä tyhjäksi tai nollaksi jos et pyydä tiettyä määrää.</translation>
</message>
<message>
+ <source>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>
+ <translation>Valinnainen tarra, joka liitetään uuteen vastaanotto-osoitteeseen (jonka käytät laskun tunnistamiseen). Se liitetään myös maksupyyntöön.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Valinnainen viesti, joka on liitetty maksupyyntöön ja joka voidaan näyttää lähettäjälle.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation> &amp;Luo uusi vastaanotto-osoite</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>Tyhjennä lomakkeen kaikki kentät.</translation>
</message>
@@ -2209,6 +2277,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Tomu:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation> Piilota siirtomaksuasetukset</translation>
+ </message>
+ <message>
<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>Mikäli lohkoissa ei ole tilaa kaikille siirtotapahtumille, voi louhijat sekä välittävät solmut pakottaa vähimmäispalkkion. Tämän vähimmäispalkkion maksaminen on täysin OK, mutta huomaa, että se saattaa johtaa siihen, ettei siirto vahvistu koskaan, jos bitcoin-siirtoja on enemmän kuin mitä verkko pystyy käsittelemään.</translation>
</message>
@@ -2277,6 +2349,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>%1 (%2 lohkoa)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>L&amp;uo allekirjoittamaton</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Luo osittain allekirjoitetun bitcoin-siirtotapahtuman (PSBT) käytettäväksi mm. offline %1 lompakko tai PSBT-yhteensopiva hardware-lompakko.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation> lompakosta '%1'</translation>
</message>
@@ -2285,10 +2365,18 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>%1 to %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Haluatko laatia tämän siirron?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Oletko varma, että haluat lähettää?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Ole hyvä ja katso siirtotapahtuma ehdotuksesi. Tämä tuottaa osittain allekirjoitetun bitcoin-siirtotapahtuman (PSBT), jonka voit kopioida ja sitten allekirjoittaa esimerkiksi offline %1 lompakko tai PSBT-yhteensopiva hardware-lompakko.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>tai</translation>
</message>
@@ -2305,6 +2393,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Siirtokulu</translation>
</message>
<message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Ei signalointia Korvattavissa korkeammalla kululla, BIP-125.</translation>
+ </message>
+ <message>
<source>Total Amount</source>
<translation>Yhteensä</translation>
</message>
@@ -2317,6 +2409,26 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Vahvista kolikoiden lähetys</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Vahvista siirtoehdotus</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Kopioi PSBT leikepöydälle</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Lähetä</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopioitu</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Katselulompakon saldo:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Vastaanottajan osoite ei ole kelvollinen. Tarkista osoite.</translation>
</message>
@@ -2412,6 +2524,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Poista tämä alkio</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Lähetettävä summa valitussa yksikössä</translation>
+ </message>
+ <message>
<source>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>
<translation>Kulu vähennetään lähetettävästä määrästä. Saaja vastaanottaa vähemmän bitcoineja kuin merkitset Määrä-kenttään. Jos saajia on monia, kulu jaetaan tasan.</translation>
</message>
@@ -2538,6 +2654,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Bitcoin-osoite jolla viesti on allekirjoitettu</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Allekirjoitettu viesti vahvistettavaksi</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Viestin allekirjoittamisen yhteydessä annettu allekirjoitus</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Tarkista viestin allekirjoitus varmistaaksesi, että se allekirjoitettiin tietyllä Bitcoin-osoitteella</translation>
</message>
@@ -2570,6 +2694,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Lompakon avaaminen peruttiin.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Ei virhettä</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Yksityistä avainta syötetylle osoitteelle ei ole saatavilla.</translation>
</message>
@@ -2740,6 +2868,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Tapahtuman näennäiskoko</translation>
</message>
<message>
+ <source>Output index</source>
+ <translation>Ulostulon indeksi</translation>
+ </message>
+ <message>
+ <source> (Certificate was not verified)</source>
+ <translation> (Sertifikaattia ei vahvistettu)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Kauppias</translation>
</message>
@@ -3093,6 +3229,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Haluatko nostaa siirtomaksua?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Haluatko nostaa siirtomaksua siirtoon?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Nykyinen palkkio:</translation>
</message>
@@ -3109,6 +3249,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Vahvista palkkion korotus</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation> Siirtoa ei voida laatia.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopioitu</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Siirtoa ei voida allekirjoittaa.</translation>
</message>
@@ -3199,6 +3347,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Ei voida lukita data-hakemistoa %s. %s on luultavasti jo käynnissä.</translation>
</message>
<message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Ei voida tarjota tiettyjä yhteyksiä, ja antaa addrmanin löytää lähteviä yhteyksiä samanaikaisesti.</translation>
+ </message>
+ <message>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
<translation>Virhe luettaessa %s! Avaimet luetttiin oikein, mutta rahansiirtotiedot tai osoitekirjan sisältö saattavat olla puutteellisia tai vääriä.</translation>
</message>
@@ -3255,6 +3407,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>-%s -osoitteen '%s' selvittäminen epäonnistui</translation>
</message>
<message>
+ <source>Change index out of range</source>
+ <translation>Vaihda hakemisto alueen ulkopuolelle</translation>
+ </message>
+ <message>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation>Konfigurointiasetuksen %s käyttöön vain %s -verkossa, kun osassa [%s].</translation>
+ </message>
+ <message>
<source>Copyright (C) %i-%i</source>
<translation>Tekijänoikeus (C) %i-%i</translation>
</message>
@@ -3263,6 +3423,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Vioittunut lohkotietokanta havaittu</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Asmap-tiedostoa %s ei löytynyt</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Asmap-tiedostoa %s ei voitu jäsentää</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Haluatko uudelleenrakentaa lohkotietokannan nyt?</translation>
</message>
@@ -3279,6 +3447,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Virhe ladattaessa %s</translation>
</message>
<message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Virhe %s:n lataamisessa: Yksityiset avaimet voidaan poistaa käytöstä vain luomisen aikana</translation>
+ </message>
+ <message>
<source>Error loading %s: Wallet corrupted</source>
<translation>Virhe ladattaessa %s: Lompakko vioittunut</translation>
</message>
@@ -3331,6 +3503,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Määrättyä lohkohakemistoa "%s" ei ole olemassa.</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Tuntematon osoitetyyppi '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Tuntematon vaihtorahatyyppi '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Päivitetään txindex -tietokantaa</translation>
</message>
@@ -3407,6 +3587,10 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Virhe: Saapuvien yhteyksien kuuntelu epäonnistui (kuuntelu palautti virheen %s)</translation>
</message>
<message>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation>Virheellinen summa -maxtxfee =: '%s' (täytyy olla vähintään %s minrelay-kulu, jotta estetään jumiutuneet siirtotapahtumat)</translation>
+ </message>
+ <message>
<source>The transaction amount is too small to send after the fee has been deducted</source>
<translation>Siirtomäärä on liian pieni lähetettäväksi kulun vähentämisen jälkeen.</translation>
</message>
@@ -3501,6 +3685,14 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>PID-tiedostoa '%s' ei voitu luoda: %s</translation>
</message>
<message>
+ <source>Unable to generate initial keys</source>
+ <translation>Alkuavaimia ei voi luoda</translation>
+ </message>
+ <message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Tuntematon -lohkosuodatusindeksiarvo %s.</translation>
+ </message>
+ <message>
<source>Verifying wallet(s)...</source>
<translation>Varmistetaan lompakko(ja)...</translation>
</message>
@@ -3550,7 +3742,7 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
</message>
<message>
<source>This is the transaction fee you will pay if you send a transaction.</source>
- <translation>Tämä on lähetyksestä maksettava maksu jonka maksat</translation>
+ <translation>Tämä on se siirtomaksu, jonka maksat, mikäli lähetät siirron.</translation>
</message>
<message>
<source>Transaction amounts must not be negative</source>
@@ -3573,10 +3765,18 @@ Huom: Koska siirtomaksu lasketaan tavujen mukaan, niin määrittelemällä 500 t
<translation>Lompakon saldo ei riitä</translation>
</message>
<message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Muuta kuin HD-jaettua lompakkoa ei voi päivittää ilman päivitystä tukemaan esijaettua avainvarastoa. Käytä -upgradewallet = 169900 tai -upgradewallet ilman määritettyä versiota.</translation>
+ </message>
+ <message>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
<translation>Siirtomaksun arviointi epäonnistui. Odota muutama lohko tai käytä -fallbackfee -valintaa..</translation>
</message>
<message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Varoitus: lompakosta {%s} tunnistetut yksityiset avaimet, on poistettu käytöstä</translation>
+ </message>
+ <message>
<source>Cannot write to data directory '%s'; check permissions.</source>
<translation>Hakemistoon '%s' ei voida kirjoittaa. Tarkista käyttöoikeudet.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts
index 1cddb413ef..e7066131b6 100644
--- a/src/qt/locale/bitcoin_fr.ts
+++ b/src/qt/locale/bitcoin_fr.ts
@@ -1789,6 +1789,14 @@
<translation>Blocs synchronisés</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Le système autonome mappé utilisé pour diversifier la sélection des pairs.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>SA mappé</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Agent utilisateur</translation>
</message>
diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_gl_ES.ts
index 1677446993..20b9fb1bbd 100644
--- a/src/qt/locale/bitcoin_tr.ts
+++ b/src/qt/locale/bitcoin_gl_ES.ts
@@ -1,541 +1,541 @@
-<TS language="tr" version="2.1">
+<TS language="gl_ES" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Adresi veya etiketi düzenlemek için sağ tıklayın</translation>
+ <translation>Fai Click co botón dereito para editar o enderezo ou etiqueta</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>Yeni adres oluştur</translation>
+ <translation>Crea un novo enderezo</translation>
</message>
<message>
<source>&amp;New</source>
- <translation>&amp;Yeni</translation>
+ <translation>&amp;Novo</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>Seçili adresi panoya kopyala</translation>
+ <translation>Copia o enderezo seleccionado ao portapapeis do sistema</translation>
</message>
<message>
<source>&amp;Copy</source>
- <translation>&amp;Kopyala</translation>
+ <translation>&amp;Copiar</translation>
</message>
<message>
<source>C&amp;lose</source>
- <translation>K&amp;apat</translation>
+ <translation>Pechar</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>Seçili adresi listeden sil</translation>
+ <translation>Borra o enderezo seleccionado actualmente da lista</translation>
</message>
<message>
<source>Enter address or label to search</source>
- <translation>Aramak için adres veya etiket girin</translation>
+ <translation>Introduce un enderezo ou etiqueta para buscar</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Seçili sekmedeki veriyi dosya olarak dışa aktar</translation>
+ <translation>Exporta os datos na pestana actual a un ficheiro</translation>
</message>
<message>
<source>&amp;Export</source>
- <translation>&amp;Dışa Aktar</translation>
+ <translation>Exportar</translation>
</message>
<message>
<source>&amp;Delete</source>
- <translation>&amp;Sil</translation>
+ <translation>Borrar</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>koinlerin gönderileceği adresi seçin</translation>
+ <translation>Selecciona o enderezo ó que enviar moedas</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>Alıcı adresi seçiniz</translation>
+ <translation>Selecciona o enderezo do que recibir moedas</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>Seçim</translation>
+ <translation>Selecciona</translation>
</message>
<message>
<source>Sending addresses</source>
- <translation>Gönderilen Adresler</translation>
+ <translation>Enderezos de envío</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>Alınan Adresler</translation>
+ <translation>Enderezos de recepción</translation>
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>Bunlar ödeme göndermek için gereken Bitcoin adreslerinizdir. Para göndermeden önce her zaman miktarı ve alıcı adresi kontrol edin.</translation>
+ <translation>Estes son os teus enderezos de Bitcoin para enviar pagamentos. Asegurate sempre de comprobar a cantidade e maila dirección antes de enviar moedas.</translation>
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
- <translation>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</translation>
+ <translation>Estes son os teus enderezos para recibir pagamentos. Usa o botón 'Crear novo enderezo de recepción' na pestana de recibir para crear un novo enderezo.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>Adresi Kopyala</translation>
+ <translation>Copiar Enderezo</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>Kopyala ve Etiketle</translation>
+ <translation>Copia Etiqueta</translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>Düzenle</translation>
+ <translation>Edita</translation>
</message>
<message>
<source>Export Address List</source>
- <translation>Adres Listesini Dışar Aktar</translation>
+ <translation>Exporta a Lista de Enderezos</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>Virgül ile ayrılmış dosya (*.csv)</translation>
+ <translation>Ficheiro Separado por Comas (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
- <translation>Dışa Aktarma Başarısız</translation>
+ <translation>Exportación Fallida</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
- <translation>Adres listesini %1'e kaydederken bir hata oluştu. Lütfen tekrar deneyin.</translation>
+ <translation>Houbo un erro tentando gardar a lista de enderezos en %1. Por favor proba de novo.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
- <translation>etiket</translation>
+ <translation>Etiqueta</translation>
</message>
<message>
<source>Address</source>
- <translation>adres</translation>
+ <translation>Enderezo</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(etiket yok)</translation>
+ <translation>(sin etiqueta)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
- <translation>Parola Diyaloğu</translation>
+ <translation>Diálogo de Frase Contrasinal</translation>
</message>
<message>
<source>Enter passphrase</source>
- <translation>Parolayı girin</translation>
+ <translation>Introduce a frase contrasinal</translation>
</message>
<message>
<source>New passphrase</source>
- <translation>Yeni parola</translation>
+ <translation>Nova frase contrasinal</translation>
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>Yeni parolayı tekrarla</translation>
+ <translation>Repite a frase contrasinal</translation>
</message>
<message>
<source>Show passphrase</source>
- <translation>Show passphrase</translation>
+ <translation>Mostra frase contrasinal</translation>
</message>
<message>
<source>Encrypt wallet</source>
- <translation>Cüzdanı Şifrele</translation>
+ <translation>Encriptar carteira</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>Bu işlem, cüzdan kilidinizi açmak için parolanıza ihtiyaç duyuyor</translation>
+ <translation>Esta operación necesita da túa frase contrasinal para desbloqueares a carteira.</translation>
</message>
<message>
<source>Unlock wallet</source>
- <translation>Cüzdanı Kilitle</translation>
+ <translation>Desbloquear carteira</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
- <translation>Bu işlem, cüzdan kilidinizi açmak için parolanıza ihtiyaç duyuyor</translation>
+ <translation>Esta operación necesita da túa frase contrasinal para desbloquea a carteira.</translation>
</message>
<message>
<source>Decrypt wallet</source>
- <translation>Cüzdanın Şifresini Çöz</translation>
+ <translation>Desencriptar carteira</translation>
</message>
<message>
<source>Change passphrase</source>
- <translation>Parolayı değiştir</translation>
+ <translation>Cambiar frase contrasinal</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
- <translation>Cüzdan Şifrelemesini Onaylayın</translation>
+ <translation>Confirmar encriptación da carteira</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
- <translation>Uyarı: Eğer cüzdanınızı şifreleyip parolanızı kaybederseniz (unutursanız) , &lt;b&gt;BÜTÜN BITCOIN'LERINIZI KAYBEDECEKSINIZ&lt;/b&gt;!</translation>
+ <translation>Aviso: Si encriptas a túa carteira e perdes a túa frase contrasinal, &lt;b&gt;PERDERÁS TODOS OS TEUS BITCOINS&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
- <translation>Cüzdanınızı şifrelemek istediğinizden emin misiniz?</translation>
+ <translation>¿Seguro que queres encriptar a túa carteira?</translation>
</message>
<message>
<source>Wallet encrypted</source>
- <translation>Cüzdan Şifrelendi</translation>
+ <translation>Carteira encriptada</translation>
</message>
<message>
<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>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;.</translation>
+ <translation>Introduce unha nova frase contrasinal para a carteira.&lt;br/&gt;Por favor utiliza una frase contrasinal que &lt;b&gt;teña dez ou máis caracteres aleatorios&lt;/b&gt;, ou &lt;b&gt;oito ou máis palabras&lt;/b&gt;.</translation>
</message>
<message>
<source>Enter the old passphrase and new passphrase for the wallet.</source>
- <translation>Enter the old passphrase and new passphrase for the wallet.</translation>
+ <translation>Introduce a frase contrasinal anterior mais a nova frase contrasinal para a carteira.</translation>
</message>
<message>
<source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
- <translation>Cüzdanınızı şifrelemenin, bitcoinlerinizin bilgisayara bulaşan kötücül bir yazılım tarafından çalınmaya karşı tamamen koruyamayacağını unutmayınız.</translation>
+ <translation>Recorda que encriptar a tua carteira non protexe completamente que os teus bitcoins poidan ser roubados por malware que afecte ó teu computador.</translation>
</message>
<message>
<source>Wallet to be encrypted</source>
- <translation>Wallet to be encrypted</translation>
+ <translation>Carteira para ser encriptada</translation>
</message>
<message>
<source>Your wallet is about to be encrypted. </source>
- <translation>Your wallet is about to be encrypted. </translation>
+ <translation>A túa carteira vai a ser encriptada.</translation>
</message>
<message>
<source>Your wallet is now encrypted. </source>
- <translation>Your wallet is now encrypted. </translation>
+ <translation>A túa carteira está agora encriptada.</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
- <translation>ÖNEMLİ: Yeni oluşturduğunuz şifrelenmiş cüzdan dosyasını önceki yedeklenmiş cüzdan dosyasıyla değiştirmeniz gerekmektedir. Güvenlik sebeplerinden dolayı yeni, şifrelenmiş cüzdanınızı kullanmaya başlar başlamaz önceki şifrelenmemiş cüzdan yedekleri kullanılmaz hale gelecektir.</translation>
+ <translation>IMPORTANTE: Calquera copia de respaldo que tiveras feita da túa carteira debería ser sustituída por unha nova copia xerada a partires da túa nova carteira encriptada. Por razóns de seguridade, as copias de respaldo da túa carteira sin encriptar non se poderán usar unha vez que empeces a utilizar a túa nova carteira encriptada.</translation>
</message>
<message>
<source>Wallet encryption failed</source>
- <translation>Cüzdan şifreleme başarısız oldu</translation>
+ <translation>Error na Encriptación da carteira </translation>
</message>
<message>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
- <translation>Cüzdan şifreleme dahili bir hata nedeniyle başarısız oldu. Cüzdanınız şifrelenemedi.</translation>
+ <translation>A encriptación da carteira fallou debido a un erro interno. A túa carteira no foi encriptada.</translation>
</message>
<message>
<source>The supplied passphrases do not match.</source>
- <translation>Girilen parolalar eşleşmiyor.</translation>
+ <translation>As frases contrasinal introducidas non coinciden.</translation>
</message>
<message>
<source>Wallet unlock failed</source>
- <translation>Cüzdan Kilidi Açma Hatası</translation>
+ <translation>Desbloqueo de carteira fallido</translation>
</message>
<message>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
- <translation>Cüzdan şifresinin açılması için girilen parola yanlıştı.</translation>
+ <translation>A frase contrasinal introducida para o desencriptamento da carteira é incorrecto.</translation>
</message>
<message>
<source>Wallet decryption failed</source>
- <translation>Cüzdan şifresinin açılması başarısız oldu</translation>
+ <translation>Fallou o desencriptado da carteira</translation>
</message>
<message>
<source>Wallet passphrase was successfully changed.</source>
- <translation>Cüzdan parolası başarılı bir şekilde değiştirildi.</translation>
+ <translation>A frase contrasinal da carteira mudouse correctamente.</translation>
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
- <translation>Dikkat! Caps Lock tuşunuz açık!</translation>
+ <translation>Aviso: ¡A tecla Bloq. Mayús está activada!</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
<message>
<source>IP/Netmask</source>
- <translation>IP/Ağ Maskesi</translation>
+ <translation>IP/Máscara de rede</translation>
</message>
<message>
<source>Banned Until</source>
- <translation>Şu zamana kadar yasaklı:</translation>
+ <translation>Vedado ata</translation>
</message>
</context>
<context>
<name>BitcoinGUI</name>
<message>
<source>Sign &amp;message...</source>
- <translation>İmza &amp;mesaj</translation>
+ <translation>Firma &amp;a mensaxe...</translation>
</message>
<message>
<source>Synchronizing with network...</source>
- <translation>Ağ ile bağlantı kuruluyor...</translation>
+ <translation>Sincronizando ca rede...</translation>
</message>
<message>
<source>&amp;Overview</source>
- <translation>&amp;Genel bakış</translation>
+ <translation>&amp;visión xeral</translation>
</message>
<message>
<source>Show general overview of wallet</source>
- <translation>Cüzdana genel bakışı göster</translation>
+ <translation>Mostra una visión xeral da carteira</translation>
</message>
<message>
<source>&amp;Transactions</source>
- <translation>&amp;‮‮‭İşlemler</translation>
+ <translation>&amp;Transaccións</translation>
</message>
<message>
<source>Browse transaction history</source>
- <translation>İşlem geçmişinize göz atın</translation>
+ <translation>Busca no historial de transaccións</translation>
</message>
<message>
<source>E&amp;xit</source>
- <translation>Çıkış</translation>
+ <translation>S&amp;aír</translation>
</message>
<message>
<source>Quit application</source>
- <translation>Başvuruyu iptal edin</translation>
+ <translation>Saír da aplicación</translation>
</message>
<message>
<source>&amp;About %1</source>
- <translation>Hakkında%1</translation>
+ <translation>&amp;A cerca de %1</translation>
</message>
<message>
<source>Show information about %1</source>
- <translation>%1 hakkındaki bilgileri görüntüle</translation>
+ <translation>Mostra información acerca de %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
- <translation>Qt Hakkında</translation>
+ <translation>Acerca de &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
- <translation>Qt hakkındaki bilgileri görüntüleyin</translation>
+ <translation>Mostra información acerca de Qt</translation>
</message>
<message>
<source>&amp;Options...</source>
- <translation>&amp;Seçenekler</translation>
+ <translation>&amp;Opcións...</translation>
</message>
<message>
<source>Modify configuration options for %1</source>
- <translation>%1 için yapılandırma ayarlarını değiştir</translation>
+ <translation>Modifica as opcións de configuración de %1</translation>
</message>
<message>
<source>&amp;Encrypt Wallet...</source>
- <translation>&amp;Cüzdan Şifreleme</translation>
+ <translation>&amp;Encriptar Carteira...</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
- <translation>&amp;Cüzdan Yedekleme</translation>
+ <translation>&amp;Respaldar Carteira...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
- <translation>&amp;Parolayı Değiştir...</translation>
+ <translation>&amp;Mudar frase contrasinal...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
- <translation>URI'yi aç</translation>
+ <translation>Abrir &amp;URI...</translation>
</message>
<message>
<source>Create Wallet...</source>
- <translation>Create Wallet...</translation>
+ <translation>Crear Carteira...</translation>
</message>
<message>
<source>Create a new wallet</source>
- <translation>Create a new wallet</translation>
+ <translation>Crear unha nova carteira</translation>
</message>
<message>
<source>Wallet:</source>
- <translation>Cüzdan:</translation>
+ <translation>Carteira:</translation>
</message>
<message>
<source>Click to disable network activity.</source>
- <translation>Ağ etkinliğini devre dışı bırakmak için tıklayın.</translation>
+ <translation>Fai click para desactivar a actividade da rede.</translation>
</message>
<message>
<source>Network activity disabled.</source>
- <translation>Ağ etkinliği devre dışı.</translation>
+ <translation>Actividade da rede desactivada.</translation>
</message>
<message>
<source>Click to enable network activity again.</source>
- <translation>Ağ aktivitesini tekrar başlatmak için tıklayın.</translation>
+ <translation>Fai click para activar a activade da red de novo.</translation>
</message>
<message>
<source>Syncing Headers (%1%)...</source>
- <translation>Üstbilgiler Senkronize Ediliyor (%1%)...</translation>
+ <translation>Sincronizando Cabeceiras (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>Bloklar disk üzerinde yeniden indeksleniyor...</translation>
+ <translation>Reindexando bloques en disco...</translation>
</message>
<message>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
- <translation>Tünelleme &lt;b&gt;etkin&lt;/b&gt;: %1</translation>
+ <translation>Proxy &lt;b&gt;activado&lt;/b&gt;: %1</translation>
</message>
<message>
<source>Send coins to a Bitcoin address</source>
- <translation>Bitcoin adresine madeni para gönderin</translation>
+ <translation>Envía moedas a un enderezo de Bitcoin</translation>
</message>
<message>
<source>Backup wallet to another location</source>
- <translation>Cüzdanınızı başka bir lokasyona yedekleyin</translation>
+ <translation>Respalda a carteira noutro destino</translation>
</message>
<message>
<source>Change the passphrase used for wallet encryption</source>
- <translation>Cüzdan şifrelemesi için kullanılan parolayı değiştir</translation>
+ <translation>Cambia a frase contrasinal usada para a encriptación da carteira</translation>
</message>
<message>
<source>&amp;Verify message...</source>
- <translation>Onay mesajı...</translation>
+ <translation>&amp;Verifica a mensaxe...</translation>
</message>
<message>
<source>&amp;Send</source>
- <translation>Gönder</translation>
+ <translation>&amp;Envía</translation>
</message>
<message>
<source>&amp;Receive</source>
- <translation>Al</translation>
+ <translation>&amp;Recibir</translation>
</message>
<message>
<source>&amp;Show / Hide</source>
- <translation>Göster / Gizle</translation>
+ <translation>&amp;Mostra / Agocha</translation>
</message>
<message>
<source>Show or hide the main Window</source>
- <translation>Ana pencereyi göster ya da gizle</translation>
+ <translation>Mostra ou agocha a xanela principal</translation>
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
- <translation>Cüzdanınıza ait özel anahtarları şifreleyin</translation>
+ <translation>Encripta as claves privadas que pertencen á túa carteira</translation>
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>İletileri adreslerin size ait olduğunu ispatlamak için Bitcoin adresleri ile imzala</translation>
+ <translation>Asina mensaxes cos teus enderezos de Bitcoin para probar que che pertencen</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
- <translation>Belirtilen Bitcoin adresleri ile imzalandıklarından emin olmak için iletileri kontrol et</translation>
+ <translation>Verifica mensaxes para asegurar que foron asinados cos enderezos de Bitcoin especificados</translation>
</message>
<message>
<source>&amp;File</source>
- <translation>&amp;Dosya</translation>
+ <translation>&amp;Arquivo</translation>
</message>
<message>
<source>&amp;Settings</source>
- <translation>&amp;Ayarlar</translation>
+ <translation>&amp;Opcións</translation>
</message>
<message>
<source>&amp;Help</source>
- <translation>&amp;Yardım</translation>
+ <translation>&amp;Axuda</translation>
</message>
<message>
<source>Tabs toolbar</source>
- <translation>Sekme araç çubuğu</translation>
+ <translation>Barra de ferramentas das pestanas</translation>
</message>
<message>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
- <translation>Ödeme talep et (QR kodu ve bitcoin URI'si oluşturur)</translation>
+ <translation>Solicita pagamentos (xera un código QR e bitocin : URIs)</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
- <translation>Kullanılan gönderim adreslerinin ve etiketlerinin listesini göster</translation>
+ <translation>Mostra a lista de enderezos de envío e etiquetas usadas</translation>
</message>
<message>
<source>Show the list of used receiving addresses and labels</source>
- <translation>Kullanılan alış adreslerinin ve etiketlerinin listesini göster</translation>
+ <translation>Mostra a lista de enderezos de recepción e etiquetas usadas</translation>
</message>
<message>
<source>&amp;Command-line options</source>
- <translation>Komut satırı ayarları</translation>
+ <translation>&amp;Opcións de comando</translation>
</message>
<message numerus="yes">
<source>%n active connection(s) to Bitcoin network</source>
- <translation><numerusform>Bitcoin şebekesine %n faal bağlantı</numerusform><numerusform>Bitcoin ağına %n etkin bağlantı var</numerusform></translation>
+ <translation><numerusform>%n active connection to Bitcoin network</numerusform><numerusform>%n Conexións activas cara a rede de Bitcoin</numerusform></translation>
</message>
<message>
<source>Indexing blocks on disk...</source>
- <translation>Bloklar disk üzerinde indeksleniyor...</translation>
+ <translation>Indexando bloques no disco...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
- <translation>Bloklar diske işleniyor...</translation>
+ <translation>Procesando bloques no disco...</translation>
</message>
<message numerus="yes">
<source>Processed %n block(s) of transaction history.</source>
- <translation><numerusform>Muamele tarihçesinden %n blok işlendi.</numerusform><numerusform>İşlem tarihçesinden %n blok işlendi</numerusform></translation>
+ <translation><numerusform>Processed %n block of transaction history.</numerusform><numerusform>Procesando %n bloques do historial de transaccións.</numerusform></translation>
</message>
<message>
<source>%1 behind</source>
- <translation>%1 geride</translation>
+ <translation>%1 tras</translation>
</message>
<message>
<source>Last received block was generated %1 ago.</source>
- <translation>Son alınan blok %1 önce oluşturulmuştu.</translation>
+ <translation>O último bloque recibido foi xerado fai %1.</translation>
</message>
<message>
<source>Transactions after this will not yet be visible.</source>
- <translation>Bundan sonraki işlemler henüz görüntülenemez.</translation>
+ <translation>Transaccións despois desta non serán aínda visibles.</translation>
</message>
<message>
<source>Error</source>
- <translation>Hata</translation>
+ <translation>Error</translation>
</message>
<message>
<source>Warning</source>
- <translation>Uyarı</translation>
+ <translation>Aviso</translation>
</message>
<message>
<source>Information</source>
- <translation>Bilgi</translation>
+ <translation>Información</translation>
</message>
<message>
<source>Up to date</source>
- <translation>Güncel</translation>
+ <translation>Actualizado</translation>
</message>
<message>
<source>Node window</source>
- <translation>Node window</translation>
+ <translation>Xanela de Nodo</translation>
</message>
<message>
<source>Open node debugging and diagnostic console</source>
- <translation>Open node debugging and diagnostic console</translation>
+ <translation>Abre a consola de depuración e diagnostico do nodo</translation>
</message>
<message>
<source>&amp;Sending addresses</source>
- <translation>&amp;Sending addresses</translation>
+ <translation>&amp;Enderezos de envío</translation>
</message>
<message>
<source>&amp;Receiving addresses</source>
- <translation>&amp;Receiving addresses</translation>
+ <translation>&amp;Enderezos de recepción</translation>
</message>
<message>
<source>Open a bitcoin: URI</source>
- <translation>Open a bitcoin: URI</translation>
+ <translation>Abre una URI de Bitcoin</translation>
</message>
<message>
<source>Open Wallet</source>
- <translation>Cüzdanı Aç</translation>
+ <translation>Abrir carteira</translation>
</message>
<message>
<source>Open a wallet</source>
- <translation>Bir cüzdan aç</translation>
+ <translation>Abrir unha carteira</translation>
</message>
<message>
<source>Close Wallet...</source>
- <translation>Cüzdanı Kapat...</translation>
+ <translation>Pechar carteira...</translation>
</message>
<message>
<source>Close wallet</source>
- <translation>Cüzdanı Kapat</translation>
+ <translation>Pechar carteira</translation>
</message>
<message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
- <translation>Olası Bitcoin komut satırı seçeneklerinin listesini görmek için %1 yardım mesajını göster</translation>
+ <translation>Mostra a %1 mensaxe de axuda para obter unha lista cas posibles opcións de línea de comando de Bitcoin </translation>
</message>
<message>
<source>default wallet</source>
- <translation>varsayılan cüzdan</translation>
+ <translation>Carteira por defecto</translation>
</message>
<message>
<source>No wallets available</source>
- <translation>No wallets available</translation>
+ <translation>Non hai carteiras dispoñibles</translation>
</message>
<message>
<source>&amp;Window</source>
- <translation>Pencere</translation>
+ <translation>&amp;Xanela</translation>
</message>
<message>
<source>Minimize</source>
- <translation>Küçült</translation>
+ <translation>Minimizar</translation>
</message>
<message>
<source>Zoom</source>
@@ -543,407 +543,412 @@
</message>
<message>
<source>Main Window</source>
- <translation>Main Window</translation>
+ <translation>Xanela Principal</translation>
</message>
<message>
<source>%1 client</source>
- <translation>%1 istemci</translation>
+ <translation>%1 cliente</translation>
</message>
<message>
<source>Connecting to peers...</source>
- <translation>Eşlere bağlanılıyor...</translation>
+ <translation>Connectando con compañeiros...</translation>
</message>
<message>
<source>Catching up...</source>
- <translation>Aralık kapatılıyor...</translation>
+ <translation>Poñéndose ao día...</translation>
</message>
<message>
<source>Error: %1</source>
- <translation>Hata: %1</translation>
+ <translation>Error: %1</translation>
</message>
<message>
<source>Warning: %1</source>
- <translation>Warning: %1</translation>
+ <translation>Aviso: %1</translation>
</message>
<message>
<source>Date: %1
</source>
- <translation>Tarih %1</translation>
+ <translation>Data: %1
+</translation>
</message>
<message>
<source>Amount: %1
</source>
- <translation>Tutar: %1
+ <translation>Cantidade: %1
</translation>
</message>
<message>
<source>Wallet: %1
</source>
- <translation>Cüzdan: %1
+ <translation>Carteira: %1
</translation>
</message>
<message>
<source>Type: %1
</source>
- <translation>Tür: %1
+ <translation>Escribe: %1
</translation>
</message>
<message>
<source>Label: %1
</source>
- <translation>Etiket: %1
+ <translation>Etiqueta: %1
</translation>
</message>
<message>
<source>Address: %1
</source>
- <translation>Adres: %1
+ <translation>Enderezo: %1
</translation>
</message>
<message>
<source>Sent transaction</source>
- <translation>İşlem gönderildi</translation>
+ <translation>Transacción enviada</translation>
</message>
<message>
<source>Incoming transaction</source>
- <translation>Gelen işlem</translation>
+ <translation>Transacción entrante</translation>
</message>
<message>
<source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
- <translation>HD anahtar üretimi&lt;b&gt;aktif&lt;/b&gt;</translation>
+ <translation>A xeración de clave HD está &lt;b&gt;activada&lt;/b&gt;</translation>
</message>
<message>
<source>HD key generation is &lt;b&gt;disabled&lt;/b&gt;</source>
- <translation>HD anahtar üretimi &lt;b&gt;pasif&lt;/b&gt;</translation>
+ <translation>A xeración de clave HD está &lt;b&gt;desactivada&lt;/b&gt;</translation>
</message>
<message>
<source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
- <translation>Private key &lt;b&gt;disabled&lt;/b&gt;</translation>
+ <translation>Clave privada &lt;b&gt;desactivada&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
- <translation>Cüzdan &lt;b&gt;şifrelenmiştir&lt;/b&gt; ve şu anda &lt;b&gt;kilidi açıktır&lt;/b&gt;</translation>
+ <translation>A carteira está &lt;b&gt;encrypted&lt;/b&gt; e actualmente &lt;b&gt;desbloqueada&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
- <translation>Cüzdan &lt;b&gt;şifrelenmiştir&lt;/b&gt; ve şu anda &lt;b&gt;kilitlidir&lt;/b&gt;</translation>
+ <translation>A carteira está &lt;b&gt;encriptada&lt;/b&gt; e actualmente &lt;b&gt;bloqueada&lt;/b&gt;</translation>
</message>
<message>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
- <translation>Ölümcül bir hata oluştu. Bitcoin yazılımı artık güvenli bir şekilde çalışmaya devam edemediği için kapatılacaktır.</translation>
+ <translation>Produciuse un error fatal. Bitcoin non pode seguir funcionando de maneira segura e sairá.</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Coin Selection</source>
- <translation>Bitcoin Seçimi</translation>
+ <translation>Selección de moeda</translation>
</message>
<message>
<source>Quantity:</source>
- <translation>Miktar:</translation>
+ <translation>Cantidade:</translation>
</message>
<message>
<source>Bytes:</source>
- <translation>Bayt</translation>
+ <translation>Bytes:</translation>
</message>
<message>
<source>Amount:</source>
- <translation>Tutar:</translation>
+ <translation>Cantidade:</translation>
</message>
<message>
<source>Fee:</source>
- <translation>Ücret:</translation>
+ <translation>taxa:</translation>
</message>
<message>
<source>Dust:</source>
- <translation>Toz:</translation>
+ <translation>po:</translation>
</message>
<message>
<source>After Fee:</source>
- <translation>Ücretten sonra kalan:</translation>
+ <translation>Despois de taxas:</translation>
</message>
<message>
<source>Change:</source>
- <translation>Değişen:</translation>
+ <translation>Cambio:</translation>
</message>
<message>
<source>(un)select all</source>
- <translation>tümünü seç(me)</translation>
+ <translation>(de)seleccionar todo</translation>
</message>
<message>
<source>Tree mode</source>
- <translation>Ağaç kipi</translation>
+ <translation>Modo en árbore</translation>
</message>
<message>
<source>List mode</source>
- <translation>Listeleme modu</translation>
+ <translation>Modo en Lista</translation>
</message>
<message>
<source>Amount</source>
- <translation>Tutar</translation>
+ <translation>Cantidade</translation>
</message>
<message>
<source>Received with label</source>
- <translation>Şu etiketle alındı</translation>
+ <translation>Recibida con etiqueta</translation>
</message>
<message>
<source>Received with address</source>
- <translation>Şu adresle alındı</translation>
+ <translation>Recibida con enderezo</translation>
</message>
<message>
<source>Date</source>
- <translation>Tarih</translation>
+ <translation>Data</translation>
</message>
<message>
<source>Confirmations</source>
- <translation>Onaylamalar</translation>
+ <translation>Confirmacións</translation>
</message>
<message>
<source>Confirmed</source>
- <translation>Kabul edilen</translation>
+ <translation>Confirmada</translation>
</message>
<message>
<source>Copy address</source>
- <translation>Adresi kopyala</translation>
+ <translation>Copiar enderezo</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Etiketi kopyala</translation>
+ <translation>Copiar etiqueta</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Tutarı kopyala</translation>
+ <translation>Copiar cantidade</translation>
</message>
<message>
<source>Copy transaction ID</source>
- <translation>İşlem ID'sini kopyala</translation>
+ <translation>Copiar ID da transacción</translation>
</message>
<message>
<source>Lock unspent</source>
- <translation>Harcanmamışı kilitle</translation>
+ <translation>Bloquear o non gastado</translation>
</message>
<message>
<source>Unlock unspent</source>
- <translation>Harcanmamışın kilidini aç</translation>
+ <translation>Desbloquear o non gastado</translation>
</message>
<message>
<source>Copy quantity</source>
- <translation>Miktarı kopyala</translation>
+ <translation>Copiar cantidade</translation>
</message>
<message>
<source>Copy fee</source>
- <translation>Ücreti kopyala</translation>
+ <translation>Copiar taxa</translation>
</message>
<message>
<source>Copy after fee</source>
- <translation>Ücretten sonrasını kopyala</translation>
+ <translation>Copiar despois de taxa</translation>
</message>
<message>
<source>Copy bytes</source>
- <translation>Baytları kopyala</translation>
+ <translation>Copiar bytes</translation>
</message>
<message>
<source>Copy dust</source>
- <translation>Tozu kopyala</translation>
+ <translation>Copiar po</translation>
</message>
<message>
<source>Copy change</source>
- <translation>Para üstünü kopyala</translation>
+ <translation>Copiar cambio</translation>
</message>
<message>
<source>(%1 locked)</source>
- <translation>(%1 kilitli)</translation>
+ <translation>(%1 bloqueado)</translation>
</message>
<message>
<source>yes</source>
- <translation>Evet</translation>
+ <translation>sí</translation>
</message>
<message>
<source>no</source>
- <translation>Hayır</translation>
+ <translation>no</translation>
</message>
<message>
<source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
- <translation>Eğer herhangi bir alıcı mevcut toz eşiğinden daha düşük bir tutar alırsa bu etiket kırmızıya dönüşür.</translation>
+ <translation>Esta etiqueta tórnase vermella se algún receptor recibe unha cantidade máis pequena que o actual límite de po.</translation>
</message>
<message>
<source>Can vary +/- %1 satoshi(s) per input.</source>
- <translation>Girdi başına +/- %1 satoshi değişebilir.</translation>
+ <translation>Pode variar +/- %1 satoshi(s) por entrada.</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(etiket yok)</translation>
+ <translation>(sen etiqueta)</translation>
</message>
<message>
<source>change from %1 (%2)</source>
- <translation>%1 ögesinden para üstü (%2)</translation>
+ <translation>Cambia de %1 a (%2)</translation>
</message>
<message>
<source>(change)</source>
- <translation>(para üstü)</translation>
+ <translation>(Cambia)</translation>
</message>
</context>
<context>
<name>CreateWalletActivity</name>
<message>
<source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
- <translation>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</translation>
+ <translation>Creando Carteira &lt;b&gt;%1&lt;/b&gt;...</translation>
</message>
<message>
<source>Create wallet failed</source>
- <translation>Create wallet failed</translation>
+ <translation>Creación de carteira fallida</translation>
</message>
<message>
<source>Create wallet warning</source>
- <translation>Create wallet warning</translation>
+ <translation>Creación de carteira con aviso</translation>
</message>
</context>
<context>
<name>CreateWalletDialog</name>
<message>
<source>Create Wallet</source>
- <translation>Create Wallet</translation>
+ <translation>Crea unha Carteira</translation>
</message>
<message>
<source>Wallet Name</source>
- <translation>Wallet Name</translation>
+ <translation>Nome da Carteira</translation>
</message>
<message>
<source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
- <translation>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</translation>
+ <translation>Encripta a carteira. A carteira sera encriptada cunha frase contrasinal que tú elixas.</translation>
</message>
<message>
<source>Encrypt Wallet</source>
- <translation>Encrypt Wallet</translation>
+ <translation>Encriptar Carteira</translation>
</message>
<message>
<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>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.</translation>
+ <translation>Desactiva as claves privadas para esta carteira. Carteiras con claves privadas desactivadas non terán claves privadas e polo tanto non poderan ter unha semente HD ou claves privadas importadas. Esto é ideal para carteiras de solo visualización.</translation>
</message>
<message>
<source>Disable Private Keys</source>
- <translation>Disable Private Keys</translation>
+ <translation>Desactivar Claves Privadas</translation>
</message>
<message>
<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>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.</translation>
+ <translation>Crear unha Carteira en blanco. As carteiras en blanco non teñen inicialmente claves privadas ou scripts. As claves privadas poden ser importadas ou unha semente HD poder ser configurada, máis adiante.</translation>
</message>
<message>
<source>Make Blank Wallet</source>
- <translation>Make Blank Wallet</translation>
+ <translation>Crea unha Carteira en Blanco</translation>
</message>
<message>
<source>Create</source>
- <translation>Create</translation>
+ <translation>Crea</translation>
</message>
</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
- <translation>Adresi Düzenle</translation>
+ <translation>Editar Enderezo</translation>
</message>
<message>
<source>&amp;Label</source>
- <translation>Etiket</translation>
+ <translation>&amp;Etiqueta</translation>
</message>
<message>
<source>The label associated with this address list entry</source>
- <translation>Bu adres listesi girdisi ile ilişkili etiket</translation>
+ <translation>A etiqueta asociada con esta entrada na lista de enderezos</translation>
</message>
<message>
<source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
- <translation>Bu adres listesi girdisi ile ilişkili adres. Sadece gönderme adresleri için değiştirilebilir.</translation>
+ <translation>O enderezo asociado con esta entrada na lista de enderezos. Solo pode ser modificado por enderezos de envío.</translation>
</message>
<message>
<source>&amp;Address</source>
- <translation>Adres</translation>
+ <translation>&amp;Enderezo</translation>
</message>
<message>
<source>New sending address</source>
- <translation>Yeni gönderim adresi</translation>
+ <translation>Novo enderezo de envío</translation>
</message>
<message>
<source>Edit receiving address</source>
- <translation>Alış adresini düzenleyin</translation>
+ <translation>Editar enderezo de recepción</translation>
</message>
<message>
<source>Edit sending address</source>
- <translation>Gönderim adresini düzenleyin</translation>
+ <translation>Editar enderezo de envío</translation>
</message>
<message>
<source>The entered address "%1" is not a valid Bitcoin address.</source>
- <translation>Girilen adres "%1" Bitcoin adresiyle eşleşmiyor.</translation>
+ <translation>O enderezo introducido "%1" non é un enderezo de Bitcoin válido.</translation>
</message>
<message>
<source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
- <translation>Adres "%1" adres "%2" etiketiyle alım adresiniz olarak mevcut ve bu sebepten gönderen adres olarak eklenemiyor.</translation>
+ <translation>O enderezo "%1" xa existe como un enderezo de recepción ca etiqueta "%2" polo que non pode ser añadido como un enderezo de envío.</translation>
</message>
<message>
<source>The entered address "%1" is already in the address book with label "%2".</source>
- <translation>girilen "%1" adresi "%2" etiketli adres defterinde zaten var.</translation>
+ <translation>O enderezo introducido "%1" xa existe na axenda de enderezos ca etiqueta "%2".</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
- <translation>Cüzdan kilidi açılamadı.</translation>
+ <translation>Non se puido desbloquear a carteira.</translation>
</message>
<message>
<source>New key generation failed.</source>
- <translation>Yeni anahtar üretimi başarısız.</translation>
+ <translation>New key generation failed.</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>Yeni bir veri klasörü oluşturulacaktır.</translation>
+ <translation>A new data directory will be created.</translation>
</message>
<message>
<source>name</source>
- <translation>isim</translation>
+ <translation>name</translation>
</message>
<message>
<source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
- <translation>Klasör zaten mevcuttur. Burada yeni bir klasör oluşturmak istiyorsanız, %1 ekleyiniz.</translation>
+ <translation>Directory already exists. Add %1 if you intend to create a new directory here.</translation>
</message>
<message>
<source>Path already exists, and is not a directory.</source>
- <translation>Erişim yolu zaten mevcuttur ve klasör değildir.</translation>
+ <translation>Path already exists, and is not a directory.</translation>
</message>
<message>
<source>Cannot create data directory here.</source>
- <translation>Burada veri klasörü oluşturulamaz.</translation>
+ <translation>Cannot create data directory here.</translation>
</message>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
- <translation>versiyon</translation>
+ <translation>version</translation>
</message>
<message>
<source>About %1</source>
- <translation>Hakkında %1</translation>
+ <translation>About %1</translation>
</message>
<message>
<source>Command-line options</source>
- <translation>Komut satırı ayarları</translation>
+ <translation>Command-line options</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Welcome</source>
- <translation>Hoş geldiniz</translation>
+ <translation>Welcome</translation>
</message>
<message>
<source>Welcome to %1.</source>
- <translation>%1'a hoşgeldiniz.</translation>
+ <translation>Welcome to %1.</translation>
</message>
<message>
<source>As this is the first time the program is launched, you can choose where %1 will store its data.</source>
- <translation>Bu programın ilk kez başlatılmasından dolayı %1 yazılımının verilerini nerede saklayacağını seçebilirsiniz.</translation>
+ <translation>As this is the first time the program is launched, you can choose where %1 will store its data.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>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.</translation>
</message>
<message>
<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>
@@ -951,24 +956,23 @@
</message>
<message>
<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>Bu başlangıç senkronizasyonu çok zorlayıcıdır ve bilgisayarınızdaki daha önce fark edilmemiş olan donanım sorunlarını ortaya çıkarabilir. %1'i her çalıştırdığınızda, kaldığı yerden devam edecektir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>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>
- <translation>Blok zinciri saklamayı sınırlamayı seçtiyseniz (budama), geçmiş veriler yine de indirilmeli ve işlenmelidir, ancak disk kullanımınızı düşük tutmak için daha sonra silinmelidir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Use the default data directory</source>
- <translation>Varsayılan veri klasörünü kullan</translation>
+ <translation>Use the default data directory</translation>
</message>
<message>
<source>Use a custom data directory:</source>
- <translation>Özel bir veri klasörü kullan:</translation>
+ <translation>Use a custom data directory:</translation>
</message>
<message>
<source>Bitcoin</source>
- <translation>Bitcoin
-</translation>
+ <translation>Bitcoin</translation>
</message>
<message>
<source>Discard blocks after verification, except most recent %1 GB (prune)</source>
@@ -976,35 +980,35 @@
</message>
<message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
- <translation>Bu dizinde en az %1 GB lık veri depolanacak ve zamanla büyüyecek.</translation>
+ <translation>At least %1 GB of data will be stored in this directory, and it will grow over time.</translation>
</message>
<message>
<source>Approximately %1 GB of data will be stored in this directory.</source>
- <translation>Yaklaşık %1 GB veri bu dizinde depolanacak.</translation>
+ <translation>Approximately %1 GB of data will be stored in this directory.</translation>
</message>
<message>
<source>%1 will download and store a copy of the Bitcoin block chain.</source>
- <translation>%1 lik Bitcoin blok zinciri nin bir kopyasını indirecek ve depolayacak.</translation>
+ <translation>%1 will download and store a copy of the Bitcoin block chain.</translation>
</message>
<message>
<source>The wallet will also be stored in this directory.</source>
- <translation>Cüzdan da bu dizinde depolanacaktır.</translation>
+ <translation>The wallet will also be stored in this directory.</translation>
</message>
<message>
<source>Error: Specified data directory "%1" cannot be created.</source>
- <translation>Hata: belirtilen "%1" veri klasörü oluşturulamaz.</translation>
+ <translation>Error: Specified data directory "%1" cannot be created.</translation>
</message>
<message>
<source>Error</source>
- <translation>Hata</translation>
+ <translation>Error</translation>
</message>
<message numerus="yes">
<source>%n GB of free space available</source>
- <translation><numerusform>%n GB boş alan mevcuttur</numerusform><numerusform>%n GB boş alan mevcuttur</numerusform></translation>
+ <translation><numerusform>%n GB of free space available</numerusform><numerusform>%n GB of free space available</numerusform></translation>
</message>
<message numerus="yes">
<source>(of %n GB needed)</source>
- <translation><numerusform>(gereken %n GB alandan)</numerusform><numerusform>(gereken %n GB alandan)</numerusform></translation>
+ <translation><numerusform>(of %n GB needed)</numerusform><numerusform>(of %n GB needed)</numerusform></translation>
</message>
<message numerus="yes">
<source>(%n GB needed for full chain)</source>
@@ -1019,43 +1023,43 @@
</message>
<message>
<source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
- <translation>Son işlemler henüz görünmeyebilir ve bu nedenle cüzdanınızın bakiyesi yanlış olabilir. Bu bilgiler, aşağıda detaylandırıldığı gibi, cüzdanınız bitcoin ağı ile senkronizasyonunu tamamladığında doğru olacaktır.</translation>
+ <translation>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</translation>
</message>
<message>
<source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
- <translation>Henüz görüntülenmeyen işlemlerden etkilenen bitcoinleri harcama girişiminde bulunmak ağ tarafından kabul edilmeyecektir.</translation>
+ <translation>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</translation>
</message>
<message>
<source>Number of blocks left</source>
- <translation>Kalan blokların sayısı</translation>
+ <translation>Number of blocks left</translation>
</message>
<message>
<source>Unknown...</source>
- <translation>Bilinmiyor...</translation>
+ <translation>Unknown...</translation>
</message>
<message>
<source>Last block time</source>
- <translation>Son blok zamanı</translation>
+ <translation>Last block time</translation>
</message>
<message>
<source>Progress</source>
- <translation>İlerleme</translation>
+ <translation>Progress</translation>
</message>
<message>
<source>Progress increase per hour</source>
- <translation>Saat başı ilerleme artışı</translation>
+ <translation>Progress increase per hour</translation>
</message>
<message>
<source>calculating...</source>
- <translation>hesaplanıyor...</translation>
+ <translation>calculating...</translation>
</message>
<message>
<source>Estimated time left until synced</source>
- <translation>Senkronize edilene kadar kalan tahmini süre</translation>
+ <translation>Estimated time left until synced</translation>
</message>
<message>
<source>Hide</source>
- <translation>Gizle</translation>
+ <translation>Hide</translation>
</message>
<message>
<source>Esc</source>
@@ -1093,7 +1097,7 @@
</message>
<message>
<source>default wallet</source>
- <translation>varsayılan cüzdan</translation>
+ <translation>default wallet</translation>
</message>
<message>
<source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
@@ -1104,83 +1108,83 @@
<name>OptionsDialog</name>
<message>
<source>Options</source>
- <translation>Ayarlar</translation>
+ <translation>Options</translation>
</message>
<message>
<source>&amp;Main</source>
- <translation>&amp;Ana Menü</translation>
+ <translation>&amp;Main</translation>
</message>
<message>
<source>Automatically start %1 after logging in to the system.</source>
- <translation>Sistemde oturum açıldığında %1 programını otomatik olarak başlat.</translation>
+ <translation>Automatically start %1 after logging in to the system.</translation>
</message>
<message>
<source>&amp;Start %1 on system login</source>
- <translation>&amp;Açılışta %1 açılsın</translation>
+ <translation>&amp;Start %1 on system login</translation>
</message>
<message>
<source>Size of &amp;database cache</source>
- <translation>Veritabanı önbelleğinin boyutu</translation>
+ <translation>Size of &amp;database cache</translation>
</message>
<message>
<source>Number of script &amp;verification threads</source>
- <translation>İş parçacıklarını &amp;denetleme betiği sayısı</translation>
+ <translation>Number of script &amp;verification threads</translation>
</message>
<message>
<source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
- <translation>Proxy bağlantısı IP adresleri (örneğin IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ <translation>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
</message>
<message>
<source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
- <translation>Bu şebeke türü yoluyla eşlere bağlanmak için belirtilen varsayılan SOCKS5 vekil sunucusunun kullanılıp kullanılmadığını gösterir.</translation>
+ <translation>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</translation>
</message>
<message>
<source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
- <translation>Tor gizli servisleri aracılığıyla eşlere ulaşmak için ayrı SOCKS&amp;5 proksi kullanın:</translation>
+ <translation>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</translation>
</message>
<message>
<source>Hide the icon from the system tray.</source>
- <translation>Simgeyi sistem tepsisinden gizleyin.</translation>
+ <translation>Hide the icon from the system tray.</translation>
</message>
<message>
<source>&amp;Hide tray icon</source>
- <translation>&amp;Simgeyi gizle</translation>
+ <translation>&amp;Hide tray icon</translation>
</message>
<message>
<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>Pencere kapatıldığında uygulamadan çıkmak yerine uygulamayı küçültür. Bu seçenek etkinleştirildiğinde, uygulama sadece menüden çıkış seçildiğinde kapanacaktır.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>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>
- <translation>İşlemler sekmesinde bağlam menüsü unsurları olarak görünen üçüncü taraf bağlantıları (mesela bir blok tarayıcısı). URL'deki %s, işlem hash değeri ile değiştirilecektir. Birden çok bağlantılar düşey çubuklar | ile ayrılacaktır.</translation>
+ <translation>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 |.</translation>
</message>
<message>
<source>Open the %1 configuration file from the working directory.</source>
- <translation>Çalışma dizininden %1  yapılandırma dosyasını aç.</translation>
+ <translation>Open the %1 configuration file from the working directory.</translation>
</message>
<message>
<source>Open Configuration File</source>
- <translation>Konfigürasyon dosyasını aç</translation>
+ <translation>Open Configuration File</translation>
</message>
<message>
<source>Reset all client options to default.</source>
- <translation>Bütün ayarları varsayılana çevir</translation>
+ <translation>Reset all client options to default.</translation>
</message>
<message>
<source>&amp;Reset Options</source>
- <translation>Seçenekleri &amp;Sıfırla</translation>
+ <translation>&amp;Reset Options</translation>
</message>
<message>
<source>&amp;Network</source>
- <translation>Ağ</translation>
+ <translation>&amp;Network</translation>
</message>
<message>
<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>Gelişmiş bazı ayarlar devredışı bırakılmış fakat tüm bloklar hala tam olarak doğrulanabilir. Bu ayarları geri almak tüm block zinciri'nin tekrar indirilmesini gerektirir. Mevcut disk kullanımınızda bir miktar artış görülebilir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Prune &amp;block storage to</source>
- <translation>Temizle &amp;block depolamasını</translation>
+ <translation>Prune &amp;block storage to</translation>
</message>
<message>
<source>GB</source>
@@ -1188,7 +1192,7 @@
</message>
<message>
<source>Reverting this setting requires re-downloading the entire blockchain.</source>
- <translation>Bu ayarları geri değiştirmek tüm blok zinciri'nin indirilmesini gerektirir.</translation>
+ <translation>Reverting this setting requires re-downloading the entire blockchain.</translation>
</message>
<message>
<source>MiB</source>
@@ -1196,55 +1200,55 @@
</message>
<message>
<source>(0 = auto, &lt;0 = leave that many cores free)</source>
- <translation>(0 = otomatik, &lt;0 = bu kadar çekirdeği kullanma)</translation>
+ <translation>(0 = auto, &lt;0 = leave that many cores free)</translation>
</message>
<message>
<source>W&amp;allet</source>
- <translation>Cüzdan</translation>
+ <translation>W&amp;allet</translation>
</message>
<message>
<source>Expert</source>
- <translation>Gelişmiş</translation>
+ <translation>Expert</translation>
</message>
<message>
<source>Enable coin &amp;control features</source>
- <translation>Para &amp;kontrolü özelliklerini etkinleştir</translation>
+ <translation>Enable coin &amp;control features</translation>
</message>
<message>
<source>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>
- <translation>Doğrulanmamış para üstünü harcamayı devre dışı bırakırsanız, bir işlemin para üstü bu işlem için en az bir doğrulama olana dek harcanamaz. Bu, aynı zamanda bakiyenizin nasıl hesaplandığını da etkiler.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>&amp;Spend unconfirmed change</source>
- <translation>Doğrulanmamış para üstünü &amp;harca</translation>
+ <translation>&amp;Spend unconfirmed change</translation>
</message>
<message>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
- <translation>Yönlendiricide Bitcoin istemci portlarını otomatik olarak açar. Bu, sadece yönlendiricinizin UPnP desteği bulunuyorsa ve etkinse çalışabilir.</translation>
+ <translation>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</translation>
</message>
<message>
<source>Map port using &amp;UPnP</source>
- <translation>Portları &amp;UPnP kullanarak haritala</translation>
+ <translation>Map port using &amp;UPnP</translation>
</message>
<message>
<source>Accept connections from outside.</source>
- <translation>Dışarıdan bağlantıları kabul et.</translation>
+ <translation>Accept connections from outside.</translation>
</message>
<message>
<source>Allow incomin&amp;g connections</source>
- <translation>Gelen bağlantılara izin ver</translation>
+ <translation>Allow incomin&amp;g connections</translation>
</message>
<message>
<source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
- <translation>Bitcoin ağına bir SOCKS5 vekil sunucusu aracılığıyla bağlan.</translation>
+ <translation>Connect to the Bitcoin network through a SOCKS5 proxy.</translation>
</message>
<message>
<source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
- <translation>SOCKS5 vekil sunucusu aracılığıyla &amp;bağlan (varsayılan vekil sunucusu):</translation>
+ <translation>&amp;Connect through SOCKS5 proxy (default proxy):</translation>
</message>
<message>
<source>Proxy &amp;IP:</source>
- <translation>Vekil &amp;IP:</translation>
+ <translation>Proxy &amp;IP:</translation>
</message>
<message>
<source>&amp;Port:</source>
@@ -1252,11 +1256,11 @@
</message>
<message>
<source>Port of the proxy (e.g. 9050)</source>
- <translation>Proxy portu (örneğin 9050)</translation>
+ <translation>Port of the proxy (e.g. 9050)</translation>
</message>
<message>
<source>Used for reaching peers via:</source>
- <translation>Eşlere ulaşmak için kullanılır, şu üzerinden:</translation>
+ <translation>Used for reaching peers via:</translation>
</message>
<message>
<source>IPv4</source>
@@ -1272,51 +1276,51 @@
</message>
<message>
<source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
- <translation>Bitcoin ağına gizli Tor servisleri için ayrı bir SOCKS5 vekil sunucusu aracılığıyla bağlan.</translation>
+ <translation>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</translation>
</message>
<message>
<source>&amp;Window</source>
- <translation>Pencere</translation>
+ <translation>&amp;Window</translation>
</message>
<message>
<source>Show only a tray icon after minimizing the window.</source>
- <translation>Küçültüldükten sonra sadece tepsi simgesi göster.</translation>
+ <translation>Show only a tray icon after minimizing the window.</translation>
</message>
<message>
<source>&amp;Minimize to the tray instead of the taskbar</source>
- <translation>İşlem çubuğu yerine sistem çekmecesine &amp;küçült</translation>
+ <translation>&amp;Minimize to the tray instead of the taskbar</translation>
</message>
<message>
<source>M&amp;inimize on close</source>
- <translation>Kapatma sırasında k&amp;üçült</translation>
+ <translation>M&amp;inimize on close</translation>
</message>
<message>
<source>&amp;Display</source>
- <translation>&amp;Görünüm</translation>
+ <translation>&amp;Display</translation>
</message>
<message>
<source>User Interface &amp;language:</source>
- <translation>Kullanıcı arayüzü dili</translation>
+ <translation>User Interface &amp;language:</translation>
</message>
<message>
<source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
- <translation>Kullanıcı arayüzünün dili burada belirtilebilir. Bu ayar %1 tekrar başlatıldığında etkinleşecektir.</translation>
+ <translation>The user interface language can be set here. This setting will take effect after restarting %1.</translation>
</message>
<message>
<source>&amp;Unit to show amounts in:</source>
- <translation>Tutarı göstermek için &amp;birim:</translation>
+ <translation>&amp;Unit to show amounts in:</translation>
</message>
<message>
<source>Choose the default subdivision unit to show in the interface and when sending coins.</source>
- <translation>Bitcoin gönderildiğinde arayüzde gösterilecek varsayılan alt birimi seçiniz.</translation>
+ <translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation>
</message>
<message>
<source>Whether to show coin control features or not.</source>
- <translation>Para kontrol özelliklerinin gösterilip gösterilmeyeceğini ayarlar.</translation>
+ <translation>Whether to show coin control features or not.</translation>
</message>
<message>
<source>&amp;Third party transaction URLs</source>
- <translation>&amp;Üçüncü parti işlem URL'leri</translation>
+ <translation>&amp;Third party transaction URLs</translation>
</message>
<message>
<source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
@@ -1324,55 +1328,55 @@
</message>
<message>
<source>&amp;OK</source>
- <translation>Tamam</translation>
+ <translation>&amp;OK</translation>
</message>
<message>
<source>&amp;Cancel</source>
- <translation>İptal</translation>
+ <translation>&amp;Cancel</translation>
</message>
<message>
<source>default</source>
- <translation>Varsayılan</translation>
+ <translation>default</translation>
</message>
<message>
<source>none</source>
- <translation>boş</translation>
+ <translation>none</translation>
</message>
<message>
<source>Confirm options reset</source>
- <translation>Seçeneklerin sıfırlanmasını teyit et</translation>
+ <translation>Confirm options reset</translation>
</message>
<message>
<source>Client restart required to activate changes.</source>
- <translation>Değişikliklerin aktif edilebilmesi için yeniden başlatma gerekiyor.</translation>
+ <translation>Client restart required to activate changes.</translation>
</message>
<message>
<source>Client will be shut down. Do you want to proceed?</source>
- <translation>İstemci kapanacaktır. Devam etmek istiyor musunuz?</translation>
+ <translation>Client will be shut down. Do you want to proceed?</translation>
</message>
<message>
<source>Configuration options</source>
- <translation>Konfigürasyon ayarları</translation>
+ <translation>Configuration options</translation>
</message>
<message>
<source>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>
- <translation>Konfigürasyon dosyası GUI ayarlarını geçersiz kılmak için gelişmiş kullanıcı ayarlarını değiştirir. Ek olarak, herhangi bir komut satırı seçeneği konfigürasyon dosyasını geçersiz kılar.</translation>
+ <translation>The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</translation>
</message>
<message>
<source>Error</source>
- <translation>Hata</translation>
+ <translation>Error</translation>
</message>
<message>
<source>The configuration file could not be opened.</source>
- <translation>Konfigürasyon dosyası açılamadı.</translation>
+ <translation>The configuration file could not be opened.</translation>
</message>
<message>
<source>This change would require a client restart.</source>
- <translation>Bu değişiklik istemcinin yeniden başlatılmasını gerektirir.</translation>
+ <translation>This change would require a client restart.</translation>
</message>
<message>
<source>The supplied proxy address is invalid.</source>
- <translation>Sağlanan proxy adresi geçerli değil.</translation>
+ <translation>The supplied proxy address is invalid.</translation>
</message>
</context>
<context>
@@ -1383,90 +1387,90 @@
</message>
<message>
<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>Gösterilen bilgi geçerli olmayabilir. Bağlantı tekrar sağlandıktan sonra cüzdanınız otomatik olarak senkronize olacaktır. Henüz senkronize olma işlemi tamamlanmadı.</translation>
+ <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>
<source>Watch-only:</source>
- <translation>Sadece görüntülenebilir:</translation>
+ <translation>Watch-only:</translation>
</message>
<message>
<source>Available:</source>
- <translation>Kullanılabilir:</translation>
+ <translation>Available:</translation>
</message>
<message>
<source>Your current spendable balance</source>
- <translation>Mevcut harcanabilir tutarınız</translation>
+ <translation>Your current spendable balance</translation>
</message>
<message>
<source>Pending:</source>
- <translation>Bekleyen:</translation>
+ <translation>Pending:</translation>
</message>
<message>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
- <translation>Henüz doğrulanmamış ve harcanabilir bakiyeye eklenmemiş işlemlerin toplamı</translation>
+ <translation>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</translation>
</message>
<message>
<source>Immature:</source>
- <translation>Olgunlaşmamış:</translation>
+ <translation>Immature:</translation>
</message>
<message>
<source>Mined balance that has not yet matured</source>
- <translation>Oluşturulan bakiye henüz olgunlaşmamıştır</translation>
+ <translation>Mined balance that has not yet matured</translation>
</message>
<message>
<source>Balances</source>
- <translation>Bakiyeler</translation>
+ <translation>Balances</translation>
</message>
<message>
<source>Total:</source>
- <translation>Toplam:</translation>
+ <translation>Total:</translation>
</message>
<message>
<source>Your current total balance</source>
- <translation>Toplam mevcut miktarınız</translation>
+ <translation>Your current total balance</translation>
</message>
<message>
<source>Your current balance in watch-only addresses</source>
- <translation>Sadece görüntülenebilir adreslerdeki mevcut miktarınız</translation>
+ <translation>Your current balance in watch-only addresses</translation>
</message>
<message>
<source>Spendable:</source>
- <translation>Harcanabilir:</translation>
+ <translation>Spendable:</translation>
</message>
<message>
<source>Recent transactions</source>
- <translation>Yakın zamanda yapılmış işlemler</translation>
+ <translation>Recent transactions</translation>
</message>
<message>
<source>Unconfirmed transactions to watch-only addresses</source>
- <translation>Sadece görüntülenebilir adreslerdeki doğrulanmamış işlemler</translation>
+ <translation>Unconfirmed transactions to watch-only addresses</translation>
</message>
<message>
<source>Mined balance in watch-only addresses that has not yet matured</source>
- <translation>Sadece izlenen adreslerin henüz olgunlaşmamış oluşturulan bakiyeleri</translation>
+ <translation>Mined balance in watch-only addresses that has not yet matured</translation>
</message>
<message>
<source>Current total balance in watch-only addresses</source>
- <translation>Sadece görüntülenebilir adreslerdeki mevcut toplam miktar</translation>
+ <translation>Current total balance in watch-only addresses</translation>
</message>
</context>
<context>
<name>PaymentServer</name>
<message>
<source>Payment request error</source>
- <translation>Ödeme isteği hatası</translation>
+ <translation>Payment request error</translation>
</message>
<message>
<source>Cannot start bitcoin: click-to-pay handler</source>
- <translation>Bitcoin başlatılamadı: tıkla-ve-öde yöneticisi</translation>
+ <translation>Cannot start bitcoin: click-to-pay handler</translation>
</message>
<message>
<source>URI handling</source>
- <translation>URI yönetimi</translation>
+ <translation>URI handling</translation>
</message>
<message>
<source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
- <translation>'bitcoin://' geçerli bir protokol değil. Onun yerine 'bitcoin:' kullanınız.</translation>
+ <translation>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</translation>
</message>
<message>
<source>Cannot process payment request because BIP70 is not supported.</source>
@@ -1482,30 +1486,30 @@
</message>
<message>
<source>Invalid payment address %1</source>
- <translation>Hatalı ödeme adresi %1</translation>
+ <translation>Invalid payment address %1</translation>
</message>
<message>
<source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
- <translation>URI ayrıştırılamıyor! Bunun nedeni geçersiz bir Bitcoin adresi veya hatalı biçimlendirilmiş URI değişkenleri olabilir.</translation>
+ <translation>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation>
</message>
<message>
<source>Payment request file handling</source>
- <translation>Ödeme talebi dosyası yönetimi</translation>
+ <translation>Payment request file handling</translation>
</message>
</context>
<context>
<name>PeerTableModel</name>
<message>
<source>User Agent</source>
- <translation>Kullanıcı Yazılımı</translation>
+ <translation>User Agent</translation>
</message>
<message>
<source>Node/Service</source>
- <translation>Düğüm/Servis</translation>
+ <translation>Node/Service</translation>
</message>
<message>
<source>NodeId</source>
- <translation>Düğüm ID'si</translation>
+ <translation>NodeId</translation>
</message>
<message>
<source>Ping</source>
@@ -1513,34 +1517,34 @@
</message>
<message>
<source>Sent</source>
- <translation>Gönder</translation>
+ <translation>Sent</translation>
</message>
<message>
<source>Received</source>
- <translation>Alındı</translation>
+ <translation>Received</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
- <translation>Tutar</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>Enter a Bitcoin address (e.g. %1)</source>
- <translation>Bitcoin adresinizi girin (örneğin %1)</translation>
+ <translation>Enter a Bitcoin address (e.g. %1)</translation>
</message>
<message>
<source>%1 d</source>
- <translation>%1 g</translation>
+ <translation>%1 d</translation>
</message>
<message>
<source>%1 h</source>
- <translation>%1 s</translation>
+ <translation>%1 h</translation>
</message>
<message>
<source>%1 m</source>
- <translation>%1 d</translation>
+ <translation>%1 m</translation>
</message>
<message>
<source>%1 s</source>
@@ -1548,11 +1552,11 @@
</message>
<message>
<source>None</source>
- <translation>Boş</translation>
+ <translation>None</translation>
</message>
<message>
<source>N/A</source>
- <translation>Yok</translation>
+ <translation>N/A</translation>
</message>
<message>
<source>%1 ms</source>
@@ -1560,31 +1564,31 @@
</message>
<message numerus="yes">
<source>%n second(s)</source>
- <translation><numerusform>%n saniye</numerusform><numerusform>%n saniye</numerusform></translation>
+ <translation><numerusform>%n second</numerusform><numerusform>%n seconds</numerusform></translation>
</message>
<message numerus="yes">
<source>%n minute(s)</source>
- <translation><numerusform>%n dakika</numerusform><numerusform>%n dakika</numerusform></translation>
+ <translation><numerusform>%n minute</numerusform><numerusform>%n minutes</numerusform></translation>
</message>
<message numerus="yes">
<source>%n hour(s)</source>
- <translation><numerusform>%n saat</numerusform><numerusform>%n saat</numerusform></translation>
+ <translation><numerusform>%n hour</numerusform><numerusform>%n hours</numerusform></translation>
</message>
<message numerus="yes">
<source>%n day(s)</source>
- <translation><numerusform>%n gün</numerusform><numerusform>%n gün</numerusform></translation>
+ <translation><numerusform>%n day</numerusform><numerusform>%n days</numerusform></translation>
</message>
<message numerus="yes">
<source>%n week(s)</source>
- <translation><numerusform>%n hafta</numerusform><numerusform>%n hafta</numerusform></translation>
+ <translation><numerusform>%n week</numerusform><numerusform>%n weeks</numerusform></translation>
</message>
<message>
<source>%1 and %2</source>
- <translation>%1 ve %2</translation>
+ <translation>%1 and %2</translation>
</message>
<message numerus="yes">
<source>%n year(s)</source>
- <translation><numerusform>%n yıl</numerusform><numerusform>%n yıl</numerusform></translation>
+ <translation><numerusform>%n year</numerusform><numerusform>%n years</numerusform></translation>
</message>
<message>
<source>%1 B</source>
@@ -1604,42 +1608,42 @@
</message>
<message>
<source>Error: Specified data directory "%1" does not exist.</source>
- <translation>Hata: Belirtilen "%1" veri klasörü yoktur.</translation>
+ <translation>Error: Specified data directory "%1" does not exist.</translation>
</message>
<message>
<source>Error: Cannot parse configuration file: %1.</source>
- <translation>Hata: %1 yapılandırma dosyası ayrıştırılamadı.</translation>
+ <translation>Error: Cannot parse configuration file: %1.</translation>
</message>
<message>
<source>Error: %1</source>
- <translation>Hata: %1</translation>
+ <translation>Error: %1</translation>
</message>
<message>
<source>%1 didn't yet exit safely...</source>
- <translation>%1 henüz güvenli bir şekilde çıkış yapmamıştır...</translation>
+ <translation>%1 didn't yet exit safely...</translation>
</message>
<message>
<source>unknown</source>
- <translation>bilinmiyor</translation>
+ <translation>unknown</translation>
</message>
</context>
<context>
<name>QRImageWidget</name>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Görüntüyü kaydet</translation>
+ <translation>&amp;Save Image...</translation>
</message>
<message>
<source>&amp;Copy Image</source>
- <translation>&amp;Görüntüyü kopyala</translation>
+ <translation>&amp;Copy Image</translation>
</message>
<message>
<source>Resulting URI too long, try to reduce the text for label / message.</source>
- <translation>Sonuç URI çok uzun, etiket ya da ileti metnini kısaltmayı deneyiniz.</translation>
+ <translation>Resulting URI too long, try to reduce the text for label / message.</translation>
</message>
<message>
<source>Error encoding URI into QR Code.</source>
- <translation>URI'nin QR koduna kodlanmasında hata oluştu.</translation>
+ <translation>Error encoding URI into QR Code.</translation>
</message>
<message>
<source>QR code support not available.</source>
@@ -1647,38 +1651,38 @@
</message>
<message>
<source>Save QR Code</source>
- <translation>QR kodu kaydet</translation>
+ <translation>Save QR Code</translation>
</message>
<message>
<source>PNG Image (*.png)</source>
- <translation>PNG Resim (*.png)</translation>
+ <translation>PNG Image (*.png)</translation>
</message>
</context>
<context>
<name>RPCConsole</name>
<message>
<source>N/A</source>
- <translation>Yok</translation>
+ <translation>N/A</translation>
</message>
<message>
<source>Client version</source>
- <translation>Arayüz versiyonu</translation>
+ <translation>Client version</translation>
</message>
<message>
<source>&amp;Information</source>
- <translation>&amp;Bilgi</translation>
+ <translation>&amp;Information</translation>
</message>
<message>
<source>General</source>
- <translation>Genel</translation>
+ <translation>General</translation>
</message>
<message>
<source>Using BerkeleyDB version</source>
- <translation>Kullanılan BerkeleyDB versiyonu</translation>
+ <translation>Using BerkeleyDB version</translation>
</message>
<message>
<source>Datadir</source>
- <translation>Veri konumu</translation>
+ <translation>Datadir</translation>
</message>
<message>
<source>To specify a non-default location of the data directory use the '%1' option.</source>
@@ -1694,99 +1698,107 @@
</message>
<message>
<source>Startup time</source>
- <translation>Başlangıç zamanı</translation>
+ <translation>Startup time</translation>
</message>
<message>
<source>Network</source>
- <translation>Ağ</translation>
+ <translation>Network</translation>
</message>
<message>
<source>Name</source>
- <translation>İsim</translation>
+ <translation>Name</translation>
</message>
<message>
<source>Number of connections</source>
- <translation>Bağlantı sayısı</translation>
+ <translation>Number of connections</translation>
</message>
<message>
<source>Block chain</source>
- <translation>Blok zinciri</translation>
+ <translation>Block chain</translation>
</message>
<message>
<source>Current number of blocks</source>
- <translation>Güncel blok sayısı</translation>
+ <translation>Current number of blocks</translation>
</message>
<message>
<source>Memory Pool</source>
- <translation>Bellek Alanı</translation>
+ <translation>Memory Pool</translation>
</message>
<message>
<source>Current number of transactions</source>
- <translation>Güncel işlem sayısı</translation>
+ <translation>Current number of transactions</translation>
</message>
<message>
<source>Memory usage</source>
- <translation>Bellek kullanımı</translation>
+ <translation>Memory usage</translation>
</message>
<message>
<source>Wallet: </source>
- <translation>Cüzdan:</translation>
+ <translation>Wallet: </translation>
</message>
<message>
<source>(none)</source>
- <translation>(boş)</translation>
+ <translation>(none)</translation>
</message>
<message>
<source>&amp;Reset</source>
- <translation>&amp;Yeniden başlat</translation>
+ <translation>&amp;Reset</translation>
</message>
<message>
<source>Received</source>
- <translation>Alındı</translation>
+ <translation>Received</translation>
</message>
<message>
<source>Sent</source>
- <translation>Gönder</translation>
+ <translation>Sent</translation>
</message>
<message>
<source>&amp;Peers</source>
- <translation>&amp;Eşler</translation>
+ <translation>&amp;Peers</translation>
</message>
<message>
<source>Banned peers</source>
- <translation>Yasaklı eşler</translation>
+ <translation>Banned peers</translation>
</message>
<message>
<source>Select a peer to view detailed information.</source>
- <translation>Ayrıntılı bilgi görmek için bir eş seçin.</translation>
+ <translation>Select a peer to view detailed information.</translation>
</message>
<message>
<source>Whitelisted</source>
- <translation>Beyaz listede</translation>
+ <translation>Whitelisted</translation>
</message>
<message>
<source>Direction</source>
- <translation>Yön</translation>
+ <translation>Direction</translation>
</message>
<message>
<source>Version</source>
- <translation>Versiyon</translation>
+ <translation>Version</translation>
</message>
<message>
<source>Starting Block</source>
- <translation>Başlangıç Bloku</translation>
+ <translation>Starting Block</translation>
</message>
<message>
<source>Synced Headers</source>
- <translation>Eşleşmiş Üstbilgiler</translation>
+ <translation>Synced Headers</translation>
</message>
<message>
<source>Synced Blocks</source>
- <translation>Eşleşmiş Bloklar</translation>
+ <translation>Synced Blocks</translation>
+ </message>
+ <message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>The mapped Autonomous System used for diversifying peer selection.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapped AS</translation>
</message>
<message>
<source>User Agent</source>
- <translation>Kullanıcı Yazılımı</translation>
+ <translation>User Agent</translation>
</message>
<message>
<source>Node window</source>
@@ -1794,214 +1806,214 @@
</message>
<message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
- <translation>Güncel veri klasöründen %1 hata ayıklama kütük dosyasını açar. Büyük kütük dosyaları için bu birkaç saniye alabilir.</translation>
+ <translation>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</translation>
</message>
<message>
<source>Decrease font size</source>
- <translation>Font boyutunu küçült</translation>
+ <translation>Decrease font size</translation>
</message>
<message>
<source>Increase font size</source>
- <translation>Yazıtipi boyutunu büyült</translation>
+ <translation>Increase font size</translation>
</message>
<message>
<source>Services</source>
- <translation>Servisler</translation>
+ <translation>Services</translation>
</message>
<message>
<source>Ban Score</source>
- <translation>Yasaklama Skoru</translation>
+ <translation>Ban Score</translation>
</message>
<message>
<source>Connection Time</source>
- <translation>Bağlantı süresi</translation>
+ <translation>Connection Time</translation>
</message>
<message>
<source>Last Send</source>
- <translation>Son gönderim</translation>
+ <translation>Last Send</translation>
</message>
<message>
<source>Last Receive</source>
- <translation>Son alış</translation>
+ <translation>Last Receive</translation>
</message>
<message>
<source>Ping Time</source>
- <translation>Ping süresi</translation>
+ <translation>Ping Time</translation>
</message>
<message>
<source>The duration of a currently outstanding ping.</source>
- <translation>Güncel olarak göze çarpan bir ping'in süresi.</translation>
+ <translation>The duration of a currently outstanding ping.</translation>
</message>
<message>
<source>Ping Wait</source>
- <translation>Ping bekliyor</translation>
+ <translation>Ping Wait</translation>
</message>
<message>
<source>Min Ping</source>
- <translation>En Düşük Ping</translation>
+ <translation>Min Ping</translation>
</message>
<message>
<source>Time Offset</source>
- <translation>Saat Farkı</translation>
+ <translation>Time Offset</translation>
</message>
<message>
<source>Last block time</source>
- <translation>Son blok zamanı</translation>
+ <translation>Last block time</translation>
</message>
<message>
<source>&amp;Open</source>
- <translation>&amp;Aç</translation>
+ <translation>&amp;Open</translation>
</message>
<message>
<source>&amp;Console</source>
- <translation>&amp;Konsol</translation>
+ <translation>&amp;Console</translation>
</message>
<message>
<source>&amp;Network Traffic</source>
- <translation>&amp;Ağ trafiği</translation>
+ <translation>&amp;Network Traffic</translation>
</message>
<message>
<source>Totals</source>
- <translation>Toplam</translation>
+ <translation>Totals</translation>
</message>
<message>
<source>In:</source>
- <translation>İçeri:</translation>
+ <translation>In:</translation>
</message>
<message>
<source>Out:</source>
- <translation>Dışarı:</translation>
+ <translation>Out:</translation>
</message>
<message>
<source>Debug log file</source>
- <translation>Hata ayıklama kütük dosyası</translation>
+ <translation>Debug log file</translation>
</message>
<message>
<source>Clear console</source>
- <translation>Konsolu temizle</translation>
+ <translation>Clear console</translation>
</message>
<message>
<source>1 &amp;hour</source>
- <translation>1 &amp;saat</translation>
+ <translation>1 &amp;hour</translation>
</message>
<message>
<source>1 &amp;day</source>
- <translation>1 &amp;gün</translation>
+ <translation>1 &amp;day</translation>
</message>
<message>
<source>1 &amp;week</source>
- <translation>1 &amp;hafta</translation>
+ <translation>1 &amp;week</translation>
</message>
<message>
<source>1 &amp;year</source>
- <translation>1 &amp;yıl</translation>
+ <translation>1 &amp;year</translation>
</message>
<message>
<source>&amp;Disconnect</source>
- <translation>&amp;Bağlantı kesildi</translation>
+ <translation>&amp;Disconnect</translation>
</message>
<message>
<source>Ban for</source>
- <translation>Yasakla</translation>
+ <translation>Ban for</translation>
</message>
<message>
<source>&amp;Unban</source>
- <translation>&amp;Yasaklamayı Kaldır</translation>
+ <translation>&amp;Unban</translation>
</message>
<message>
<source>Welcome to the %1 RPC console.</source>
- <translation>%1 RPC konsoluna hoş geldiniz.</translation>
+ <translation>Welcome to the %1 RPC console.</translation>
</message>
<message>
<source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
- <translation>Geçmişte gezinmek için yukarı ve aşağı oklarını kullanın ve ekranı temizlemek için %1 kullanın.</translation>
+ <translation>Use up and down arrows to navigate history, and %1 to clear screen.</translation>
</message>
<message>
<source>Type %1 for an overview of available commands.</source>
- <translation>Mevcut komutlara göz atmak için %1 yazın.</translation>
+ <translation>Type %1 for an overview of available commands.</translation>
</message>
<message>
<source>For more information on using this console type %1.</source>
- <translation>Bu konsolun kullanımı hakkında daha fazla bilgi için %1 yazın.</translation>
+ <translation>For more information on using this console type %1.</translation>
</message>
<message>
<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>
- <translation>UYARI: Bitcoin dolandırıcılarının çok fazla etkin olduğu zamanlarda, dolandırıcılar bazı kullanıcılara buraya komutlar yazmalarını söylerek onların cüzdanlarındaki bitcoinleri çalmışlardır. Bir komutun sonuçlarını tam olarak anlamadan bu konsolu kullanmayın.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Network activity disabled</source>
- <translation>Ağ aktivitesi pasif</translation>
+ <translation>Network activity disabled</translation>
</message>
<message>
<source>Executing command without any wallet</source>
- <translation>Komut bir cüzdan olmadan çalıştırılıyor</translation>
+ <translation>Executing command without any wallet</translation>
</message>
<message>
<source>Executing command using "%1" wallet</source>
- <translation>Komut "%1" cüzdanı kullanılarak çalıştırılıyor</translation>
+ <translation>Executing command using "%1" wallet</translation>
</message>
<message>
<source>(node id: %1)</source>
- <translation>(düğüm kimliği: %1)</translation>
+ <translation>(node id: %1)</translation>
</message>
<message>
<source>via %1</source>
- <translation>%1 vasıtasıyla</translation>
+ <translation>via %1</translation>
</message>
<message>
<source>never</source>
- <translation>asla</translation>
+ <translation>never</translation>
</message>
<message>
<source>Inbound</source>
- <translation>Gelen</translation>
+ <translation>Inbound</translation>
</message>
<message>
<source>Outbound</source>
- <translation>Giden</translation>
+ <translation>Outbound</translation>
</message>
<message>
<source>Yes</source>
- <translation>Evet</translation>
+ <translation>Yes</translation>
</message>
<message>
<source>No</source>
- <translation>Hayır</translation>
+ <translation>No</translation>
</message>
<message>
<source>Unknown</source>
- <translation>Bilinmiyor</translation>
+ <translation>Unknown</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>&amp;Amount:</source>
- <translation>&amp;Tutar:</translation>
+ <translation>&amp;Amount:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Etiket</translation>
+ <translation>&amp;Label:</translation>
</message>
<message>
<source>&amp;Message:</source>
- <translation>&amp;Mesaj</translation>
+ <translation>&amp;Message:</translation>
</message>
<message>
<source>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>
- <translation>Talep açıldığında gösterilecek, isteğinize dayalı, ödeme talebi ile ilişkilendirilecek bir ileti. Not: Bu ileti ödeme ile birlikte Bitcoin ağı üzerinden gönderilmeyecektir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>An optional label to associate with the new receiving address.</source>
- <translation>Yeni alım adresi ile ilişkili, seçiminize dayalı etiket.</translation>
+ <translation>An optional label to associate with the new receiving address.</translation>
</message>
<message>
<source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
- <translation>Ödeme talep etmek için bu formu kullanın. Tüm alanlar &lt;b&gt;seçime dayalıdır&lt;/b&gt;.</translation>
+ <translation>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</translation>
</message>
<message>
<source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
- <translation>Seçiminize dayalı talep edilecek tutar. Belli bir tutar talep etmemek için bunu boş bırakın veya sıfır değerini kullanın.</translation>
+ <translation>An optional amount to request. Leave this empty or zero to not request a specific amount.</translation>
</message>
<message>
<source>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>
@@ -2017,11 +2029,11 @@
</message>
<message>
<source>Clear all fields of the form.</source>
- <translation>Formdaki tüm alanları temizle.</translation>
+ <translation>Clear all fields of the form.</translation>
</message>
<message>
<source>Clear</source>
- <translation>Temizle</translation>
+ <translation>Clear</translation>
</message>
<message>
<source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
@@ -2029,70 +2041,70 @@
</message>
<message>
<source>Generate native segwit (Bech32) address</source>
- <translation>Yerli segwit (Bech32) adresi oluştur</translation>
+ <translation>Generate native segwit (Bech32) address</translation>
</message>
<message>
<source>Requested payments history</source>
- <translation>Talep edilen ödemelerin tarihçesi</translation>
+ <translation>Requested payments history</translation>
</message>
<message>
<source>Show the selected request (does the same as double clicking an entry)</source>
- <translation>Seçilen talebi göster (bir unsura çift tıklamakla aynı anlama gelir)</translation>
+ <translation>Show the selected request (does the same as double clicking an entry)</translation>
</message>
<message>
<source>Show</source>
- <translation>Göster</translation>
+ <translation>Show</translation>
</message>
<message>
<source>Remove the selected entries from the list</source>
- <translation>Seçilen unsurları listeden kaldır</translation>
+ <translation>Remove the selected entries from the list</translation>
</message>
<message>
<source>Remove</source>
- <translation>Sil</translation>
+ <translation>Remove</translation>
</message>
<message>
<source>Copy URI</source>
- <translation>URI'yi kopyala</translation>
+ <translation>Copy URI</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Etiketi kopyala</translation>
+ <translation>Copy label</translation>
</message>
<message>
<source>Copy message</source>
- <translation>Mesajı kopyala</translation>
+ <translation>Copy message</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Tutarı kopyala</translation>
+ <translation>Copy amount</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>QR Code</source>
- <translation>QR kod</translation>
+ <translation>QR Code</translation>
</message>
<message>
<source>Copy &amp;URI</source>
- <translation>URI'yi kopyala</translation>
+ <translation>Copy &amp;URI</translation>
</message>
<message>
<source>Copy &amp;Address</source>
- <translation>&amp;Adresi Kopyala</translation>
+ <translation>Copy &amp;Address</translation>
</message>
<message>
<source>&amp;Save Image...</source>
- <translation>&amp;Görüntüyü kaydet</translation>
+ <translation>&amp;Save Image...</translation>
</message>
<message>
<source>Request payment to %1</source>
- <translation>%1 unsuruna ödeme talep et</translation>
+ <translation>Request payment to %1</translation>
</message>
<message>
<source>Payment information</source>
- <translation>Ödeme bilgisi</translation>
+ <translation>Payment information</translation>
</message>
<message>
<source>URI</source>
@@ -2100,117 +2112,117 @@
</message>
<message>
<source>Address</source>
- <translation>adres</translation>
+ <translation>Address</translation>
</message>
<message>
<source>Amount</source>
- <translation>Tutar</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>Label</source>
- <translation>etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Message</source>
- <translation>Mesaj</translation>
+ <translation>Message</translation>
</message>
<message>
<source>Wallet</source>
- <translation>Cüzdan</translation>
+ <translation>Wallet</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
- <translation>Tarih</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Label</source>
- <translation>etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Message</source>
- <translation>Mesaj</translation>
+ <translation>Message</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(etiket yok)</translation>
+ <translation>(no label)</translation>
</message>
<message>
<source>(no message)</source>
- <translation>(mesaj yok)</translation>
+ <translation>(no message)</translation>
</message>
<message>
<source>(no amount requested)</source>
- <translation>(tutar talep edilmedi)</translation>
+ <translation>(no amount requested)</translation>
</message>
<message>
<source>Requested</source>
- <translation>Talep edilen</translation>
+ <translation>Requested</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Send Coins</source>
- <translation>Coin gönder</translation>
+ <translation>Send Coins</translation>
</message>
<message>
<source>Coin Control Features</source>
- <translation>Para kontrolü özellikleri</translation>
+ <translation>Coin Control Features</translation>
</message>
<message>
<source>Inputs...</source>
- <translation>Girdiler...</translation>
+ <translation>Inputs...</translation>
</message>
<message>
<source>automatically selected</source>
- <translation>Otomatik seçildi</translation>
+ <translation>automatically selected</translation>
</message>
<message>
<source>Insufficient funds!</source>
- <translation>Yetersiz fon!</translation>
+ <translation>Insufficient funds!</translation>
</message>
<message>
<source>Quantity:</source>
- <translation>Miktar:</translation>
+ <translation>Quantity:</translation>
</message>
<message>
<source>Bytes:</source>
- <translation>Bayt</translation>
+ <translation>Bytes:</translation>
</message>
<message>
<source>Amount:</source>
- <translation>Tutar:</translation>
+ <translation>Amount:</translation>
</message>
<message>
<source>Fee:</source>
- <translation>Ücret:</translation>
+ <translation>Fee:</translation>
</message>
<message>
<source>After Fee:</source>
- <translation>Ücretten sonra kalan:</translation>
+ <translation>After Fee:</translation>
</message>
<message>
<source>Change:</source>
- <translation>Değişen:</translation>
+ <translation>Change:</translation>
</message>
<message>
<source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
- <translation>Bu etkinleştirildiyse fakat para üstü adresi boş ya da geçersizse para üstü yeni oluşturulan bir adrese gönderilecektir.</translation>
+ <translation>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</translation>
</message>
<message>
<source>Custom change address</source>
- <translation>Özel para üstü adresi</translation>
+ <translation>Custom change address</translation>
</message>
<message>
<source>Transaction Fee:</source>
- <translation>Gönderim ücreti:</translation>
+ <translation>Transaction Fee:</translation>
</message>
<message>
<source>Choose...</source>
- <translation>Seçiniz...</translation>
+ <translation>Choose...</translation>
</message>
<message>
<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>
@@ -2218,7 +2230,7 @@
</message>
<message>
<source>Warning: Fee estimation is currently not possible.</source>
- <translation>Uyarı: Ücret tahmini şu anda mümkün değildir.</translation>
+ <translation>Warning: Fee estimation is currently not possible.</translation>
</message>
<message>
<source>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size.
@@ -2230,39 +2242,39 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>per kilobyte</source>
- <translation>kilobyte başına</translation>
+ <translation>per kilobyte</translation>
</message>
<message>
<source>Hide</source>
- <translation>Gizle</translation>
+ <translation>Hide</translation>
</message>
<message>
<source>Recommended:</source>
- <translation>Önerilen:</translation>
+ <translation>Recommended:</translation>
</message>
<message>
<source>Custom:</source>
- <translation>Özel:</translation>
+ <translation>Custom:</translation>
</message>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
- <translation>(Zeki ücret henüz başlatılmadı. Bu genelde birkaç blok alır...)</translation>
+ <translation>(Smart fee not initialized yet. This usually takes a few blocks...)</translation>
</message>
<message>
<source>Send to multiple recipients at once</source>
- <translation>Birçok alıcıya aynı anda gönder</translation>
+ <translation>Send to multiple recipients at once</translation>
</message>
<message>
<source>Add &amp;Recipient</source>
- <translation>&amp;Alıcı ekle</translation>
+ <translation>Add &amp;Recipient</translation>
</message>
<message>
<source>Clear all fields of the form.</source>
- <translation>Formdaki tüm alanları temizle.</translation>
+ <translation>Clear all fields of the form.</translation>
</message>
<message>
<source>Dust:</source>
- <translation>Toz:</translation>
+ <translation>Dust:</translation>
</message>
<message>
<source>Hide transaction fee settings</source>
@@ -2278,7 +2290,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Confirmation time target:</source>
- <translation>Doğrulama süresi hedefi:</translation>
+ <translation>Confirmation time target:</translation>
</message>
<message>
<source>Enable Replace-By-Fee</source>
@@ -2290,51 +2302,51 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Clear &amp;All</source>
- <translation>Hepsini sil</translation>
+ <translation>Clear &amp;All</translation>
</message>
<message>
<source>Balance:</source>
- <translation>Bakiye:</translation>
+ <translation>Balance:</translation>
</message>
<message>
<source>Confirm the send action</source>
- <translation>Yollama etkinliğini teyit ediniz</translation>
+ <translation>Confirm the send action</translation>
</message>
<message>
<source>S&amp;end</source>
- <translation>G&amp;önder</translation>
+ <translation>S&amp;end</translation>
</message>
<message>
<source>Copy quantity</source>
- <translation>Miktarı kopyala</translation>
+ <translation>Copy quantity</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Tutarı kopyala</translation>
+ <translation>Copy amount</translation>
</message>
<message>
<source>Copy fee</source>
- <translation>Ücreti kopyala</translation>
+ <translation>Copy fee</translation>
</message>
<message>
<source>Copy after fee</source>
- <translation>Ücretten sonrasını kopyala</translation>
+ <translation>Copy after fee</translation>
</message>
<message>
<source>Copy bytes</source>
- <translation>Baytları kopyala</translation>
+ <translation>Copy bytes</translation>
</message>
<message>
<source>Copy dust</source>
- <translation>Tozu kopyala</translation>
+ <translation>Copy dust</translation>
</message>
<message>
<source>Copy change</source>
- <translation>Para üstünü kopyala</translation>
+ <translation>Copy change</translation>
</message>
<message>
<source>%1 (%2 blocks)</source>
- <translation>%1 (%2 blok)</translation>
+ <translation>%1 (%2 blocks)</translation>
</message>
<message>
<source>Cr&amp;eate Unsigned</source>
@@ -2354,7 +2366,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>%1 to %2</source>
- <translation>%1'den %2'e</translation>
+ <translation>%1 to %2</translation>
</message>
<message>
<source>Do you want to draft this transaction?</source>
@@ -2362,7 +2374,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Are you sure you want to send?</source>
- <translation>Göndermek istediğinize emin misiniz?</translation>
+ <translation>Are you sure you want to send?</translation>
</message>
<message>
<source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
@@ -2370,7 +2382,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>or</source>
- <translation>ya da</translation>
+ <translation>or</translation>
</message>
<message>
<source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
@@ -2378,11 +2390,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Please, review your transaction.</source>
- <translation>Lütfen, işleminizi gözden geçirin.</translation>
+ <translation>Please, review your transaction.</translation>
</message>
<message>
<source>Transaction fee</source>
- <translation>Gönderim ücreti</translation>
+ <translation>Transaction fee</translation>
</message>
<message>
<source>Not signalling Replace-By-Fee, BIP-125.</source>
@@ -2390,7 +2402,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Total Amount</source>
- <translation>Toplam Tutar</translation>
+ <translation>Total Amount</translation>
</message>
<message>
<source>To review recipient list click "Show Details..."</source>
@@ -2398,7 +2410,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Confirm send coins</source>
- <translation>Coin gönderimini onaylayın</translation>
+ <translation>Confirm send coins</translation>
</message>
<message>
<source>Confirm transaction proposal</source>
@@ -2422,82 +2434,82 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>The recipient address is not valid. Please recheck.</source>
- <translation>Alıcı adresi geçerli değildir. Lütfen tekrar kontrol ediniz.</translation>
+ <translation>The recipient address is not valid. Please recheck.</translation>
</message>
<message>
<source>The amount to pay must be larger than 0.</source>
- <translation>Ödeyeceğiniz tutarın 0'dan yüksek olması gerekir.</translation>
+ <translation>The amount to pay must be larger than 0.</translation>
</message>
<message>
<source>The amount exceeds your balance.</source>
- <translation>Tutar bakiyenizden yüksektir.</translation>
+ <translation>The amount exceeds your balance.</translation>
</message>
<message>
<source>The total exceeds your balance when the %1 transaction fee is included.</source>
- <translation>Toplam, %1 işlem ücreti eklendiğinde bakiyenizi geçmektedir.</translation>
+ <translation>The total exceeds your balance when the %1 transaction fee is included.</translation>
</message>
<message>
<source>Duplicate address found: addresses should only be used once each.</source>
- <translation>Tekrarlayan adres bulundu: adresler sadece bir kez kullanılmalıdır.</translation>
+ <translation>Duplicate address found: addresses should only be used once each.</translation>
</message>
<message>
<source>Transaction creation failed!</source>
- <translation>İşlem oluşturma başarısız!</translation>
+ <translation>Transaction creation failed!</translation>
</message>
<message>
<source>A fee higher than %1 is considered an absurdly high fee.</source>
- <translation>%1 tutarından yüksek bir ücret saçma derecede yüksek bir ücret olarak kabul edilir.</translation>
+ <translation>A fee higher than %1 is considered an absurdly high fee.</translation>
</message>
<message>
<source>Payment request expired.</source>
- <translation>Ödeme talebinin geçerlilik süresi bitti.</translation>
+ <translation>Payment request expired.</translation>
</message>
<message numerus="yes">
<source>Estimated to begin confirmation within %n block(s).</source>
- <translation><numerusform>Tahmini %n blok içinde doğrulamaya başlanacaktır.</numerusform><numerusform>Tahmini %n blok içinde doğrulamaya başlanacaktır.</numerusform></translation>
+ <translation><numerusform>Estimated to begin confirmation within %n block.</numerusform><numerusform>Estimated to begin confirmation within %n blocks.</numerusform></translation>
</message>
<message>
<source>Warning: Invalid Bitcoin address</source>
- <translation>Uyarı: Hatalı Bitcoin adresi</translation>
+ <translation>Warning: Invalid Bitcoin address</translation>
</message>
<message>
<source>Warning: Unknown change address</source>
- <translation>Uyarı: Bilinmeyen para üstü adresi</translation>
+ <translation>Warning: Unknown change address</translation>
</message>
<message>
<source>Confirm custom change address</source>
- <translation>Özel para üstü adresini onayla</translation>
+ <translation>Confirm custom change address</translation>
</message>
<message>
<source>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>
- <translation>Para üstü için seçtiğiniz adres bu cüzdanın bir parçası değil. Cüzdanınızdaki bir miktar veya tüm para bu adrese gönderilebilir. Emin misiniz?</translation>
+ <translation>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?</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(etiket yok)</translation>
+ <translation>(no label)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>A&amp;mount:</source>
- <translation>T&amp;utar:</translation>
+ <translation>A&amp;mount:</translation>
</message>
<message>
<source>Pay &amp;To:</source>
- <translation>&amp;Şu adrese öde:</translation>
+ <translation>Pay &amp;To:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Etiket</translation>
+ <translation>&amp;Label:</translation>
</message>
<message>
<source>Choose previously used address</source>
- <translation>Önceden kullanılmış adres seç</translation>
+ <translation>Choose previously used address</translation>
</message>
<message>
<source>The Bitcoin address to send the payment to</source>
- <translation>Ödemenin yollanacağı Bitcoin adresi</translation>
+ <translation>The Bitcoin address to send the payment to</translation>
</message>
<message>
<source>Alt+A</source>
@@ -2505,7 +2517,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Paste address from clipboard</source>
- <translation>Panodaki adresi yapıştırın</translation>
+ <translation>Paste address from clipboard</translation>
</message>
<message>
<source>Alt+P</source>
@@ -2513,7 +2525,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Remove this entry</source>
- <translation>Bu ögeyi kaldır</translation>
+ <translation>Remove this entry</translation>
</message>
<message>
<source>The amount to send in the selected unit</source>
@@ -2521,77 +2533,77 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>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>
- <translation>Ücret yollanan tutardan alınacaktır. Alıcı tutar alanına girdiğinizden daha az bitcoin alacaktır. Eğer birden çok alıcı seçiliyse ücret eşit olarak bölünecektir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>S&amp;ubtract fee from amount</source>
- <translation>Ücreti tutardan düş</translation>
+ <translation>S&amp;ubtract fee from amount</translation>
</message>
<message>
<source>Use available balance</source>
- <translation>Mevcut bakiyeyi kullan</translation>
+ <translation>Use available balance</translation>
</message>
<message>
<source>Message:</source>
- <translation>Mesaj:</translation>
+ <translation>Message:</translation>
</message>
<message>
<source>This is an unauthenticated payment request.</source>
- <translation>Bu, kimliği doğrulanmamış bir ödeme talebidir.</translation>
+ <translation>This is an unauthenticated payment request.</translation>
</message>
<message>
<source>This is an authenticated payment request.</source>
- <translation>Bu, kimliği doğrulanmış bir ödeme talebidir.</translation>
+ <translation>This is an authenticated payment request.</translation>
</message>
<message>
<source>Enter a label for this address to add it to the list of used addresses</source>
- <translation>Kullanılmış adres listesine eklemek için bu adrese bir etiket girin</translation>
+ <translation>Enter a label for this address to add it to the list of used addresses</translation>
</message>
<message>
<source>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>
- <translation>Referans için bitcoin: URI'siyle iliştirilmiş işlemle birlikte depolanacak bir ileti. Not: Bu mesaj Bitcoin ağı üzerinden gönderilmeyecektir.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>Pay To:</source>
- <translation>Şu adrese öde:</translation>
+ <translation>Pay To:</translation>
</message>
<message>
<source>Memo:</source>
- <translation>Not:</translation>
+ <translation>Memo:</translation>
</message>
</context>
<context>
<name>ShutdownWindow</name>
<message>
<source>%1 is shutting down...</source>
- <translation>%1 kapanıyor...</translation>
+ <translation>%1 is shutting down...</translation>
</message>
<message>
<source>Do not shut down the computer until this window disappears.</source>
- <translation>Bu pencere kalkıncaya dek bilgisayarı kapatmayınız.</translation>
+ <translation>Do not shut down the computer until this window disappears.</translation>
</message>
</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
<source>Signatures - Sign / Verify a Message</source>
- <translation>İmzalar - İleti İmzala / Kontrol et</translation>
+ <translation>Signatures - Sign / Verify a Message</translation>
</message>
<message>
<source>&amp;Sign Message</source>
- <translation>İleti &amp;imzala</translation>
+ <translation>&amp;Sign Message</translation>
</message>
<message>
<source>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>
- <translation>Adreslerinize yollanan bitcoinleri alabileceğiniz ispatlamak için adreslerinizle iletiler/anlaşmalar imzalayabilirsiniz. Oltalama saldırılarının kimliğinizi imzanızla elde etmeyi deneyebilecekleri için belirsiz ya da rastgele hiçbir şey imzalamamaya dikkat ediniz. Sadece ayrıntılı açıklaması olan ve tümüne katıldığınız ifadeleri imzalayınız.</translation>
+ <translation>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.</translation>
</message>
<message>
<source>The Bitcoin address to sign the message with</source>
- <translation>İletinin imzalanmasında kullanılacak Bitcoin adresi</translation>
+ <translation>The Bitcoin address to sign the message with</translation>
</message>
<message>
<source>Choose previously used address</source>
- <translation>Önceden kullanılmış adres seç</translation>
+ <translation>Choose previously used address</translation>
</message>
<message>
<source>Alt+A</source>
@@ -2599,7 +2611,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Paste address from clipboard</source>
- <translation>Panodaki adresi yapıştırın</translation>
+ <translation>Paste address from clipboard</translation>
</message>
<message>
<source>Alt+P</source>
@@ -2607,43 +2619,43 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Enter the message you want to sign here</source>
- <translation>İmzalamak istediğiniz iletiyi burada giriniz</translation>
+ <translation>Enter the message you want to sign here</translation>
</message>
<message>
<source>Signature</source>
- <translation>İmza</translation>
+ <translation>Signature</translation>
</message>
<message>
<source>Copy the current signature to the system clipboard</source>
- <translation>Güncel imzayı sistem panosuna kopyala</translation>
+ <translation>Copy the current signature to the system clipboard</translation>
</message>
<message>
<source>Sign the message to prove you own this Bitcoin address</source>
- <translation>Bu Bitcoin adresinin sizin olduğunu ispatlamak için iletiyi imzalayın</translation>
+ <translation>Sign the message to prove you own this Bitcoin address</translation>
</message>
<message>
<source>Sign &amp;Message</source>
- <translation>İmza &amp;Mesaj</translation>
+ <translation>Sign &amp;Message</translation>
</message>
<message>
<source>Reset all sign message fields</source>
- <translation>Tüm ileti alanlarını sıfırla</translation>
+ <translation>Reset all sign message fields</translation>
</message>
<message>
<source>Clear &amp;All</source>
- <translation>Hepsini sil</translation>
+ <translation>Clear &amp;All</translation>
</message>
<message>
<source>&amp;Verify Message</source>
- <translation>İletiyi &amp;kontrol et</translation>
+ <translation>&amp;Verify Message</translation>
</message>
<message>
<source>Enter the receiver'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>
- <translation>Alıcının adresini, iletiyi (satır sonları, boşluklar, sekmeler vs. karakterleri tam olarak kopyaladığınızdan emin olunuz) ve imzayı aşağıya giriniz. Bir ortadaki adam saldırısı tarafından kandırılmaya engel olmak için imzadan, imzalı iletinin içeriğini aşan bir anlam çıkarmamaya dikkat ediniz. Bunun sadece imzalayan tarafın adres ile alım yapabildiğini ispatladığını ve herhangi bir işlemin gönderi tarafını kanıtlayamayacağını unutmayınız!</translation>
+ <translation>Enter the receiver'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!</translation>
</message>
<message>
<source>The Bitcoin address the message was signed with</source>
- <translation>İletinin imzalanmasında kullanılan Bitcoin adresi</translation>
+ <translation>The Bitcoin address the message was signed with</translation>
</message>
<message>
<source>The signed message to verify</source>
@@ -2655,35 +2667,35 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
- <translation>Belirtilen Bitcoin adresi ile imzalandığını doğrulamak için iletiyi kontrol et</translation>
+ <translation>Verify the message to ensure it was signed with the specified Bitcoin address</translation>
</message>
<message>
<source>Verify &amp;Message</source>
- <translation>&amp;İletiyi kontrol et</translation>
+ <translation>Verify &amp;Message</translation>
</message>
<message>
<source>Reset all verify message fields</source>
- <translation>Tüm ileti kontrolü alanlarını sıfırla</translation>
+ <translation>Reset all verify message fields</translation>
</message>
<message>
<source>Click "Sign Message" to generate signature</source>
- <translation>İmzayı oluşturmak için "İletiyi İmzala"ya tıklayın</translation>
+ <translation>Click "Sign Message" to generate signature</translation>
</message>
<message>
<source>The entered address is invalid.</source>
- <translation>Girilen adres hatalı.</translation>
+ <translation>The entered address is invalid.</translation>
</message>
<message>
<source>Please check the address and try again.</source>
- <translation>Adresi kontrol ettikten sonra lütfen tekrar deneyin.</translation>
+ <translation>Please check the address and try again.</translation>
</message>
<message>
<source>The entered address does not refer to a key.</source>
- <translation>Girilen adres herhangi bir anahtara işaret etmemektedir.</translation>
+ <translation>The entered address does not refer to a key.</translation>
</message>
<message>
<source>Wallet unlock was cancelled.</source>
- <translation>Cüzdan kilidinin açılması iptal edildi.</translation>
+ <translation>Wallet unlock was cancelled.</translation>
</message>
<message>
<source>No error</source>
@@ -2691,35 +2703,35 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Private key for the entered address is not available.</source>
- <translation>Girilen adres için özel anahtar mevcut değildir.</translation>
+ <translation>Private key for the entered address is not available.</translation>
</message>
<message>
<source>Message signing failed.</source>
- <translation>İleti imzalaması başarısız oldu.</translation>
+ <translation>Message signing failed.</translation>
</message>
<message>
<source>Message signed.</source>
- <translation>İleti imzalandı.</translation>
+ <translation>Message signed.</translation>
</message>
<message>
<source>The signature could not be decoded.</source>
- <translation>İmzanın kodu çözülemedi.</translation>
+ <translation>The signature could not be decoded.</translation>
</message>
<message>
<source>Please check the signature and try again.</source>
- <translation>İmzanızı kontrol ettikten sonra lütfen tekrar deneyin.</translation>
+ <translation>Please check the signature and try again.</translation>
</message>
<message>
<source>The signature did not match the message digest.</source>
- <translation>İmza iletinin özeti ile eşleşmedi.</translation>
+ <translation>The signature did not match the message digest.</translation>
</message>
<message>
<source>Message verification failed.</source>
- <translation>Mesaj onayı hatalı.</translation>
+ <translation>Message verification failed.</translation>
</message>
<message>
<source>Message verified.</source>
- <translation>Mesaj onaylandı.</translation>
+ <translation>Message verified.</translation>
</message>
</context>
<context>
@@ -2733,135 +2745,135 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<name>TransactionDesc</name>
<message numerus="yes">
<source>Open for %n more block(s)</source>
- <translation><numerusform>%n taneden daha fazla blok için açık</numerusform><numerusform>%n taneden daha fazla blok için açık</numerusform></translation>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
</message>
<message>
<source>Open until %1</source>
- <translation>%1 değerine dek açık</translation>
+ <translation>Open until %1</translation>
</message>
<message>
<source>conflicted with a transaction with %1 confirmations</source>
- <translation>%1 doğrulamalı bir işlem ile çelişti</translation>
+ <translation>conflicted with a transaction with %1 confirmations</translation>
</message>
<message>
<source>0/unconfirmed, %1</source>
- <translation>0/doğrulanmamış, %1</translation>
+ <translation>0/unconfirmed, %1</translation>
</message>
<message>
<source>in memory pool</source>
- <translation>bellek alanında</translation>
+ <translation>in memory pool</translation>
</message>
<message>
<source>not in memory pool</source>
- <translation>bellek alanında değil</translation>
+ <translation>not in memory pool</translation>
</message>
<message>
<source>abandoned</source>
- <translation>terk edilmiş</translation>
+ <translation>abandoned</translation>
</message>
<message>
<source>%1/unconfirmed</source>
- <translation>%1/doğrulanmadı</translation>
+ <translation>%1/unconfirmed</translation>
</message>
<message>
<source>%1 confirmations</source>
- <translation>%1 doğrulama</translation>
+ <translation>%1 confirmations</translation>
</message>
<message>
<source>Status</source>
- <translation>Durum</translation>
+ <translation>Status</translation>
</message>
<message>
<source>Date</source>
- <translation>Tarih</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Source</source>
- <translation>Kaynak</translation>
+ <translation>Source</translation>
</message>
<message>
<source>Generated</source>
- <translation>Oluşturuldu</translation>
+ <translation>Generated</translation>
</message>
<message>
<source>From</source>
- <translation>Gönderen</translation>
+ <translation>From</translation>
</message>
<message>
<source>unknown</source>
- <translation>bilinmiyor</translation>
+ <translation>unknown</translation>
</message>
<message>
<source>To</source>
- <translation>Alıcı</translation>
+ <translation>To</translation>
</message>
<message>
<source>own address</source>
- <translation>kendi adresiniz</translation>
+ <translation>own address</translation>
</message>
<message>
<source>watch-only</source>
- <translation>sadece-izlenen</translation>
+ <translation>watch-only</translation>
</message>
<message>
<source>label</source>
- <translation>etiket</translation>
+ <translation>label</translation>
</message>
<message>
<source>Credit</source>
- <translation>Alınan Tutar</translation>
+ <translation>Credit</translation>
</message>
<message numerus="yes">
<source>matures in %n more block(s)</source>
- <translation><numerusform>%n ek blok sonrasında olgunlaşacak</numerusform><numerusform>%n ek blok sonrasında olgunlaşacak</numerusform></translation>
+ <translation><numerusform>matures in %n more block</numerusform><numerusform>matures in %n more blocks</numerusform></translation>
</message>
<message>
<source>not accepted</source>
- <translation>kabul edilmedi</translation>
+ <translation>not accepted</translation>
</message>
<message>
<source>Debit</source>
- <translation>Çekilen Tutar</translation>
+ <translation>Debit</translation>
</message>
<message>
<source>Total debit</source>
- <translation>Toplam çekilen tutar</translation>
+ <translation>Total debit</translation>
</message>
<message>
<source>Total credit</source>
- <translation>Toplam alınan tutar</translation>
+ <translation>Total credit</translation>
</message>
<message>
<source>Transaction fee</source>
- <translation>Gönderim ücreti</translation>
+ <translation>Transaction fee</translation>
</message>
<message>
<source>Net amount</source>
- <translation>Net tutar</translation>
+ <translation>Net amount</translation>
</message>
<message>
<source>Message</source>
- <translation>Mesaj</translation>
+ <translation>Message</translation>
</message>
<message>
<source>Comment</source>
- <translation>Yorum</translation>
+ <translation>Comment</translation>
</message>
<message>
<source>Transaction ID</source>
- <translation>İşlem ID'si</translation>
+ <translation>Transaction ID</translation>
</message>
<message>
<source>Transaction total size</source>
- <translation>Gönderimin toplam boyutu</translation>
+ <translation>Transaction total size</translation>
</message>
<message>
<source>Transaction virtual size</source>
- <translation>İşlem sanal boyutu</translation>
+ <translation>Transaction virtual size</translation>
</message>
<message>
<source>Output index</source>
- <translation>Çıktı indeksi</translation>
+ <translation>Output index</translation>
</message>
<message>
<source> (Certificate was not verified)</source>
@@ -2869,284 +2881,284 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Merchant</source>
- <translation>Tüccar</translation>
+ <translation>Merchant</translation>
</message>
<message>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
- <translation>Oluşturulan bitcoin'lerin harcanabilmelerinden önce %1 blok beklemeleri gerekmektedir. Bu blok, oluşturduğunuzda, blok zincirine eklenmesi için ağda yayınlandı. Zincire eklenmesi başarısız olursa, durumu "kabul edilmedi" olarak değiştirilecek ve harcanamayacaktır. Bu, bazen başka bir düğüm sizden birkaç saniye önce ya da sonra blok oluşturursa meydana gelebilir.</translation>
+ <translation>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</translation>
</message>
<message>
<source>Debug information</source>
- <translation>Hata giderme bilgisi</translation>
+ <translation>Debug information</translation>
</message>
<message>
<source>Transaction</source>
- <translation>İşlem</translation>
+ <translation>Transaction</translation>
</message>
<message>
<source>Inputs</source>
- <translation>Girdiler</translation>
+ <translation>Inputs</translation>
</message>
<message>
<source>Amount</source>
- <translation>Tutar</translation>
+ <translation>Amount</translation>
</message>
<message>
<source>true</source>
- <translation>doğru</translation>
+ <translation>true</translation>
</message>
<message>
<source>false</source>
- <translation>anlış</translation>
+ <translation>false</translation>
</message>
</context>
<context>
<name>TransactionDescDialog</name>
<message>
<source>This pane shows a detailed description of the transaction</source>
- <translation>Bu pano işlemin ayrıntılı açıklamasını gösterir</translation>
+ <translation>This pane shows a detailed description of the transaction</translation>
</message>
<message>
<source>Details for %1</source>
- <translation>%1 için ayrıntılar</translation>
+ <translation>Details for %1</translation>
</message>
</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
- <translation>Tarih</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Type</source>
- <translation>Tip</translation>
+ <translation>Type</translation>
</message>
<message>
<source>Label</source>
- <translation>etiket</translation>
+ <translation>Label</translation>
</message>
<message numerus="yes">
<source>Open for %n more block(s)</source>
- <translation><numerusform>%n taneden daha fazla blok için açık</numerusform><numerusform>%n taneden daha fazla blok için açık</numerusform></translation>
+ <translation><numerusform>Open for %n more block</numerusform><numerusform>Open for %n more blocks</numerusform></translation>
</message>
<message>
<source>Open until %1</source>
- <translation>%1 değerine dek açık</translation>
+ <translation>Open until %1</translation>
</message>
<message>
<source>Unconfirmed</source>
- <translation>Doğrulanmamış</translation>
+ <translation>Unconfirmed</translation>
</message>
<message>
<source>Abandoned</source>
- <translation>Terk edilmiş</translation>
+ <translation>Abandoned</translation>
</message>
<message>
<source>Confirming (%1 of %2 recommended confirmations)</source>
- <translation>Doğrulanıyor (%1 kere doğrulandı, önerilen doğrulama sayısı %2)</translation>
+ <translation>Confirming (%1 of %2 recommended confirmations)</translation>
</message>
<message>
<source>Confirmed (%1 confirmations)</source>
- <translation>Onaylandı (%1 onaylanan)</translation>
+ <translation>Confirmed (%1 confirmations)</translation>
</message>
<message>
<source>Conflicted</source>
- <translation>Uyuşmadı</translation>
+ <translation>Conflicted</translation>
</message>
<message>
<source>Immature (%1 confirmations, will be available after %2)</source>
- <translation>Olgunlaşmamış (%1 doğrulama, %2 doğrulama sonra kullanılabilir olacaktır)</translation>
+ <translation>Immature (%1 confirmations, will be available after %2)</translation>
</message>
<message>
<source>Generated but not accepted</source>
- <translation>Oluşturuldu fakat kabul edilmedi</translation>
+ <translation>Generated but not accepted</translation>
</message>
<message>
<source>Received with</source>
- <translation>ile alındı</translation>
+ <translation>Received with</translation>
</message>
<message>
<source>Received from</source>
- <translation>Alındığı kişi</translation>
+ <translation>Received from</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Gönderildiği adres</translation>
+ <translation>Sent to</translation>
</message>
<message>
<source>Payment to yourself</source>
- <translation>Kendinize ödeme</translation>
+ <translation>Payment to yourself</translation>
</message>
<message>
<source>Mined</source>
- <translation>Kazıldı</translation>
+ <translation>Mined</translation>
</message>
<message>
<source>watch-only</source>
- <translation>sadece-izlenen</translation>
+ <translation>watch-only</translation>
</message>
<message>
<source>(n/a)</source>
- <translation>(yok)</translation>
+ <translation>(n/a)</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(etiket yok)</translation>
+ <translation>(no label)</translation>
</message>
<message>
<source>Transaction status. Hover over this field to show number of confirmations.</source>
- <translation>İşlem durumu. Doğrulama sayısını görüntülemek için fare imlecini bu alanın üzerinde tutunuz.</translation>
+ <translation>Transaction status. Hover over this field to show number of confirmations.</translation>
</message>
<message>
<source>Date and time that the transaction was received.</source>
- <translation>İşlemin alındığı tarih ve zaman.</translation>
+ <translation>Date and time that the transaction was received.</translation>
</message>
<message>
<source>Type of transaction.</source>
- <translation>İşlemin türü.</translation>
+ <translation>Type of transaction.</translation>
</message>
<message>
<source>Whether or not a watch-only address is involved in this transaction.</source>
- <translation>Bu işleme sadece-izlenen bir adresin dahil edilip, edilmediği.</translation>
+ <translation>Whether or not a watch-only address is involved in this transaction.</translation>
</message>
<message>
<source>User-defined intent/purpose of the transaction.</source>
- <translation>İşlemin kullanıcı tanımlı amacı.</translation>
+ <translation>User-defined intent/purpose of the transaction.</translation>
</message>
<message>
<source>Amount removed from or added to balance.</source>
- <translation>Bakiyeden kaldırılan ya da bakiyeye eklenen tutar.</translation>
+ <translation>Amount removed from or added to balance.</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
- <translation>Hepsi</translation>
+ <translation>All</translation>
</message>
<message>
<source>Today</source>
- <translation>Bugün</translation>
+ <translation>Today</translation>
</message>
<message>
<source>This week</source>
- <translation>Bu hafta</translation>
+ <translation>This week</translation>
</message>
<message>
<source>This month</source>
- <translation>Bu Ay</translation>
+ <translation>This month</translation>
</message>
<message>
<source>Last month</source>
- <translation>Son ay</translation>
+ <translation>Last month</translation>
</message>
<message>
<source>This year</source>
- <translation>Bu yıl</translation>
+ <translation>This year</translation>
</message>
<message>
<source>Range...</source>
- <translation>Tarih Aralığı</translation>
+ <translation>Range...</translation>
</message>
<message>
<source>Received with</source>
- <translation>ile alındı</translation>
+ <translation>Received with</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Gönderildiği adres</translation>
+ <translation>Sent to</translation>
</message>
<message>
<source>To yourself</source>
- <translation>Kendinize</translation>
+ <translation>To yourself</translation>
</message>
<message>
<source>Mined</source>
- <translation>Kazıldı</translation>
+ <translation>Mined</translation>
</message>
<message>
<source>Other</source>
- <translation>Diğerleri</translation>
+ <translation>Other</translation>
</message>
<message>
<source>Enter address, transaction id, or label to search</source>
- <translation>Aramak için adres, gönderim numarası ya da etiket yazınız</translation>
+ <translation>Enter address, transaction id, or label to search</translation>
</message>
<message>
<source>Min amount</source>
- <translation>En düşük tutar</translation>
+ <translation>Min amount</translation>
</message>
<message>
<source>Abandon transaction</source>
- <translation>İşlemden vazgeç</translation>
+ <translation>Abandon transaction</translation>
</message>
<message>
<source>Increase transaction fee</source>
- <translation>İşlem ücretini artır</translation>
+ <translation>Increase transaction fee</translation>
</message>
<message>
<source>Copy address</source>
- <translation>Adresi kopyala</translation>
+ <translation>Copy address</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Etiketi kopyala</translation>
+ <translation>Copy label</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>Tutarı kopyala</translation>
+ <translation>Copy amount</translation>
</message>
<message>
<source>Copy transaction ID</source>
- <translation>İşlem ID'sini kopyala</translation>
+ <translation>Copy transaction ID</translation>
</message>
<message>
<source>Copy raw transaction</source>
- <translation>Ham işlemi kopyala</translation>
+ <translation>Copy raw transaction</translation>
</message>
<message>
<source>Copy full transaction details</source>
- <translation>Tüm işlem ayrıntılarını kopyala</translation>
+ <translation>Copy full transaction details</translation>
</message>
<message>
<source>Edit label</source>
- <translation>Etiketi düzenle</translation>
+ <translation>Edit label</translation>
</message>
<message>
<source>Show transaction details</source>
- <translation>İşlem ayrıntılarını göster</translation>
+ <translation>Show transaction details</translation>
</message>
<message>
<source>Export Transaction History</source>
- <translation>İşlem Tarihçesini Dışarı Aktar</translation>
+ <translation>Export Transaction History</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>Virgül ile ayrılmış dosya (*.csv)</translation>
+ <translation>Comma separated file (*.csv)</translation>
</message>
<message>
<source>Confirmed</source>
- <translation>Kabul edilen</translation>
+ <translation>Confirmed</translation>
</message>
<message>
<source>Watch-only</source>
- <translation>Sadece izlenen</translation>
+ <translation>Watch-only</translation>
</message>
<message>
<source>Date</source>
- <translation>Tarih</translation>
+ <translation>Date</translation>
</message>
<message>
<source>Type</source>
- <translation>Tip</translation>
+ <translation>Type</translation>
</message>
<message>
<source>Label</source>
- <translation>etiket</translation>
+ <translation>Label</translation>
</message>
<message>
<source>Address</source>
- <translation>adres</translation>
+ <translation>Address</translation>
</message>
<message>
<source>ID</source>
@@ -3154,41 +3166,41 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Exporting Failed</source>
- <translation>Dışa Aktarma Başarısız</translation>
+ <translation>Exporting Failed</translation>
</message>
<message>
<source>There was an error trying to save the transaction history to %1.</source>
- <translation>İşlem tarihçesinin %1 konumuna kaydedilmeye çalışıldığı sırada bir hata meydana geldi.</translation>
+ <translation>There was an error trying to save the transaction history to %1.</translation>
</message>
<message>
<source>Exporting Successful</source>
- <translation>Dışarı Aktarma Başarılı</translation>
+ <translation>Exporting Successful</translation>
</message>
<message>
<source>The transaction history was successfully saved to %1.</source>
- <translation>İşlem tarihçesi %1 konumuna başarıyla kaydedildi.</translation>
+ <translation>The transaction history was successfully saved to %1.</translation>
</message>
<message>
<source>Range:</source>
- <translation>Tarih Aralığı:</translation>
+ <translation>Range:</translation>
</message>
<message>
<source>to</source>
- <translation>Alıcı</translation>
+ <translation>to</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
<message>
<source>Unit to show amounts in. Click to select another unit.</source>
- <translation>Tutarı göstermek için birim. Başka bir birim seçmek için tıklayınız.</translation>
+ <translation>Unit to show amounts in. Click to select another unit.</translation>
</message>
</context>
<context>
<name>WalletController</name>
<message>
<source>Close wallet</source>
- <translation>Cüzdanı Kapat</translation>
+ <translation>Close wallet</translation>
</message>
<message>
<source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
@@ -3203,14 +3215,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<name>WalletFrame</name>
<message>
<source>No wallet has been loaded.</source>
- <translation>Hiçbir cüzdan yüklenmedi.</translation>
+ <translation>No wallet has been loaded.</translation>
</message>
</context>
<context>
<name>WalletModel</name>
<message>
<source>Send Coins</source>
- <translation>Coin gönder</translation>
+ <translation>Send Coins</translation>
</message>
<message>
<source>Fee bump error</source>
@@ -3218,11 +3230,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Increasing transaction fee failed</source>
- <translation>İşlem ücreti artırma başarısız oldu</translation>
+ <translation>Increasing transaction fee failed</translation>
</message>
<message>
<source>Do you want to increase the fee?</source>
- <translation>Ücreti artırmak istiyor musunuz?</translation>
+ <translation>Do you want to increase the fee?</translation>
</message>
<message>
<source>Do you want to draft a transaction with fee increase?</source>
@@ -3230,15 +3242,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Current fee:</source>
- <translation>Şimdiki ücret:</translation>
+ <translation>Current fee:</translation>
</message>
<message>
<source>Increase:</source>
- <translation>Artış:</translation>
+ <translation>Increase:</translation>
</message>
<message>
<source>New fee:</source>
- <translation>Yeni ücret:</translation>
+ <translation>New fee:</translation>
</message>
<message>
<source>Confirm fee bump</source>
@@ -3254,85 +3266,85 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Can't sign transaction.</source>
- <translation>İşlem imzalanamıyor.</translation>
+ <translation>Can't sign transaction.</translation>
</message>
<message>
<source>Could not commit transaction</source>
- <translation>Alışveriş taahüt edilemedi.</translation>
+ <translation>Could not commit transaction</translation>
</message>
<message>
<source>default wallet</source>
- <translation>varsayılan cüzdan</translation>
+ <translation>default wallet</translation>
</message>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
- <translation>&amp;Çıkar</translation>
+ <translation>&amp;Export</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Mevcut sekmedeki verileri bir dosyaya aktar</translation>
+ <translation>Export the data in the current tab to a file</translation>
</message>
<message>
<source>Backup Wallet</source>
- <translation>Cüzdanı yedekle</translation>
+ <translation>Backup Wallet</translation>
</message>
<message>
<source>Wallet Data (*.dat)</source>
- <translation>Cüzdan Verileri (*.dat)</translation>
+ <translation>Wallet Data (*.dat)</translation>
</message>
<message>
<source>Backup Failed</source>
- <translation>Yedekleme başarısız</translation>
+ <translation>Backup Failed</translation>
</message>
<message>
<source>There was an error trying to save the wallet data to %1.</source>
- <translation>Cüzdan verilerinin %1 konumuna kaydedilmesi sırasında bir hata meydana geldi.</translation>
+ <translation>There was an error trying to save the wallet data to %1.</translation>
</message>
<message>
<source>Backup Successful</source>
- <translation>Yedekleme tamamlandı</translation>
+ <translation>Backup Successful</translation>
</message>
<message>
<source>The wallet data was successfully saved to %1.</source>
- <translation>Cüzdan verileri %1 konumuna başarıyla kaydedildi.</translation>
+ <translation>The wallet data was successfully saved to %1.</translation>
</message>
<message>
<source>Cancel</source>
- <translation>İptal</translation>
+ <translation>Cancel</translation>
</message>
</context>
<context>
<name>bitcoin-core</name>
<message>
<source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
- <translation>MIT yazılım lisansı altında dağıtılmıştır, beraberindeki %s ya da %s dosyasına bakınız.</translation>
+ <translation>Distributed under the MIT software license, see the accompanying file %s or %s</translation>
</message>
<message>
<source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
- <translation>Budama, en düşük değer olan %d MiB'den düşük olarak ayarlanmıştır. Lütfen daha yüksek bir sayı kullanınız.</translation>
+ <translation>Prune configured below the minimum of %d MiB. Please use a higher number.</translation>
</message>
<message>
<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>Budama: son cüzdan eşleşmesi budanmış verilerin ötesine gitmektedir. -reindex kullanmanız gerekmektedir (Budanmış düğüm ise tüm blok zincirini tekrar indirmeniz gerekir.)</translation>
+ <translation>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</translation>
</message>
<message>
<source>Error: A fatal internal error occurred, see debug.log for details</source>
- <translation>Hata: Ölümcül dahili bir hata meydana geldi, ayrıntılar için debug.log dosyasına bakınız</translation>
+ <translation>Error: A fatal internal error occurred, see debug.log for details</translation>
</message>
<message>
<source>Pruning blockstore...</source>
- <translation>Blockstore budanıyor...</translation>
+ <translation>Pruning blockstore...</translation>
</message>
<message>
<source>Unable to start HTTP server. See debug log for details.</source>
- <translation>HTTP sunucusu başlatılamadı. Ayrıntılar için debug.log dosyasına bakınız.</translation>
+ <translation>Unable to start HTTP server. See debug log for details.</translation>
</message>
<message>
<source>The %s developers</source>
- <translation>%s ekip</translation>
+ <translation>The %s developers</translation>
</message>
<message>
<source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
@@ -3340,7 +3352,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
- <translation>%s veri dizininde kilit elde edilemedi. %s muhtemelen hâlihazırda çalışmaktadır.</translation>
+ <translation>Cannot obtain a lock on data directory %s. %s is probably already running.</translation>
</message>
<message>
<source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
@@ -3348,23 +3360,23 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
- <translation>%s dosyasının okunması sırasında bir hata meydana geldi! Tüm anahtarlar doğru bir şekilde okundu, ancak işlem verileri ya da adres defteri ögeleri hatalı veya eksik olabilir.</translation>
+ <translation>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation>
</message>
<message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
- <translation>Lütfen bilgisayarınızın saat ve tarihinin doğru olduğunu kontrol ediniz! Saatinizde gecikme varsa %s doğru şekilde çalışamaz.</translation>
+ <translation>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</translation>
</message>
<message>
<source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
- <translation>%s programını faydalı buluyorsanız lütfen katkıda bulununuz. Yazılım hakkında daha fazla bilgi için %s adresini ziyaret ediniz.</translation>
+ <translation>Please contribute if you find %s useful. Visit %s for further information about the software.</translation>
</message>
<message>
<source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
- <translation>Blok veritabanı gelecekten gibi görünen bir blok içermektedir. Bu, bilgisayarınızın saat ve tarihinin yanlış ayarlanmış olmasından kaynaklanabilir. Blok veritabanını sadece bilgisayarınızın tarih ve saatinin doğru olduğundan eminseniz yeniden derleyin.</translation>
+ <translation>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</translation>
</message>
<message>
<source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
- <translation>Bu kararlı sürümden önceki bir deneme sürümüdür. - risklerini bilerek kullanma sorumluluğu sizdedir - bitcoin oluşturmak ya da ticari uygulamalar için kullanmayınız</translation>
+ <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation>
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
@@ -3376,35 +3388,35 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
- <translation>Veritabanını çatallama öncesi duruma geri sarmak mümkün değil. Blok zincirini tekrar indirmeniz gerekmektedir</translation>
+ <translation>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</translation>
</message>
<message>
<source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
- <translation>Uyarı: Ağ üyeleri aralarında tamamen anlaşmış gibi gözükmüyor! Bazı madenciler sorun yaşıyor gibi görünmektedir.</translation>
+ <translation>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</translation>
</message>
<message>
<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>Uyarı: Ağ eşlerimizle tamamen anlaşamamışız gibi görünüyor! Güncelleme yapmanız gerekebilir ya da diğer düğümlerin güncelleme yapmaları gerekebilir.</translation>
+ <translation>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</translation>
</message>
<message>
<source>%d of last 100 blocks have unexpected version</source>
- <translation>son 100 bloğun %d kadarı beklenmeyen versiyona sahip</translation>
+ <translation>%d of last 100 blocks have unexpected version</translation>
</message>
<message>
<source>%s corrupt, salvage failed</source>
- <translation>%s bozuk, geri kazanım başarısız oldu</translation>
+ <translation>%s corrupt, salvage failed</translation>
</message>
<message>
<source>-maxmempool must be at least %d MB</source>
- <translation>-maxmempool en az %d MB olmalıdır</translation>
+ <translation>-maxmempool must be at least %d MB</translation>
</message>
<message>
<source>Cannot resolve -%s address: '%s'</source>
- <translation>Çözümlenemedi - %s adres: '%s'</translation>
+ <translation>Cannot resolve -%s address: '%s'</translation>
</message>
<message>
<source>Change index out of range</source>
- <translation>Aralık dışında değişiklik indeksi</translation>
+ <translation>Change index out of range</translation>
</message>
<message>
<source>Config setting for %s only applied on %s network when in [%s] section.</source>
@@ -3416,7 +3428,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Corrupted block database detected</source>
- <translation>Bozuk blok veritabanı tespit edildi</translation>
+ <translation>Corrupted block database detected</translation>
</message>
<message>
<source>Could not find asmap file %s</source>
@@ -3428,60 +3440,59 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Do you want to rebuild the block database now?</source>
- <translation>Blok veritabanını şimdi yeniden inşa etmek istiyor musunuz?</translation>
+ <translation>Do you want to rebuild the block database now?</translation>
</message>
<message>
<source>Error initializing block database</source>
- <translation>Blok veritabanını başlatılırken bir hata meydana geldi</translation>
+ <translation>Error initializing block database</translation>
</message>
<message>
<source>Error initializing wallet database environment %s!</source>
- <translation>%s cüzdan veritabanı ortamının başlatılmasında hata meydana geldi!</translation>
+ <translation>Error initializing wallet database environment %s!</translation>
</message>
<message>
<source>Error loading %s</source>
- <translation>%s unsurunun yüklenmesinde hata oluştu</translation>
+ <translation>Error loading %s</translation>
</message>
<message>
<source>Error loading %s: Private keys can only be disabled during creation</source>
- <translation>%s yüklenirken hata oluştu: Özel anahtarlar yalnızca oluşturma sırasında devre dışı bırakılabilir
- </translation>
+ <translation>Error loading %s: Private keys can only be disabled during creation</translation>
</message>
<message>
<source>Error loading %s: Wallet corrupted</source>
- <translation>%s unsurunun yüklenmesinde hata oluştu: bozuk cüzdan</translation>
+ <translation>Error loading %s: Wallet corrupted</translation>
</message>
<message>
<source>Error loading %s: Wallet requires newer version of %s</source>
- <translation>%s unsurunun yüklenmesinde hata oluştu: cüzdan %s programının yeni bir sürümüne ihtiyaç duyuyor</translation>
+ <translation>Error loading %s: Wallet requires newer version of %s</translation>
</message>
<message>
<source>Error loading block database</source>
- <translation>Blok veritabanının yüklenmesinde hata</translation>
+ <translation>Error loading block database</translation>
</message>
<message>
<source>Error opening block database</source>
- <translation>Blok veritabanının açılışı sırasında hata</translation>
+ <translation>Error opening block database</translation>
</message>
<message>
<source>Failed to listen on any port. Use -listen=0 if you want this.</source>
- <translation>Herhangi bir portun dinlenmesi başarısız oldu. Bunu istiyorsanız -listen=0 seçeneğini kullanınız.</translation>
+ <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation>
</message>
<message>
<source>Failed to rescan the wallet during initialization</source>
- <translation>Başlatma sırasında cüzdanı yeniden tarama işlemi başarısız oldu</translation>
+ <translation>Failed to rescan the wallet during initialization</translation>
</message>
<message>
<source>Importing...</source>
- <translation>İçe aktarılıyor...</translation>
+ <translation>Importing...</translation>
</message>
<message>
<source>Incorrect or no genesis block found. Wrong datadir for network?</source>
- <translation>Yanlış ya da bulunamamış doğuş bloğu. Ağ için yanlış veri klasörü mü?</translation>
+ <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
</message>
<message>
<source>Initialization sanity check failed. %s is shutting down.</source>
- <translation>Başlatma sınaması başarısız oldu. %s kapatılıyor.</translation>
+ <translation>Initialization sanity check failed. %s is shutting down.</translation>
</message>
<message>
<source>Invalid P2P permission: '%s'</source>
@@ -3489,15 +3500,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
- <translation>-%s=&lt;tutar&gt; için geçersiz tutar: '%s'</translation>
+ <translation>Invalid amount for -%s=&lt;amount&gt;: '%s'</translation>
</message>
<message>
<source>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</source>
- <translation>Geçersiz miktarda -discardfee=&lt;amount&gt;:'%s'</translation>
+ <translation>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
<source>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</source>
- <translation>-fallbackfee=&lt;tutar&gt; için geçersiz tutar: '%s'</translation>
+ <translation>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
<source>Specified blocks directory "%s" does not exist.</source>
@@ -3505,7 +3516,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unknown address type '%s'</source>
- <translation>Bilinmeyen adres türü '%s'</translation>
+ <translation>Unknown address type '%s'</translation>
</message>
<message>
<source>Unknown change type '%s'</source>
@@ -3513,11 +3524,11 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Upgrading txindex database</source>
- <translation>txindex veritabanı yükseltiliyor</translation>
+ <translation>Upgrading txindex database</translation>
</message>
<message>
<source>Loading P2P addresses...</source>
- <translation>P2P adresleri yükleniyor...</translation>
+ <translation>Loading P2P addresses...</translation>
</message>
<message>
<source>Error: Disk space is too low!</source>
@@ -3525,87 +3536,87 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Loading banlist...</source>
- <translation>Ban listesi yükleniyor...</translation>
+ <translation>Loading banlist...</translation>
</message>
<message>
<source>Not enough file descriptors available.</source>
- <translation>Kafi derecede dosya tanımlayıcıları mevcut değil.</translation>
+ <translation>Not enough file descriptors available.</translation>
</message>
<message>
<source>Prune cannot be configured with a negative value.</source>
- <translation>Budama negatif bir değerle yapılandırılamaz.</translation>
+ <translation>Prune cannot be configured with a negative value.</translation>
</message>
<message>
<source>Prune mode is incompatible with -txindex.</source>
- <translation>Budama kipi -txindex ile uyumsuzdur.</translation>
+ <translation>Prune mode is incompatible with -txindex.</translation>
</message>
<message>
<source>Replaying blocks...</source>
- <translation>Bloklar tekrar işleniyor...</translation>
+ <translation>Replaying blocks...</translation>
</message>
<message>
<source>Rewinding blocks...</source>
- <translation>Bloklar geri sarılıyor...</translation>
+ <translation>Rewinding blocks...</translation>
</message>
<message>
<source>The source code is available from %s.</source>
- <translation>Kaynak kod şuradan elde edilebilir: %s.</translation>
+ <translation>The source code is available from %s.</translation>
</message>
<message>
<source>Transaction fee and change calculation failed</source>
- <translation>İşlem ücreti ve para üstü hesaplamasında hata meydana geldi.</translation>
+ <translation>Transaction fee and change calculation failed</translation>
</message>
<message>
<source>Unable to bind to %s on this computer. %s is probably already running.</source>
- <translation>Bu bilgisayarda %s unsuruna bağlanılamadı. %s muhtemelen hâlihazırda çalışmaktadır.</translation>
+ <translation>Unable to bind to %s on this computer. %s is probably already running.</translation>
</message>
<message>
<source>Unable to generate keys</source>
- <translation>Anahtar üretilemiyor</translation>
+ <translation>Unable to generate keys</translation>
</message>
<message>
<source>Unsupported logging category %s=%s.</source>
- <translation>Desteklenmeyen günlük kategorisi %s=%s.</translation>
+ <translation>Unsupported logging category %s=%s.</translation>
</message>
<message>
<source>Upgrading UTXO database</source>
- <translation>UTXO veritabanı yükseltiliyor</translation>
+ <translation>Upgrading UTXO database</translation>
</message>
<message>
<source>User Agent comment (%s) contains unsafe characters.</source>
- <translation>Kullanıcı Aracı açıklaması (%s) güvensiz karakterler içermektedir.</translation>
+ <translation>User Agent comment (%s) contains unsafe characters.</translation>
</message>
<message>
<source>Verifying blocks...</source>
- <translation>Bloklar Onaylanıyor...</translation>
+ <translation>Verifying blocks...</translation>
</message>
<message>
<source>Wallet needed to be rewritten: restart %s to complete</source>
- <translation>%s tamamlanması için cüzdanın yeniden başlatılması gerekiyor</translation>
+ <translation>Wallet needed to be rewritten: restart %s to complete</translation>
</message>
<message>
<source>Error: Listening for incoming connections failed (listen returned error %s)</source>
- <translation>Hata: İçeri gelen bağlantıların dinlenmesi başarısız oldu (dinleme %s hatasını verdi)</translation>
+ <translation>Error: Listening for incoming connections failed (listen returned error %s)</translation>
</message>
<message>
<source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
- <translation>-maxtxfee=&lt;tutar&gt; için geçersiz tutar: '%s' (Sıkışmış işlemleri önlemek için en az %s değerinde en düşük aktarım ücretine eşit olmalıdır)</translation>
+ <translation>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</translation>
</message>
<message>
<source>The transaction amount is too small to send after the fee has been deducted</source>
- <translation>Bu işlem, tutar düşüldükten sonra göndermek için çok düşük</translation>
+ <translation>The transaction amount is too small to send after the fee has been deducted</translation>
</message>
<message>
<source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
- <translation>Budama olmayan kipe dönmek için veritabanını -reindex ile tekrar derlemeniz gerekir. Bu, tüm blok zincirini tekrar indirecektir</translation>
+ <translation>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</translation>
</message>
<message>
<source>Error reading from database, shutting down.</source>
- <translation>Veritabanı okuma hatası, kapatıldı.</translation>
+ <translation>Error reading from database, shutting down.</translation>
</message>
<message>
<source>Error upgrading chainstate database</source>
- <translation>Zincirdurumu veritabanı yükseltme hatası</translation>
+ <translation>Error upgrading chainstate database</translation>
</message>
<message>
<source>Error: Disk space is low for %s</source>
@@ -3613,23 +3624,23 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Invalid -onion address or hostname: '%s'</source>
- <translation>Hatalı -onion adresi ya da host adı: '%s'</translation>
+ <translation>Invalid -onion address or hostname: '%s'</translation>
</message>
<message>
<source>Invalid -proxy address or hostname: '%s'</source>
- <translation>Geçersiz -proxy adresi veya ana makine adı: '%s'</translation>
+ <translation>Invalid -proxy address or hostname: '%s'</translation>
</message>
<message>
<source>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</source>
- <translation>-paytxfee=&lt;tutar&gt;:'%s' unsurunda geçersiz tutar (asgari %s olması lazımdır)</translation>
+ <translation>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</translation>
</message>
<message>
<source>Invalid netmask specified in -whitelist: '%s'</source>
- <translation>-whitelist: '%s' unsurunda geçersiz bir ağ maskesi belirtildi</translation>
+ <translation>Invalid netmask specified in -whitelist: '%s'</translation>
</message>
<message>
<source>Need to specify a port with -whitebind: '%s'</source>
- <translation>-whitebind: '%s' ile bir port belirtilmesi lazımdır</translation>
+ <translation>Need to specify a port with -whitebind: '%s'</translation>
</message>
<message>
<source>Prune mode is incompatible with -blockfilterindex.</source>
@@ -3637,7 +3648,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
- <translation>Sistem sınırlamaları sebebiyle -maxconnections %d değerinden %d değerine düşürülmüştür.</translation>
+ <translation>Reducing -maxconnections from %d to %d, because of system limitations.</translation>
</message>
<message>
<source>Section [%s] is not recognized.</source>
@@ -3645,19 +3656,19 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Signing transaction failed</source>
- <translation>İşlemin imzalanması başarısız oldu</translation>
+ <translation>Signing transaction failed</translation>
</message>
<message>
<source>Specified -walletdir "%s" does not exist</source>
- <translation>Belirtilen -walletdir "%s" mevcut değil</translation>
+ <translation>Specified -walletdir "%s" does not exist</translation>
</message>
<message>
<source>Specified -walletdir "%s" is a relative path</source>
- <translation>Belirtilen -walletdir "%s" göreceli bir yoldur</translation>
+ <translation>Specified -walletdir "%s" is a relative path</translation>
</message>
<message>
<source>Specified -walletdir "%s" is not a directory</source>
- <translation>Belirtilen -walletdir "%s" bir dizin değildir</translation>
+ <translation>Specified -walletdir "%s" is not a directory</translation>
</message>
<message>
<source>The specified config file %s does not exist
@@ -3667,23 +3678,23 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>The transaction amount is too small to pay the fee</source>
- <translation>İşlemdeki bitcoin tutarı ücreti ödemek için çok düşük</translation>
+ <translation>The transaction amount is too small to pay the fee</translation>
</message>
<message>
<source>This is experimental software.</source>
- <translation>Bu deneysel bir yazılımdır.</translation>
+ <translation>This is experimental software.</translation>
</message>
<message>
<source>Transaction amount too small</source>
- <translation>İşlem tutarı çok düşük</translation>
+ <translation>Transaction amount too small</translation>
</message>
<message>
<source>Transaction too large</source>
- <translation>İşlem çok büyük</translation>
+ <translation>Transaction too large</translation>
</message>
<message>
<source>Unable to bind to %s on this computer (bind returned error %s)</source>
- <translation>Bu bilgisayarda %s ögesine bağlanılamadı (bağlanma %s hatasını verdi)</translation>
+ <translation>Unable to bind to %s on this computer (bind returned error %s)</translation>
</message>
<message>
<source>Unable to create the PID file '%s': %s</source>
@@ -3691,7 +3702,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unable to generate initial keys</source>
- <translation>Başlangıç anahtarları üretilemiyor</translation>
+ <translation>Unable to generate initial keys</translation>
</message>
<message>
<source>Unknown -blockfilterindex value %s.</source>
@@ -3699,75 +3710,75 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Verifying wallet(s)...</source>
- <translation>Cüzdan(lar) onaylanıyor...</translation>
+ <translation>Verifying wallet(s)...</translation>
</message>
<message>
<source>Warning: unknown new rules activated (versionbit %i)</source>
- <translation>Uyarı: bilinmeyen yeni kurallar etkinleştirilmiştir (versionbit %i)</translation>
+ <translation>Warning: unknown new rules activated (versionbit %i)</translation>
</message>
<message>
<source>Zapping all transactions from wallet...</source>
- <translation>Cüzdandaki tüm işlemler kaldırılıyor...</translation>
+ <translation>Zapping all transactions from wallet...</translation>
</message>
<message>
<source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
- <translation>-maxtxfee çok yüksek bir değere ayarlanmış! Bu denli yüksek ücretler tek bir işlemde ödenebilir.</translation>
+ <translation>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</translation>
</message>
<message>
<source>This is the transaction fee you may pay when fee estimates are not available.</source>
- <translation>İşlem ücret tahminleri mevcut olmadığında ödeyebileceğiniz işlem ücreti budur.</translation>
+ <translation>This is the transaction fee you may pay when fee estimates are not available.</translation>
</message>
<message>
<source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
- <translation>Ağ sürümü zincirinin toplam boyutu (%i) en yüksek boyutu geçmektedir (%i). Kullanıcı aracı açıklamasının sayısı veya boyutunu azaltınız.</translation>
+ <translation>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</translation>
</message>
<message>
<source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
- <translation>Uyarı: wallet.dat bozuk, veriler geri kazanıldı! Özgün %s, %s olarak %s klasörüne kaydedildi; bakiyeniz ya da işlemleriniz yanlışsa bir yedeklemeden tekrar yüklemeniz gerekir.</translation>
+ <translation>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</translation>
</message>
<message>
<source>%s is set very high!</source>
- <translation>Ayarlanan %s çok yüksek!</translation>
+ <translation>%s is set very high!</translation>
</message>
<message>
<source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
- <translation>%s cüzdanı yüklenirken hata oluştu. Belirtilen -wallet dosya adında başka bir kopya daha var.</translation>
+ <translation>Error loading wallet %s. Duplicate -wallet filename specified.</translation>
</message>
<message>
<source>Starting network threads...</source>
- <translation>Bağlantı konuları başlıyor</translation>
+ <translation>Starting network threads...</translation>
</message>
<message>
<source>The wallet will avoid paying less than the minimum relay fee.</source>
- <translation>Cüzdan minimum değişim ücretinden daha düşük olan ödemeyi önleyecektir</translation>
+ <translation>The wallet will avoid paying less than the minimum relay fee.</translation>
</message>
<message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
- <translation>Her işlem için minimum işlem ücretiniz budur</translation>
+ <translation>This is the minimum transaction fee you pay on every transaction.</translation>
</message>
<message>
<source>This is the transaction fee you will pay if you send a transaction.</source>
- <translation>Bir işlem göndermeniz durumunda işlem ücretiniz budur</translation>
+ <translation>This is the transaction fee you will pay if you send a transaction.</translation>
</message>
<message>
<source>Transaction amounts must not be negative</source>
- <translation>İşlem miktarı negatif olmamalı</translation>
+ <translation>Transaction amounts must not be negative</translation>
</message>
<message>
<source>Transaction has too long of a mempool chain</source>
- <translation>İşlem çok uzun bir bellek havuzu zincirine sahip</translation>
+ <translation>Transaction has too long of a mempool chain</translation>
</message>
<message>
<source>Transaction must have at least one recipient</source>
- <translation>İşlemin en az bir alıcıya sahip olmalı</translation>
+ <translation>Transaction must have at least one recipient</translation>
</message>
<message>
<source>Unknown network specified in -onlynet: '%s'</source>
- <translation>Belirsiz ağ belirtildi -onlynet: '%s'</translation>
+ <translation>Unknown network specified in -onlynet: '%s'</translation>
</message>
<message>
<source>Insufficient funds</source>
- <translation>Yetersiz Bakiye</translation>
+ <translation>Insufficient funds</translation>
</message>
<message>
<source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
@@ -3775,7 +3786,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
- <translation>İşlem ücreti hesaplama başarısız. Fallbackfee özelliği devre dışı. Lütfen bir kaç blok için bekleyiniz yada -fallbackfee özelliğini aktif ediniz.</translation>
+ <translation>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</translation>
</message>
<message>
<source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
@@ -3783,27 +3794,27 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Cannot write to data directory '%s'; check permissions.</source>
- <translation>Veriler klasöre yazılamıyor '%s'; yetkilendirmeyi kontrol edin.</translation>
+ <translation>Cannot write to data directory '%s'; check permissions.</translation>
</message>
<message>
<source>Loading block index...</source>
- <translation>Blok indeksi yükleniyor</translation>
+ <translation>Loading block index...</translation>
</message>
<message>
<source>Loading wallet...</source>
- <translation>Cüzdan Bekleniyor...</translation>
+ <translation>Loading wallet...</translation>
</message>
<message>
<source>Cannot downgrade wallet</source>
- <translation>Cüzdan indirgenememektedir</translation>
+ <translation>Cannot downgrade wallet</translation>
</message>
<message>
<source>Rescanning...</source>
- <translation>Tekrar taranıyor...</translation>
+ <translation>Rescanning...</translation>
</message>
<message>
<source>Done loading</source>
- <translation>Yükleme tamamlandı</translation>
+ <translation>Done loading</translation>
</message>
</context>
</TS> \ No newline at end of file
diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts
index 78570e8bbf..56c37a74dd 100644
--- a/src/qt/locale/bitcoin_he.ts
+++ b/src/qt/locale/bitcoin_he.ts
@@ -141,7 +141,7 @@
</message>
<message>
<source>Encrypt wallet</source>
- <translation>הצפנת הארנק</translation>
+ <translation>הצפן ארנק</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
@@ -180,6 +180,31 @@
<translation>הארנק מוצפן</translation>
</message>
<message>
+ <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>הקש סיסמה חדשה לארנק.
+השתמש בסיסמה הכוללת עשרה או יותר תווים אקראים, או שמונה או יותר מילים.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>הקש את הסיסמא הישנה והחדשה לארנק.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>זכור שהצפנת הארנק לא יכולה להגן עליך לגמרי מגניבת המטבעות שלך על ידי תוכנה זדונית שנמצאת על המחשב שלך.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>הארנק המיועד להצפנה</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>הארנק שלך עומד להיות מוצפן/</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>הארנק שלך מוצפן כעת/</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>חשוב! כל גיבוי קודם שעשית לארנק שלך יש להחליף עם קובץ הארנק המוצפן שזה עתה נוצר. מסיבות אבטחה, גיבויים קודמים של קובץ הארנק הלא-מוצפן יהפכו לחסרי שימוש ברגע שתתחיל להשתמש בארנק החדש המוצפן.</translation>
</message>
@@ -302,6 +327,14 @@
<translation>פתיחת &amp;כתובת משאב…</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>צור ארנק...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>צור ארנק חדש</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>ארנק:</translation>
</message>
@@ -431,7 +464,7 @@
</message>
<message>
<source>Transactions after this will not yet be visible.</source>
- <translation>ההעברות שבוצעו לאחר העברה זו לא יופיעו.</translation>
+ <translation>עסקאות שבוצעו לאחר העברה זו לא יופיעו.</translation>
</message>
<message>
<source>Error</source>
@@ -450,6 +483,14 @@
<translation>עדכני</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>חלון צומת</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>פתיחת ניפוי באגים בצומת וגם מסוף בקרה לאבחון</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;כתובות למשלוח</translation>
</message>
@@ -458,6 +499,10 @@
<translation>&amp;כתובות לקבלה</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>פתיחת ביטקוין: כתובת משאב</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>פתיחת ארנק</translation>
</message>
@@ -670,7 +715,7 @@
</message>
<message>
<source>Copy transaction ID</source>
- <translation>העתקת מזהה ההעברה</translation>
+ <translation>העתקת מזהה העסקה</translation>
</message>
<message>
<source>Lock unspent</source>
@@ -739,10 +784,46 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>יצירת הארנק נכשלה</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>אזהרה לגבי יצירת הארנק</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>צור ארנק.</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>שם הארנק</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>הצפן את הארנק. הארנק יהיה מוצפן באמצעות סיסמא לבחירתך.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>הצפנת ארנק</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>השבתת מפתחות פרטיים</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>צור ארנק ריק</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>צור</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -954,6 +1035,10 @@
<translation>הסתר</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>לא ידוע. סינכרון כותרות (%1, %2%)...</translation>
</message>
@@ -961,6 +1046,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>פתיחת כתובת משאב ביטקוין</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>כתובת משאב:</translation>
</message>
@@ -968,6 +1057,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>פתיחת ארנק נכשלה</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>אזהרת פתיחת ארנק</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>ארנק בררת מחדל</translation>
</message>
@@ -1004,7 +1101,7 @@
</message>
<message>
<source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
- <translation>כתובת ה־IP של המתווך (לדוגמה IPv4: 127.0.0.1‏ / IPv6: ::1)</translation>
+ <translation>כתובת ה־IP של הפרוקסי (לדוגמה IPv4: 127.0.0.1‏ / IPv6: ::1)</translation>
</message>
<message>
<source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
@@ -1506,6 +1603,10 @@
<translation>שגיאה בקידוד ה URI לברקוד.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>תמיכה בקוד QR לא זמינה.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>שמירת קוד QR</translation>
</message>
@@ -1645,10 +1746,18 @@
<translation>בלוקים מסונכרנים</translation>
</message>
<message>
+ <source>Mapped AS</source>
+ <translation>מופה בתור</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>סוכן משתמש</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>חלון צומת</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>פתחו את לוג ניפוי השגיאות ה%1 מתיקיית הנתונים הנוכחית. עבור קבצי לוג גדולים ייתכן זמן המתנה של מספר שניות.</translation>
</message>
@@ -1798,7 +1907,7 @@
</message>
<message>
<source>(node id: %1)</source>
- <translation>(node id: %1)</translation>
+ <translation>(מזהה צומת: %1)</translation>
</message>
<message>
<source>via %1</source>
@@ -1860,6 +1969,10 @@
<translation>סכום כרשות לבקשה. ניתן להשאיר זאת ריק כדי לא לבקש סכום מסוים.</translation>
</message>
<message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;יצירת כתובת קבלה חדשה</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>ניקוי של כל השדות בטופס.</translation>
</message>
@@ -2109,6 +2222,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>אבק:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>הסתרת הגדרות עמלת עסקה</translation>
+ </message>
+ <message>
<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>כאשר יש פחות נפח עסקאות מאשר מקום בבלוק, כורים וכן צמתות מקשרות יכולות להכתיב עמלות מינימום. התשלום של עמלת מינימום הנו תקין, אך יש לקחת בחשבון שהדבר יכול לגרום לעסקה שלא תאושר ברגע שיש יותר ביקוש לעסקאות ביטקוין מאשר הרשת יכולה לעבד.</translation>
</message>
@@ -2177,10 +2294,30 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%1 (%2 בלוקים)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>יצ&amp;ירת לא חתום</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>יוצר עסקת ביטקוין חתומה חלקית (PSBT) לשימוש עם ארנק %1 לא מחובר למשל, או עם PSBT ארנק חומרה תואם.</translation>
+ </message>
+ <message>
+ <source> from wallet '%1'</source>
+ <translation>מתוך ארנק '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1 אל '%2'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 ל %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>האם ברצונך לשמור עסקה זו כטיוטה?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>לשלוח?</translation>
</message>
@@ -2213,6 +2350,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>אימות שליחת מטבעות</translation>
</message>
<message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>העתקת PSBT אל לוח הגזירים</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>שלח</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT הועתקה</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>יתרת צפייה-בלבד</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>כתובת הנמען שגויה. נא לבדוק שוב.</translation>
</message>
@@ -2434,6 +2587,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>כתובת הביטקוין שאתה נחתמה ההודעה</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>ההודעה החתומה לאימות</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>ניתן לאמת את ההודעה כדי להבטיח שהיא נחתמה עם כתובת הביטקוין הנתונה</translation>
</message>
@@ -2466,6 +2623,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>שחרור הארנק בוטל.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>אין שגיאה</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>המפתח הפרטי לכתובת שהוכנסה אינו זמין.</translation>
</message>
@@ -2640,12 +2801,16 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>מפתח פלט</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(האישור לא אומת)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>סוחר</translation>
</message>
<message>
<source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
- <translation>מטבעות מופקים חייבים להבשיל במשך %1 בלוקים לפני שניתן לבזבזם. כשהפקתם בלוק זה, הבלוק שודר לרשת לצורך הוספה לבלוקצ'יין. אם הבלוק לא יתווסף לבלוקצ'יין, מצב הבלוק ישונה ל "לא התקבל" ולא יהיה ניתן לבזבזו. מצב זה עלול לקרות כאשר שרת ביטקוין אחר מפיק בלוק בהפרש של כמה שניות משלכם.</translation>
+ <translation>מטבעות מופקים חייבים להבשיל במשך %1 בלוקים לפני שניתן לבזבזם. כשהפקתם בלוק זה, הבלוק שודר לרשת לצורך הוספה לבלוקצ'יין. אם הבלוק לא יתווסף לבלוקצ'יין, מצב הבלוק ישונה ל"לא התקבל" ולא יהיה ניתן לבזבזו. מצב זה עלול לקרות כאשר צומת אחרת מפיקה בלוק בהפרש של כמה שניות משלכם.</translation>
</message>
<message>
<source>Debug information</source>
@@ -2963,6 +3128,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>סגירת ארנק</translation>
</message>
<message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>האם את/ה בטוח/ה שברצונך לסגור את הארנק &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<translation>סגירת הארנק למשך זמן רב מדי יכול לגרור את הצורך לסינכרון מחדש של כל השרשרת אם אופצית הגיזום אקטיבית.</translation>
</message>
@@ -2990,7 +3159,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Do you want to increase the fee?</source>
- <translation>להגדיל את העמלה?</translation>
+ <translation>האם ברצונך להגדיל את העמלה?</translation>
</message>
<message>
<source>Current fee:</source>
@@ -3009,6 +3178,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>אישור הקפצת עמלה</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>לא ניתן לשמור את העסקה כטיוטה.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT הועתקה</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>אי אפשר לחתום על ההעברה.</translation>
</message>
@@ -3231,6 +3408,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>איתחול של תהליך בדיקות השפיות נכשל. %s בתהליך סגירה.</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>הרשאת P2P שגויה: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>סכום שגוי עבור ‎-%s=&lt;amount&gt;:‏ '%s'</translation>
</message>
@@ -3247,6 +3428,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>התיקיה שהוגדרה "%s" לא קיימת.</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>כתובת לא ידועה מסוג "%s"</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>סוג שינוי לא ידוע: "%s"</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>שדרוג מאגר נתוני txindex </translation>
</message>
@@ -3368,6 +3557,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>עליך לציין פתחה עם ‎-whitebind:‏ '%s'</translation>
</message>
<message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>מצב מצומצם לא ניתן לשימוש עם blockfilterindex</translation>
+ </message>
+ <message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation>הורדת -maxconnections מ %d ל %d, עקב מגבלות מערכת.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_hi.ts b/src/qt/locale/bitcoin_hi.ts
index bf67fd01f5..841d2ca4dd 100644
--- a/src/qt/locale/bitcoin_hi.ts
+++ b/src/qt/locale/bitcoin_hi.ts
@@ -3,11 +3,11 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>पते या लेबल को संपादित करने के लिए दाहिना-क्लिक करें</translation>
+ <translation>एड्रेस या लेबल को बदलने के लिए राइट-क्लिक करें </translation>
</message>
<message>
<source>Create a new address</source>
- <translation>एक नया पता बनाएं</translation>
+ <translation>नया एड्रेस बनाएं</translation>
</message>
<message>
<source>&amp;New</source>
@@ -15,7 +15,7 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>चुनिन्दा पते को सिस्टम क्लिपबोर्ड पर कापी करे !</translation>
+ <translation>चुने हुए एड्रेस को सिस्टम क्लिपबोर्ड पर कॉपी करें</translation>
</message>
<message>
<source>&amp;Copy</source>
@@ -23,15 +23,15 @@
</message>
<message>
<source>C&amp;lose</source>
- <translation>सी&amp;लूज़</translation>
+ <translation>&amp;बंद करें</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>सूची से वर्तमान में चयनित पता हटाएं</translation>
+ <translation>चुने हुए एड्रेस को सूची से हटाएं</translation>
</message>
<message>
<source>Enter address or label to search</source>
- <translation>ढूँदने के लिए कृपा करके पता या लेबल टाइप करे !</translation>
+ <translation>ढूंढने के लिए एड्रेस या लेबल दर्ज करें</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
@@ -43,47 +43,55 @@
</message>
<message>
<source>&amp;Delete</source>
- <translation>&amp;मिटाए !!</translation>
+ <translation>&amp;मिटाए</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>सिक्कों को भेजने के लिए पता चुनें</translation>
+ <translation>कॉइन भेजने के लिए एड्रेस चुनें</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>सिक्कों को प्राप्त करने के लिए पता चुनें</translation>
+ <translation>कॉइन प्राप्त करने के लिए एड्रेस चुनें </translation>
+ </message>
+ <message>
+ <source>C&amp;hoose</source>
+ <translation>&amp;चुनें</translation>
</message>
<message>
<source>Sending addresses</source>
- <translation>सभी पते भेज रहा है</translation>
+ <translation>एड्रेस भेजे जा रहें हैं</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>पतों को प्राप्त कर रहा है</translation>
+ <translation>एड्रेस प्राप्त किए जा रहें हैं</translation>
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>ये भुगतान भेजने के लिए ये आपके बिटकॉइन पते हैं। हमेशा सिक्के भेजने से पहले राशि और प्राप्तकर्ता पते की जांच करें।</translation>
+ <translation>भुगतान करने के लिए ये आपके बिटकॉइन एड्रेस हैं। कॉइन भेजने से पहले राशि और गंतव्य एड्रेस की हमेशा जाँच करें </translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>भुगतान प्राप्त करने के लिए ये आपके बिटकॉइन एड्रेस हैं। नया एड्रेस बनाने के लिए रिसीव टैब में 'नया एड्रेस बनाएं' बटन का प्रयोग करें</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>&amp;पता कॉपी करें </translation>
+ <translation>&amp;एड्रेस कॉपी करें</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>प्रतिलिप करे और चिन्हित करें</translation>
+ <translation>कॉपी &amp;लेबल </translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>&amp;संशोधित करें </translation>
+ <translation>&amp;बदलाव करें</translation>
</message>
<message>
<source>Export Address List</source>
- <translation>निर्यात पता सूची</translation>
+ <translation>एड्रेस की सूची निर्यात करें</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>कोमा द्वारा अलग की गई फ़ाइल (* .csv)</translation>
+ <translation>कौमा सेपरेटेड फाइल (* .csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
@@ -91,22 +99,22 @@
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
- <translation>पता सूची को %1 में सहेजने का प्रयास करने में त्रुटि हुई। कृपया पुन: प्रयास करें।</translation>
+ <translation>एड्रेस की सूची को %1 में सहेजने का प्रयास करने में त्रुटि हुई। कृपया पुन: प्रयास करें।</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
- <translation>परचा</translation>
+ <translation>लेबल</translation>
</message>
<message>
<source>Address</source>
- <translation>पता</translation>
+ <translation>एड्रेस</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(कोई परचा नहीं )</translation>
+ <translation>(लेबल नहीं है)</translation>
</message>
</context>
<context>
@@ -117,15 +125,19 @@
</message>
<message>
<source>Enter passphrase</source>
- <translation>पहचान शब्द/अक्षर डालिए !</translation>
+ <translation>पासफ्रेज़ दर्ज करें</translation>
</message>
<message>
<source>New passphrase</source>
- <translation>नया पहचान शब्द/अक्षर डालिए !</translation>
+ <translation>नया पासफ्रेज़</translation>
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>दोबारा नया पहचान शब्द/अक्षर डालिए !</translation>
+ <translation>नया पासफ्रेज़ दोबारा दर्ज करें </translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>पासफ्रेज़ उजागर करे</translation>
</message>
<message>
<source>Encrypt wallet</source>
@@ -168,26 +180,86 @@
<translation>वॉलेट को एन्क्रिप्ट किया गया है</translation>
</message>
<message>
+ <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>वॉलेट में नया सुरक्षा संवाद दर्ज करें | कृपया दस या उससे अधिक, या फिर आठ या उससे अधिक अव्यवस्थित अंको से ही अपना सुरक्षा संवाद बनाएं ।</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>वॉलेट में पुराना एवं नया सुरक्षा संवाद दर्ज करें ।</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>याद रखें कि अपने बटुए (वॉलेट) एन्क्रिप्ट करना आपके कंप्यूटर को संक्रमित करने वाले मैलवेयर द्वारा आपके बिटकॉइन को चोरी होने से पूरी तरह से सुरक्षित नहीं कर सकता है।</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>बटुए "वॉलेट" को एन्क्रिप्ट किया जाना है</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>आपका बटुआ "वॉलेट" एन्क्रिप्टेड होने वाला है।</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>आपका बटुआ "वॉलेट" एन्क्रिप्ट हो गया है।</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>महत्वपूर्ण: किसी भी पिछले बैकअप को आपने अपनी वॉलेट फ़ाइल से बनाया था, उसे नए जनरेट किए गए एन्क्रिप्टेड वॉलेट फ़ाइल से बदल दिया जाना चाहिए। सुरक्षा कारणों से, अनएन्क्रिप्टेड वॉलेट फ़ाइल के पिछले बैकअप बेकार हो जाएंगे जैसे ही आप नए, एन्क्रिप्टेड वॉलेट का उपयोग करना शुरू करते हैं।</translation>
+ </message>
+ <message>
<source>Wallet encryption failed</source>
<translation>वॉलेट एन्क्रिप्शन विफल रहा</translation>
</message>
<message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>आंतरिक त्रुटि के कारण वॉलेट एन्क्रिप्शन विफल रहा। आपका वॉलेट "बटुआ" एन्क्रिप्ट नहीं किया गया था।</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>आपूर्ति किए गए पासफ़्रेज़ मेल नहीं खाते हैं।</translation>
+ </message>
+ <message>
<source>Wallet unlock failed</source>
<translation>वॉलेट अनलॉक विफल रहा</translation>
</message>
<message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>वॉलेट डिक्रिप्शन के लिए दर्ज किया गया पासफ़्रेज़ गलत था।</translation>
+ </message>
+ <message>
<source>Wallet decryption failed</source>
<translation>वॉलेट डिक्रिप्शन विफल</translation>
</message>
- </context>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>वॉलेट पासफ़्रेज़ को सफलतापूर्वक बदल दिया गया था।</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>चेतावनी: कैप्स लॉक कुंजी चालू है!</translation>
+ </message>
+</context>
<context>
<name>BanTableModel</name>
- </context>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>आईपी /नेटमास्क "Netmask"</translation>
+ </message>
+ <message>
+ <source>Banned Until</source>
+ <translation> तक बैन कर दिया</translation>
+ </message>
+</context>
<context>
<name>BitcoinGUI</name>
<message>
+ <source>Sign &amp;message...</source>
+ <translation>हस्ताक्षर और संदेश ...</translation>
+ </message>
+ <message>
<source>Synchronizing with network...</source>
- <translation>नेटवर्क से समकालिक (मिल) रहा है ...</translation>
+ <translation>नेटवर्क से समकालिकरण जारी है ...</translation>
</message>
<message>
<source>&amp;Overview</source>
@@ -215,14 +287,38 @@
<translation>अप्लिकेशन से बाहर निकलना !</translation>
</message>
<message>
+ <source>&amp;About %1</source>
+ <translation>और %1 के बारे में</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>%1 के बारे में जानकारी दिखाएं</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>के बारे में और क्यूटी "Qt"</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>क्यूटी "Qt" के बारे में जानकारी दिखाएँ</translation>
+ </message>
+ <message>
<source>&amp;Options...</source>
<translation>&amp;विकल्प</translation>
</message>
<message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>और वॉलेट को गोपित "एन्क्रिप्ट" करें</translation>
+ </message>
+ <message>
<source>&amp;Backup Wallet...</source>
<translation>&amp;बैकप वॉलेट</translation>
</message>
<message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>और पासफ़्रेज़ बदलें</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>तिजोरी</translation>
</message>
@@ -564,7 +660,15 @@
<name>TransactionDesc</name>
<message>
<source>Date</source>
- <translation>taareek</translation>
+ <translation>दिनांक</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>स्रोत</translation>
+ </message>
+ <message>
+ <source>Generated</source>
+ <translation>उत्पन्न</translation>
</message>
<message>
<source>unknown</source>
@@ -654,6 +758,10 @@
<context>
<name>bitcoin-core</name>
<message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>यह एक पूर्व-रिलीज़ परीक्षण बिल्ड है - अपने जोखिम पर उपयोग करें - खनन या व्यापारी अनुप्रयोगों के लिए उपयोग न करें</translation>
+ </message>
+ <message>
<source>Verifying blocks...</source>
<translation>ब्लॉक्स जाँचे जा रहा है...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts
index 34c8b91844..e8c13c3c27 100644
--- a/src/qt/locale/bitcoin_hu.ts
+++ b/src/qt/locale/bitcoin_hu.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>A cím vagy címke szerkeszteséhez kattintson a jobb gombbal</translation>
+ <translation>A cím vagy címke szerkesztéséhez kattints a jobb gombbal</translation>
</message>
<message>
<source>Create a new address</source>
@@ -482,6 +482,14 @@
<translation>Naprakész</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Csomópont ablak</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Nyisd meg a hibaellenőrző és diagnosztizáló konzolt.</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Küldő címek</translation>
</message>
@@ -550,6 +558,10 @@
<translation>Hiba: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>Vigyázz: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Dátum: %1
@@ -806,6 +818,10 @@
<translation>A tárcához tartozó privát kulcsok letiltása. Azok a tárcák, melyeknél a privát kulcsok le vannak tiltva, nem tartalmaznak privát kulcsokat és nem tartalmazhatnak HD magot vagy importált privát kulcsokat. Ez azoknál a tárcáknál ideális, melyeket csak megfigyelésre használnak.</translation>
</message>
<message>
+ <source>Disable Private Keys</source>
+ <translation>Kapcsold ki a Privát Kódot</translation>
+ </message>
+ <message>
<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>Üres tárca készítése. Az üres tárcák kezdetben nem tartalmaznak privát kulcsokat vagy szkripteket. Később lehetséges a privát kulcsok vagy címek importálása avagy egy HD mag beállítása.</translation>
</message>
@@ -813,7 +829,11 @@
<source>Make Blank Wallet</source>
<translation>Üres tárca készítése</translation>
</message>
- </context>
+ <message>
+ <source>Create</source>
+ <translation>Létrehozás</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -1020,10 +1040,18 @@
<source>Hide</source>
<translation>Elrejtés</translation>
</message>
+ <message>
+ <source>Esc</source>
+ <translation>Kilépés</translation>
+ </message>
</context>
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Nyisd meg a bitcoin címedet</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1408,6 +1436,10 @@
<translation>URI kezelés</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' nem érvényes egységes erőforrás azonosító (URI). Használd helyette a 'bitcoin'-t.</translation>
+ </message>
+ <message>
<source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
<translation>A BIP70 széleskörű biztonsági hiányosságai következtében határozottan ajánljuk, hogy hagyjon figyelmen kívül bármiféle kereskedelmi utasítást, amely a tárca váltására készteti.</translation>
</message>
@@ -1573,6 +1605,10 @@
<translation>Hiba lépett fel az URI QR kóddá alakításakor.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>QR kód támogatás nem elérhető.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>QR Kód Mentése</translation>
</message>
@@ -1608,6 +1644,10 @@
<translation>Adatkönyvtár</translation>
</message>
<message>
+ <source>Blocksdir</source>
+ <translation>Blokk könyvtár</translation>
+ </message>
+ <message>
<source>Startup time</source>
<translation>Bekapcsolás ideje</translation>
</message>
@@ -1648,6 +1688,10 @@
<translation>Tárca:</translation>
</message>
<message>
+ <source>(none)</source>
+ <translation>(nincs)</translation>
+ </message>
+ <message>
<source>&amp;Reset</source>
<translation>&amp;Visszaállítás</translation>
</message>
@@ -1700,6 +1744,10 @@
<translation>User Agent</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Csomópont ablak</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>A %1 debug log fájl megnyitása a jelenlegi könyvtárból. Ez néhány másodpercig eltarthat nagyobb log fájlok esetén.</translation>
</message>
@@ -2156,6 +2204,14 @@
<translation>Por-határ:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Rejtsd el a tranzakciós költségek beállításait</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>A túl alacsony illeték a tranzakció soha be nem teljesülését eredményezheti (olvassa el az elemleírást)</translation>
+ </message>
+ <message>
<source>Confirmation time target:</source>
<translation>Várható megerősítési idő:</translation>
</message>
@@ -2232,14 +2288,30 @@
<translation>Később növelheti a tranzakció díját (lásd Replace-By-Fee, BIP-125).</translation>
</message>
<message>
+ <source>Please, review your transaction.</source>
+ <translation>Kérjük, hogy ellenőrizze le a tranzakcióját.</translation>
+ </message>
+ <message>
<source>Transaction fee</source>
<translation>Tranzakciós díj</translation>
</message>
<message>
+ <source>Total Amount</source>
+ <translation>Teljes összeg</translation>
+ </message>
+ <message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>A címzett lista ellenőrzéséhez kattintson a "További részletek" gombra.</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Összeg küldésének megerősítése</translation>
</message>
<message>
+ <source>Send</source>
+ <translation>Küldés</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>A fogadó címe érvénytelen. Kérem ellenőrizze.</translation>
</message>
@@ -2489,6 +2561,10 @@
<translation>Tárca megnyitása megszakítva</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Nincs hiba</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>A megadott cím privát kulcsa nem található.</translation>
</message>
@@ -2647,10 +2723,18 @@
<translation>Tranzakció teljes mérete</translation>
</message>
<message>
+ <source>Transaction virtual size</source>
+ <translation>A tranzakció virtuális mérete</translation>
+ </message>
+ <message>
<source>Output index</source>
<translation>Indeks izhoda</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(A tanúsítvány nem ellenőrzött)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Kereskedő</translation>
</message>
@@ -3058,7 +3142,11 @@
<source>The wallet data was successfully saved to %1.</source>
<translation>A tárca adatai sikeresen elmentve %1.</translation>
</message>
- </context>
+ <message>
+ <source>Cancel</source>
+ <translation>Bezárás</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
@@ -3194,6 +3282,10 @@
<translation>Helytelen vagy nemlétező genézis blokk. Helytelen hálózati adatkönyvtár?</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Érvénytelen P2P jog: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>Neveljavna količina za -%s=&lt;amount&gt;: '%s'</translation>
</message>
@@ -3206,10 +3298,18 @@
<translation>Neveljavna količina za -fallbackfee=&lt;amount&gt;: '%s'</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Ismeretlen cím típus '%s'</translation>
+ </message>
+ <message>
<source>Loading P2P addresses...</source>
<translation>P2P címek betöltése...</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Hiba: A lemezen kevés hely elérhető!</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>Tiltólista betöltése...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_id.ts b/src/qt/locale/bitcoin_id.ts
index e7be7825fc..09d3b87bca 100644
--- a/src/qt/locale/bitcoin_id.ts
+++ b/src/qt/locale/bitcoin_id.ts
@@ -482,6 +482,14 @@
<translation>Terbaru</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Jendela Node</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Buka konsol debug dan diagnosa node</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>Address &amp;Pengirim</translation>
</message>
@@ -490,6 +498,10 @@
<translation>Address &amp;Penerima</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Buka URI bitcoin:</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Buka Wallet</translation>
</message>
@@ -1050,6 +1062,14 @@
<translation>Sembunyikan</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Keluar</translation>
+ </message>
+ <message>
+ <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>%1 menyinkronkan. Program ini akan mengunduh header dan blok dari rekan dan memvalidasi sampai blok terbaru.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Tidak diketahui. Sinkronisasi Header (%1, %2%)...</translation>
</message>
@@ -1057,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Buka URI bitcoin:</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1449,6 +1473,10 @@
<translation>'bitcoin://' bukanlah alamat URI yang valid. Silakan gunakan 'bitcoin:'.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Tidak dapat memproses pembayaran karena dukungan BIP70 tidak disertakan.</translation>
+ </message>
+ <message>
<source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
<translation>Berhubung kelemahan keamanan yang meluas di BIP70, sangat disarankan agar instruksi pedagang untuk mengganti dompet diabaikan.</translation>
</message>
@@ -1767,6 +1795,10 @@
</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Jendela Node</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Buka file log debug %1 dari direktori data saat ini. Dapat memakan waktu beberapa detik untuk file log besar.</translation>
</message>
@@ -1974,6 +2006,10 @@
<translation>Nilai permintaan opsional. Biarkan ini kosong atau nol bila tidak meminta nilai tertentu.</translation>
</message>
<message>
+ <source>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>
+ <translation>Label fakultatif untuk menghubungkan dengan alamat penerima baru (anda menggunakannya untuk mengindetifikasi faktur). Itu juga dilampirkan pada permintaan pembayaran.</translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
<translation>&amp;Create alamat penerima baru</translation>
</message>
@@ -2227,6 +2263,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Dust:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Sembunyikan pengaturan biaya transaksi</translation>
+ </message>
+ <message>
<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>Ketika volume transaksi lebih sedikit daripada ruang di blok, penambang serta simpul yang menyiarkanikan dapat memberlakukan biaya minimum. Anda boleh hanya membayar biaya minimum, tetapi perlu diketahui bahwa ini dapat menghasilkan transaksi yang tidak pernah dikonfirmasi setelah ada lebih banyak permintaan untuk transaksi bitcoin daripada yang dapat diproses jaringan.</translation>
</message>
@@ -2307,6 +2347,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>%1 ke %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Apakah anda ingin menjadikan transaksi ini sebagai konsep?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Apakah anda yakin ingin mengirimkan?</translation>
</message>
@@ -2343,6 +2387,26 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Konfirmasi pengiriman koin</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Konfirmasi proposal transaksi</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Salin PSBT ke papan klip</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Kirim</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT disalin</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Saldo (hanya lihat):</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Alamat penerima tidak sesuai. Mohon periksa kembali.</translation>
</message>
@@ -2434,6 +2498,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Hapus masukan ini</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Jumlah yang ingin dikirim dalam unit yang dipilih</translation>
+ </message>
+ <message>
<source>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>
<translation>Biaya akan diambil dari jumlah yang dikirim. Penerima akan menerima bitcoin lebih sedikit daripada yang di masukkan di bidang jumlah. Jika ada beberapa penerima, biaya dibagi rata.</translation>
</message>
@@ -2556,6 +2624,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Alamat Bitcoin yang menandatangani pesan</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Pesan yang ditandatangani untuk diverifikasi</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verifikasi pesan untuk memastikannya ditandatangani dengan alamat Bitcoin tersebut</translation>
</message>
@@ -2588,6 +2660,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Pembukaan kunci dompet dibatalkan.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Tidak ada kesalahan</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Private key untuk alamat yang dimasukkan tidak tersedia.</translation>
</message>
@@ -2638,10 +2714,22 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Buka sampai %1</translation>
</message>
<message>
+ <source>conflicted with a transaction with %1 confirmations</source>
+ <translation>Konflik dengan sebuah transaksi dengan %1 konfirmasi</translation>
+ </message>
+ <message>
<source>0/unconfirmed, %1</source>
<translation>0/belum dikonfirmasi, %1</translation>
</message>
<message>
+ <source>in memory pool</source>
+ <translation>Dalam pool memory</translation>
+ </message>
+ <message>
+ <source>not in memory pool</source>
+ <translation>Tidak dalam pool memory</translation>
+ </message>
+ <message>
<source>%1/unconfirmed</source>
<translation>%1/belum dikonfirmasi</translation>
</message>
@@ -2662,10 +2750,30 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Sumber</translation>
</message>
<message>
+ <source>Generated</source>
+ <translation>Dihasilkan</translation>
+ </message>
+ <message>
+ <source>From</source>
+ <translation>Dari</translation>
+ </message>
+ <message>
<source>unknown</source>
<translation>tidak diketahui</translation>
</message>
<message>
+ <source>To</source>
+ <translation>Untuk</translation>
+ </message>
+ <message>
+ <source>own address</source>
+ <translation>alamat milik sendiri</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>hanya-melihat</translation>
+ </message>
+ <message>
<source>label</source>
<translation>label</translation>
</message>
@@ -2694,6 +2802,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Biaya Transaksi</translation>
</message>
<message>
+ <source>Net amount</source>
+ <translation>Jumlah bersih</translation>
+ </message>
+ <message>
<source>Message</source>
<translation>Pesan</translation>
</message>
@@ -2706,6 +2818,26 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>ID Transaksi</translation>
</message>
<message>
+ <source>Transaction total size</source>
+ <translation>Ukuran transaksi total</translation>
+ </message>
+ <message>
+ <source>Transaction virtual size</source>
+ <translation>Ukuran transaksi virtual</translation>
+ </message>
+ <message>
+ <source>Output index</source>
+ <translation>Indeks outpu</translation>
+ </message>
+ <message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Sertifikat tidak diverifikasi)</translation>
+ </message>
+ <message>
+ <source>Merchant</source>
+ <translation>Penjual</translation>
+ </message>
+ <message>
<source>Debug information</source>
<translation>Informasi debug</translation>
</message>
@@ -2714,6 +2846,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Transaksi</translation>
</message>
<message>
+ <source>Inputs</source>
+ <translation>Input</translation>
+ </message>
+ <message>
<source>Amount</source>
<translation>Jumlah</translation>
</message>
@@ -2751,6 +2887,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<source>Label</source>
<translation>Label</translation>
</message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Buka %n untuk blok lebih</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Buka sampai %1</translation>
@@ -2760,6 +2900,22 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Belum dikonfirmasi</translation>
</message>
<message>
+ <source>Abandoned</source>
+ <translation>yang ditelantarkan</translation>
+ </message>
+ <message>
+ <source>Confirmed (%1 confirmations)</source>
+ <translation>Dikonfirmasi (%1 konfirmasi)</translation>
+ </message>
+ <message>
+ <source>Conflicted</source>
+ <translation>Bertentangan</translation>
+ </message>
+ <message>
+ <source>Generated but not accepted</source>
+ <translation>Dihasilkan tapi tidak diterima</translation>
+ </message>
+ <message>
<source>Received with</source>
<translation>Diterima dengan</translation>
</message>
@@ -2772,10 +2928,26 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Dikirim ke</translation>
</message>
<message>
+ <source>Payment to yourself</source>
+ <translation>Pembayaran untuk diri sendiri</translation>
+ </message>
+ <message>
+ <source>Mined</source>
+ <translation>Ditambang</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>hanya-melihat</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(tidak ada label)</translation>
</message>
<message>
+ <source>Transaction status. Hover over this field to show number of confirmations.</source>
+ <translation>Status transaksi. Arahkan kursor ke bidang ini untuk menampilkan jumlah konfirmasi.</translation>
+ </message>
+ <message>
<source>Date and time that the transaction was received.</source>
<translation>Tanggal dan waktu transaksi telah diterima.</translation>
</message>
@@ -2783,7 +2955,19 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<source>Type of transaction.</source>
<translation>Tipe transaksi.</translation>
</message>
- </context>
+ <message>
+ <source>Whether or not a watch-only address is involved in this transaction.</source>
+ <translation>Apakah alamat hanya-melihat terlibat dalam transaksi ini atau tidak.</translation>
+ </message>
+ <message>
+ <source>User-defined intent/purpose of the transaction.</source>
+ <translation>maksud/tujuan transaksi yang ditentukan pengguna.</translation>
+ </message>
+ <message>
+ <source>Amount removed from or added to balance.</source>
+ <translation>Jumlah dihapus dari atau ditambahkan ke saldo.</translation>
+ </message>
+</context>
<context>
<name>TransactionView</name>
<message>
@@ -2811,6 +2995,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Tahun ini</translation>
</message>
<message>
+ <source>Range...</source>
+ <translation>Jarak...</translation>
+ </message>
+ <message>
<source>Received with</source>
<translation>Diterima dengan</translation>
</message>
@@ -2819,6 +3007,14 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Dikirim ke</translation>
</message>
<message>
+ <source>To yourself</source>
+ <translation>Untuk diri sendiri</translation>
+ </message>
+ <message>
+ <source>Mined</source>
+ <translation>Ditambang</translation>
+ </message>
+ <message>
<source>Other</source>
<translation>Lainnya</translation>
</message>
@@ -2855,6 +3051,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Salain ID Transaksi</translation>
</message>
<message>
+ <source>Copy raw transaction</source>
+ <translation>Salin transaksi yang belum diproses</translation>
+ </message>
+ <message>
<source>Copy full transaction details</source>
<translation>Salin detail transaksi</translation>
</message>
@@ -2879,6 +3079,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Terkonfirmasi</translation>
</message>
<message>
+ <source>Watch-only</source>
+ <translation>Hanya-melihat</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>Tanggal</translation>
</message>
@@ -2903,23 +3107,55 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Mengekspor Gagal</translation>
</message>
<message>
+ <source>There was an error trying to save the transaction history to %1.</source>
+ <translation>Terjadi kesalahan saat mencoba menyimpan riwayat transaksi ke %1.</translation>
+ </message>
+ <message>
<source>Exporting Successful</source>
<translation>Ekspor Berhasil</translation>
</message>
- </context>
+ <message>
+ <source>The transaction history was successfully saved to %1.</source>
+ <translation>Riwayat transaksi berhasil disimpan ke %1.</translation>
+ </message>
+ <message>
+ <source>Range:</source>
+ <translation>Jarak:</translation>
+ </message>
+ <message>
+ <source>to</source>
+ <translation>untuk</translation>
+ </message>
+</context>
<context>
<name>UnitDisplayStatusBarControl</name>
- </context>
+ <message>
+ <source>Unit to show amounts in. Click to select another unit.</source>
+ <translation>Unit untuk menunjukkan jumlah. Klik untuk memilih unit lain.</translation>
+ </message>
+</context>
<context>
<name>WalletController</name>
<message>
<source>Close wallet</source>
<translation>Tutup wallet</translation>
</message>
- </context>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Apakah anda yakin ingin menutup dompet &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Menutup dompet terlalu lama dapat menyebabkan harus menyinkron ulang seluruh rantai jika pemangkasan diaktifkan.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
- </context>
+ <message>
+ <source>No wallet has been loaded.</source>
+ <translation>Tidak ada dompent yang dimuat.</translation>
+ </message>
+</context>
<context>
<name>WalletModel</name>
<message>
@@ -2927,6 +3163,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Kirim Koin</translation>
</message>
<message>
+ <source>Fee bump error</source>
+ <translation>Kesalahan biaya tagihan</translation>
+ </message>
+ <message>
<source>Increasing transaction fee failed</source>
<translation>Gagal meningkatkan biaya transaksi</translation>
</message>
@@ -2935,6 +3175,10 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Apa Anda ingin meningkatkan biayanya?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Apakah anda ingin membuat draf transaksi dengan kenaikan biaya?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Biaya saat ini:</translation>
</message>
@@ -2947,6 +3191,26 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Biaya baru:</translation>
</message>
<message>
+ <source>Confirm fee bump</source>
+ <translation>Konfirmasi biaya tambahan</translation>
+ </message>
+ <message>
+ <source>Can't draft transaction.</source>
+ <translation>Tidak dapat membuat konsep transaksi.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT disalin</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>Tidak dapat menandatangani transaksi.</translation>
+ </message>
+ <message>
+ <source>Could not commit transaction</source>
+ <translation>Tidak dapat melakukan transaksi</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>wallet default</translation>
</message>
@@ -2962,14 +3226,30 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Ekspor data dalam tab sekarang ke sebuah berkas</translation>
</message>
<message>
+ <source>Backup Wallet</source>
+ <translation>Cadangkan Dompet</translation>
+ </message>
+ <message>
+ <source>Wallet Data (*.dat)</source>
+ <translation>Data Dompet (*.dat)</translation>
+ </message>
+ <message>
<source>Backup Failed</source>
<translation>Pencadangan Gagal</translation>
</message>
<message>
+ <source>There was an error trying to save the wallet data to %1.</source>
+ <translation>Terjadi kesalahan saat mencoba menyimpan data dompet ke %1.</translation>
+ </message>
+ <message>
<source>Backup Successful</source>
<translation>Pencadangan Berhasil</translation>
</message>
<message>
+ <source>The wallet data was successfully saved to %1.</source>
+ <translation>Data dompet berhasil disimpan ke %1.</translation>
+ </message>
+ <message>
<source>Cancel</source>
<translation>Batal</translation>
</message>
@@ -2977,6 +3257,66 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<context>
<name>bitcoin-core</name>
<message>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <translation>Didistribusikan di bawah lisensi perangkat lunak MIT, lihat berkas terlampir %s atau %s</translation>
+ </message>
+ <message>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <translation>Pemangkasan dikonfigurasikan di bawah minimum dari %d MiB. Harap gunakan angka yang lebih tinggi.</translation>
+ </message>
+ <message>
+ <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>Pemangkasan: sinkronisasi dompet terakhir melampaui data yang sudah dipangkas. Anda perlu -reindex (unduh seluruh blockchain lagi jika terjadi node pemangkasan)</translation>
+ </message>
+ <message>
+ <source>Error: A fatal internal error occurred, see debug.log for details</source>
+ <translation>Error: Kesalahan internal fatal terjadi, lihat debug.log untuk detailnya</translation>
+ </message>
+ <message>
+ <source>Pruning blockstore...</source>
+ <translation>Memangkas blockstore...</translation>
+ </message>
+ <message>
+ <source>Unable to start HTTP server. See debug log for details.</source>
+ <translation>Tidak dapat memulai server HTTP. Lihat log debug untuk detailnya.</translation>
+ </message>
+ <message>
+ <source>The %s developers</source>
+ <translation>Pengembang %s</translation>
+ </message>
+ <message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Tidak dapat memperoleh kunci pada direktori data %s. %s mungkin sudah berjalan.</translation>
+ </message>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Kesalahan membaca %s! Semua kunci dibaca dengan benar, tetapi data transaksi atau entri buku alamat mungkin hilang atau salah.</translation>
+ </message>
+ <message>
+ <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <translation>Periksa apakah tanggal dan waktu komputer anda benar! Jika jam anda salah, %s tidak akan berfungsi dengan baik.</translation>
+ </message>
+ <message>
+ <source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <translation>Silakan berkontribusi jika %s berguna. Kunjungi %s untuk informasi lebih lanjut tentang perangkat lunak.</translation>
+ </message>
+ <message>
+ <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
+ <translation>Blok basis data berisi blok yang tampaknya berasal dari masa depan. Ini mungkin karena tanggal dan waktu komputer anda diatur secara tidak benar. Bangun kembali blok basis data jika anda yakin tanggal dan waktu komputer anda benar</translation>
+ </message>
+ <message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>Ini adalah uji coba pra-rilis - gunakan dengan risiko anda sendiri - jangan digunakan untuk aplikasi penambangan atau penjual</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Peringatan: Jaringan tampaknya tidak sepenuhnya setuju! Beberapa penambang tampaknya mengalami masalah.</translation>
+ </message>
+ <message>
+ <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>Peringatan: Kami tampaknya tidak sepenuhnya setuju dengan peers kami! Anda mungkin perlu memutakhirkan, atau nodes lain mungkin perlu dimutakhirkan.</translation>
+ </message>
+ <message>
<source>Corrupted block database detected</source>
<translation>Menemukan database blok yang rusak</translation>
</message>
@@ -3009,26 +3349,128 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Tidak bisa cari blok pertama, atau blok pertama salah. Salah direktori untuk jaringan?</translation>
</message>
<message>
+ <source>Loading P2P addresses...</source>
+ <translation>Memuat alamat P2P....</translation>
+ </message>
+ <message>
<source>Error: Disk space is too low!</source>
<translation>Eror: Kapasitas penyimpanan penuh!</translation>
</message>
<message>
+ <source>Loading banlist...</source>
+ <translation>Memuat banlist...</translation>
+ </message>
+ <message>
<source>Not enough file descriptors available.</source>
<translation>Deskripsi berkas tidak tersedia dengan cukup.</translation>
</message>
<message>
+ <source>Prune cannot be configured with a negative value.</source>
+ <translation>Pemangkasan tidak dapat dikonfigurasi dengan nilai negatif.</translation>
+ </message>
+ <message>
+ <source>The source code is available from %s.</source>
+ <translation>Kode sumber tersedia dari %s.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>Biaya transaksi dan kalkulasi perubahan gagal</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <translation>Tidak dapat mengikat ke %s di komputer ini. %s mungkin sudah berjalan.</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Tidak dapat menghasilkan kunci</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Kategori logging yang tidak didukung %s=%s.</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>Memutakhirkan basis data UTXO</translation>
+ </message>
+ <message>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation>Komentar Agen Pengguna (%s) berisi karakter yang tidak aman.</translation>
+ </message>
+ <message>
<source>Verifying blocks...</source>
<translation>Blok-blok sedang diverifikasi...</translation>
</message>
<message>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation>Dompet harus ditulis ulang: mulai ulang %s untuk menyelesaikan</translation>
+ </message>
+ <message>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation>Error: Mendengarkan koneksi yang masuk gagal (dengarkan kesalahan yang dikembalikan %s)</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>Jumlah transaksi terlalu kecil untuk dikirim setelah biaya dikurangi</translation>
+ </message>
+ <message>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation>Anda perlu membangun kembali basis data menggunakan -reindex untuk kembali ke mode tidak dipangkas. Ini akan mengunduh ulang seluruh blockchain</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Kesalahan membaca dari basis data, mematikan.</translation>
+ </message>
+ <message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Kesalahan memutakhirkan basis data chainstate</translation>
+ </message>
+ <message>
<source>Error: Disk space is low for %s</source>
<translation>Eror: Kapasitas penyimpanan penuh untuk %s</translation>
</message>
<message>
+ <source>Invalid -onion address or hostname: '%s'</source>
+ <translation>Alamat -onion atau hostname tidak valid: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid -proxy address or hostname: '%s'</source>
+ <translation>Alamat proxy atau hostname tidak valid: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid netmask specified in -whitelist: '%s'</source>
+ <translation>Netmask tidak valid yang ditentukan di -whitelist: '%s'</translation>
+ </message>
+ <message>
+ <source>Need to specify a port with -whitebind: '%s'</source>
+ <translation>Perlu menentukan port dengan -whitebind: '%s'</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Mode pemangkasan tidak kompatibel dengan -blockfilterindex.</translation>
+ </message>
+ <message>
+ <source>Section [%s] is not recognized.</source>
+ <translation>Bagian [%s] tidak dikenali.</translation>
+ </message>
+ <message>
<source>Signing transaction failed</source>
<translation>Tandatangani transaksi tergagal</translation>
</message>
<message>
+ <source>The specified config file %s does not exist
+</source>
+ <translation>Berkas konfigurasi %s yang ditentukan tidak ada
+</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>Jumlah transaksi terlalu kecil untuk membayar biaya ongkos</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>Ini adalah perangkat lunak eksperimental.</translation>
+ </message>
+ <message>
<source>Transaction amount too small</source>
<translation>Nilai transaksi terlalu kecil</translation>
</message>
@@ -3037,10 +3479,46 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Transaksi terlalu besar</translation>
</message>
<message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Memverifikasi dompet...</translation>
+ </message>
+ <message>
<source>Zapping all transactions from wallet...</source>
<translation>Setiap transaksi dalam dompet sedang di-'Zap'...</translation>
</message>
<message>
+ <source>%s is set very high!</source>
+ <translation>%s diset sangat tinggi!</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Terjadi kesalahan saat memuat dompet %s duplikat -wallet nama file yang diterapkan</translation>
+ </message>
+ <message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>Dompet akan menghindari pembayaran kurang dari biaya minimum ongkos relay.</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>Ini adalah ongkos transaksi minimum yang anda bayarkan untuk setiap transaksi.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <translation>Ini adalah ongkos transaksi yang akan anda bayarkan jika anda mengirim transaksi.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>Jumlah transaksi tidak boleh negatif</translation>
+ </message>
+ <message>
+ <source>Transaction has too long of a mempool chain</source>
+ <translation>Transaksi mempunyai rantai mempool yang terlalu panjang</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>Transaksi harus mempunyai paling tidak satu penerima</translation>
+ </message>
+ <message>
<source>Unknown network specified in -onlynet: '%s'</source>
<translation>Jaringan tidak diketahui yang ditentukan dalam -onlynet: '%s'</translation>
</message>
@@ -3049,6 +3527,14 @@ Catatan: Karena biaya dihitung berdasarkan per byte, biaya "100 satoshi per kB"
<translation>Saldo tidak mencukupi</translation>
</message>
<message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Peringatan: Kunci pribadi terdeteksi di dompet {%s} dengan kunci pribadi yang dinonaktifkan</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Tidak dapat menulis ke direktori data '%s'; periksa izinnya.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Memuat indeks blok...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_is.ts b/src/qt/locale/bitcoin_is.ts
index 6eb5c10107..25fe30bbdd 100644
--- a/src/qt/locale/bitcoin_is.ts
+++ b/src/qt/locale/bitcoin_is.ts
@@ -168,6 +168,10 @@
<translation>Veski dulkóðað</translation>
</message>
<message>
+ <source>Wallet to be encrypted</source>
+ <translation>Veski sem á að dulkóða</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>MIKILVÆGT: Nýja dulkóðaða veskisskráin þarf að koma í staðinn fyrir öll fyrri afrit sem þú hefur gert af upprunalegu veskisskránni. Af öryggisástæðum munu öll fyrri afrit af ódulkóðaða veskinu verða óvirk um leið og þú byrjar að nota nýja, dulkóðaða veskið.</translation>
</message>
diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts
index 2cd0941a8c..a8137d3031 100644
--- a/src/qt/locale/bitcoin_it.ts
+++ b/src/qt/locale/bitcoin_it.ts
@@ -1790,6 +1790,14 @@ Per specificare più URL separarli con una barra verticale "|".</translation>
<translation>Blocchi sincronizzati</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Il Sistema Autonomo mappato utilizzato per diversificare la selezione dei peer.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>AS mappato</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User Agent</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts
index ea1bf17838..e40e5ec4fb 100644
--- a/src/qt/locale/bitcoin_ja.ts
+++ b/src/qt/locale/bitcoin_ja.ts
@@ -67,7 +67,7 @@
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>これらは、あなたが知っている支払い送り先の Bitcoin アドレスです。コインを送る前に、必ず金額と送金先アドレスを確認してください。</translation>
+ <translation>これらは、あなたが知っている送信先の Bitcoin アドレスです。コインを送る前に必ず、金額と受取用アドレスを確認してください。</translation>
</message>
<message>
<source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
@@ -91,7 +91,7 @@
</message>
<message>
<source>Comma separated file (*.csv)</source>
- <translation>テキスト CSV (*.csv)</translation>
+ <translation>CSVファイル (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
@@ -114,7 +114,7 @@
</message>
<message>
<source>(no label)</source>
- <translation>(ラベル無し)</translation>
+ <translation>(ラベル無し)</translation>
</message>
</context>
<context>
@@ -133,7 +133,7 @@
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>新しいパスフレーズをもう一度</translation>
+ <translation>新しいパスフレーズをもう一度入力</translation>
</message>
<message>
<source>Show passphrase</source>
@@ -169,7 +169,7 @@
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
- <translation>警告: もしもあなたのウォレットを暗号化してパスフレーズを忘れてしまったら、&lt;b&gt;あなたの Bitcoin はすべて失われます&lt;/b&gt;!</translation>
+ <translation>警告: ウォレットの暗号化後にパスフレーズを忘れてしまった場合、&lt;b&gt;あなたの Bitcoin はすべて失われます&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
@@ -206,7 +206,7 @@
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
- <translation>重要: 今までに作成されたウォレット ファイルのバックアップは、暗号化された新しいウォレット ファイルに置き換える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレット ファイルのバックアップはすぐに使えなくなります。</translation>
+ <translation>重要: 今までに作成されたウォレットファイルのバックアップは、暗号化された新しいウォレットファイルに置き換える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレットファイルのバックアップはすぐに使えなくなります。</translation>
</message>
<message>
<source>Wallet encryption failed</source>
@@ -392,7 +392,7 @@
</message>
<message>
<source>Show or hide the main Window</source>
- <translation>メイン ウインドウを表示または非表示する</translation>
+ <translation>メインウィンドウを表示または非表示にする</translation>
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
@@ -400,7 +400,7 @@
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>Bitcoin アドレスでメッセージに署名して、アドレスを所有していることを証明する</translation>
+ <translation>Bitcoin アドレスでメッセージに署名することで、そのアドレスの所有権を証明する</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
@@ -424,7 +424,7 @@
</message>
<message>
<source>Request payments (generates QR codes and bitcoin: URIs)</source>
- <translation>支払いをリクエストする (QRコードと bitcoin: URIを生成する)&lt;</translation>
+ <translation>支払いをリクエストする(QRコードと bitcoin:で始まるURIを生成する)</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
@@ -483,6 +483,10 @@
<translation>ブロックは最新</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>ノードウィンドウ</translation>
+ </message>
+ <message>
<source>Open node debugging and diagnostic console</source>
<translation>ノードのデバッグ・診断コンソールを開く</translation>
</message>
@@ -516,7 +520,7 @@
</message>
<message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
- <translation>%1 のヘルプ メッセージを表示して、使用可能な XPChain のコマンドライン オプションの一覧を見る。</translation>
+ <translation>%1 のヘルプ メッセージを表示し、使用可能な Bitcoin のコマンドラインオプション一覧を見る。</translation>
</message>
<message>
<source>default wallet</source>
@@ -528,7 +532,7 @@
</message>
<message>
<source>&amp;Window</source>
- <translation>ウインドウ (&amp;W)</translation>
+ <translation>ウィンドウ (&amp;W)</translation>
</message>
<message>
<source>Minimize</source>
@@ -731,7 +735,7 @@
</message>
<message>
<source>Copy fee</source>
- <translation>手数料をコピーす</translation>
+ <translation>手数料をコピー</translation>
</message>
<message>
<source>Copy after fee</source>
@@ -767,11 +771,11 @@
</message>
<message>
<source>Can vary +/- %1 satoshi(s) per input.</source>
- <translation>ひとつの入力につき %1 satoshi 前後ずれることがあります。</translation>
+ <translation>インプット毎に %1 satoshi 前後変動する場合があります。</translation>
</message>
<message>
<source>(no label)</source>
- <translation>(ラベル無し)</translation>
+ <translation>(ラベル無し)</translation>
</message>
<message>
<source>change from %1 (%2)</source>
@@ -779,7 +783,7 @@
</message>
<message>
<source>(change)</source>
- <translation>(おつり)</translation>
+ <translation>(おつり)</translation>
</message>
</context>
<context>
@@ -895,7 +899,7 @@
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>新しいデータ ディレクトリが作成されます。</translation>
+ <translation>新しいデータディレクトリが作成されます。</translation>
</message>
<message>
<source>name</source>
@@ -926,7 +930,7 @@
</message>
<message>
<source>Command-line options</source>
- <translation>コマンドライン オプション</translation>
+ <translation>コマンドラインオプション</translation>
</message>
</context>
<context>
@@ -945,7 +949,7 @@
</message>
<message>
<source>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>
- <translation>OKをクリックすると、%1 は %4 がリリースされた%3年最初の取引からの完全な %4 ブロックチェーン(%2GB)のダウンロードおよび処理を開始します。</translation>
+ <translation>OKをクリックすると、%1 は %4 がリリースされた%3年における最初の取引からの完全な %4 ブロックチェーン(%2GB)のダウンロードおよび処理を開始します。</translation>
</message>
<message>
<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>
@@ -961,11 +965,11 @@
</message>
<message>
<source>Use the default data directory</source>
- <translation>デフォルトのデータ ディレクトリを使用</translation>
+ <translation>デフォルトのデータディレクトリを使用</translation>
</message>
<message>
<source>Use a custom data directory:</source>
- <translation>カスタム データ ディレクトリを使用:</translation>
+ <translation>カスタムデータディレクトリを使用:</translation>
</message>
<message>
<source>Bitcoin</source>
@@ -973,7 +977,7 @@
</message>
<message>
<source>Discard blocks after verification, except most recent %1 GB (prune)</source>
- <translation>最新の%1 GBを除いて、検証後にブロックを破棄 (剪定する)</translation>
+ <translation>最新の%1 GBを除き、検証後にブロックを破棄する(剪定する)</translation>
</message>
<message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
@@ -1024,7 +1028,7 @@
</message>
<message>
<source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
- <translation>まだ表示されていない取引が関係する Bitcoin を使用しようとすると、ネットワークから認証を受けられません。</translation>
+ <translation>まだ表示されていない取引が関係する Bitcoin の使用を試みた場合、ネットワークから認証を受けられません。</translation>
</message>
<message>
<source>Number of blocks left</source>
@@ -1044,7 +1048,7 @@
</message>
<message>
<source>Progress increase per hour</source>
- <translation>一時間あたりの進捗増加</translation>
+ <translation>一時間毎の進捗増加</translation>
</message>
<message>
<source>calculating...</source>
@@ -1052,15 +1056,19 @@
</message>
<message>
<source>Estimated time left until synced</source>
- <translation>同期完了までの推定残り時間</translation>
+ <translation>同期完了までの推定時間</translation>
</message>
<message>
<source>Hide</source>
<translation>隠す</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
<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>%1は現在同期中です。ブロック チェーンの先端に到達するまで、ピアからヘッダーとブロックをダウンロードし検証します。</translation>
+ <translation>%1は現在同期中です。ブロックチェーンの先端に到達するまで、ピアからヘッダーとブロックをダウンロードし検証します。</translation>
</message>
<message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
@@ -1213,7 +1221,7 @@
</message>
<message>
<source>&amp;Spend unconfirmed change</source>
- <translation>未検証のお釣りを使用する(&amp;S)</translation>
+ <translation>未承認のお釣りを使用する(&amp;S)</translation>
</message>
<message>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
@@ -1782,10 +1790,22 @@
<translation>同期済みブロック</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>ピア選択の多様化に使用できるマップ化された自律システム。</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>マップ化された自律システム</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>ユーザーエージェント</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>ノードウィンドウ</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>現在のデータディレクトリから %1 のデバッグ用ログファイルを開きます。ログファイルが巨大な場合、数秒かかることがあります。</translation>
</message>
@@ -2258,6 +2278,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>ダスト:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>トランザクション手数料の設定を隠す</translation>
+ </message>
+ <message>
<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>ブロック内の空きよりトランザクション流量が少ない場合、マイナーや中継ノードは最低限の手数料でも処理することがあります。この最低限の手数料だけを支払っても問題ありませんが、一度トランザクションの需要がネットワークの処理能力を超えてしまった場合には、トランザクションが永久に承認されなくなってしまう可能性があることにご注意ください。</translation>
</message>
@@ -2326,6 +2350,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%1 (%2 ブロック)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>未署名で作成</translation>
+ </message>
+ <message>
<source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
<translation>オフライン%1ウォレットまたはPSBTに対応したハードウェアウォレットと合わせて使用するためのPSBT(部分的に署名されたトランザクション)を作成します。</translation>
</message>
@@ -2342,10 +2370,18 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%1 送金先: %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>このトランザクションのひな形を作成しますか?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>送金してもよろしいですか?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>トランザクション提案を確認してください。これにより、部分的に署名されたビットコイン・トランザクション(PSBT)が作成されます。これをコピーして例えばオフラインの %1 ウォレットやPSBTを扱えるハードウェアウォレットで残りの署名が出来ます。</translation>
+ </message>
+ <message>
<source>or</source>
<translation>または</translation>
</message>
@@ -2378,14 +2414,26 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>送金の確認</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>トランザクション提案を承認する</translation>
+ </message>
+ <message>
<source>Copy PSBT to clipboard</source>
<translation>PSBTをクリップボードにコピー</translation>
</message>
<message>
+ <source>Send</source>
+ <translation>送金</translation>
+ </message>
+ <message>
<source>PSBT copied</source>
<translation>PSBTがコピーされました</translation>
</message>
<message>
+ <source>Watch-only balance:</source>
+ <translation>監視限定残高</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>送金先アドレスが不正です。再確認してください。</translation>
</message>
@@ -2481,6 +2529,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>この項目を削除</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>送金する金額の単位を選択</translation>
+ </message>
+ <message>
<source>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>
<translation>手数料は送金する金額から差し引かれます。送金先には金額欄で指定した額よりも少ない Bitcoin が送られます。送金先が複数ある場合は、手数料は均等に分けられます。</translation>
</message>
@@ -3186,6 +3238,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>手数料を上乗せしてもよろしいですか?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>このトランザクションに手数料を上乗せしたひな形を作成しますか?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>現在の手数料:</translation>
</message>
@@ -3202,6 +3258,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>手数料上乗せの確認</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>トランザクションのひな型を作成できませんでした。</translation>
+ </message>
+ <message>
<source>PSBT copied</source>
<translation>PSBTがコピーされました</translation>
</message>
diff --git a/src/qt/locale/bitcoin_km.ts b/src/qt/locale/bitcoin_km.ts
index e00ea3145d..4ef5205777 100644
--- a/src/qt/locale/bitcoin_km.ts
+++ b/src/qt/locale/bitcoin_km.ts
@@ -3,87 +3,110 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>ចុចខាងស្តាំដើម្បីកែអាស្រយដ្ឋាន​ ឬ ស្លាក</translation>
+ <translation>ចុចម៉ៅស្ដាំ ដើម្បីកែសម្រួលអាសយដ្ឋាន រឺស្លាក</translation>
</message>
<message>
<source>Create a new address</source>
- <translation>បង្កើតអាស្រយដ្ឋានថ្មីមួយ</translation>
+ <translation>បង្កើតអាសយដ្ឋានថ្មីមួយ</translation>
</message>
<message>
<source>&amp;New</source>
- <translation>&amp;ថ្មី</translation>
+ <translation>ថ្មី</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>ចម្លងអាសយដ្ឋានដែលបានរើស</translation>
+ <translation>ចម្លងអាសយដ្ឋានបច្ចុប្បន្នដែលបានជ្រើសទៅក្ដារតម្រៀបរបស់ប្រព័ន្ធ</translation>
</message>
<message>
<source>&amp;Copy</source>
- <translation>&amp;ចម្លង</translation>
+ <translation>ចម្លង</translation>
</message>
<message>
<source>C&amp;lose</source>
- <translation>&amp;បិទ</translation>
+ <translation>បិទ</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>លុប​អាសយដ្ឋានដែល​បាន​រើស​ពី​បញ្ជី</translation>
+ <translation>លុប​អាសយដ្ឋានដែល​បានជ្រើស​ពី​បញ្ជី</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>វាយអាសយដ្ឋាន រឺស្លាកដើម្បីស្វែងរក</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>នាំចេញទិន្នន័យនៃថេបបច្ចុប្បន្នទៅជាឯកសារ</translation>
+ <translation>នាំចេញទិន្នន័យនៃផ្ទាំងបច្ចុប្បន្នទៅជាឯកសារ</translation>
</message>
<message>
<source>&amp;Export</source>
- <translation>&amp;នាំចេញ</translation>
+ <translation>នាំចេញ</translation>
</message>
<message>
<source>&amp;Delete</source>
- <translation>&amp;លុប</translation>
+ <translation>លុប</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>ជ្រើសរើសអាស្រយដើម្បីផ្ញើរកាកជាមួយ</translation>
+ <translation>ជ្រើសរើសអាសយដ្ឋានដើម្បីផ្ញើកាក់ទៅ</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>ជ្រើសរើសអាស្រយដើម្បីទទួលកាក់ជាមួយ
-</translation>
+ <translation>ជ្រើសរើសអាសយដ្ឋានដើម្បីទទួលយកកាក់ជាមួយ</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>&amp;ជ្រើសរើស</translation>
+ <translation>ជ្រើសរើស</translation>
</message>
<message>
<source>Sending addresses</source>
- <translation>អាសយដ្ឋានផ្ញើ</translation>
+ <translation>អាសយដ្ឋានដែលផ្ញើ</translation>
</message>
<message>
<source>Receiving addresses</source>
- <translation>អាសយដ្ឋានទទួួល</translation>
+ <translation>អាសយដ្ឋានដែលទទួល</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>ទាំងនេះ​គឺជាអាសយដ្ឋាន Bitcoin របស់អ្នកសម្រាប់ធ្វើការផ្ញើការបង់ប្រាក់។ តែងតែពិនិត្យមើលចំនួនប្រាក់ និងអាសយដ្ឋានដែលទទួល មុនពេលផ្ញើប្រាក់។</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>ទាំងនេះ​គឺជាអាសយដ្ឋាន Bitcoin របស់អ្នកសម្រាប់ធ្វើការទទួលបង់ប្រាក់។ ប្រើប៊ូតុង "បង្កើតអាសយដ្ឋានទទួលថ្មី" ស្ថិតក្នុងផ្ទាំងទទួល ដើម្បីបង្កើតអាសយដ្ឋានថ្មីមួយ។</translation>
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>&amp;ចម្លង​អាស្រយដ្ឋាន</translation>
+ <translation>ចម្លងអាសយដ្ឋាន</translation>
</message>
<message>
<source>Copy &amp;Label</source>
- <translation>ចម្លង&amp;ឡាបែល</translation>
+ <translation>ចម្លង ស្លាក</translation>
</message>
<message>
<source>&amp;Edit</source>
- <translation>&amp;កែ</translation>
+ <translation>កែសម្រួល</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>នាំចេញនូវបញ្ជីអាសយដ្ឋាន</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>ឯកសារបំបែកដោយក្បៀស (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
- <translation>បរាជ័យ​ការបញ្ជូនចេញ</translation>
+ <translation>ការនាំចេញបានបរាជ័យ</translation>
</message>
- </context>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>ទាំងនេះជាកំហុសព្យាយាម ដើម្បីរក្សាទុកបញ្ជីអាសយដ្ឋានទៅ %1 ។ សូមព្យាយាមម្ដងទៀត។</translation>
+ </message>
+</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
- <translation>ឡាបែល</translation>
+ <translation>ស្លាក</translation>
</message>
<message>
<source>Address</source>
@@ -91,65 +114,289 @@
</message>
<message>
<source>(no label)</source>
- <translation>(គ្មាន​ឡាបែល)</translation>
+ <translation>(គ្មាន​ស្លាក)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
+ <source>Passphrase Dialog</source>
+ <translation>ការហៅឃ្លាសម្ងាត់</translation>
+ </message>
+ <message>
<source>Enter passphrase</source>
- <translation>បញ្ចូលពាក្យសម្ងាត់</translation>
+ <translation>បញ្ចូលឃ្លាសម្ងាត់</translation>
</message>
<message>
<source>New passphrase</source>
- <translation>ពាក្យសម្ងាត់ថ្មី</translation>
+ <translation>ឃ្លាសម្ងាត់ថ្មី</translation>
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>វាយពាក្យសម្ងាត់ម្ដងទៀត</translation>
+ <translation>ឃ្លាសម្ងាត់ថ្នីម្ដងទៀត</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>បង្ហាញឃ្លាសម្ងាត់</translation>
</message>
<message>
<source>Encrypt wallet</source>
- <translation>កាបូប​អែនក្រីព</translation>
+ <translation>អ៊ិនគ្រីបកាបូប​ចល័ត</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>ប្រតិបត្តិការនេះ ត្រូវការឃ្លាសម្ងាត់កាបូបចល័តរបស់អ្នក ដើម្បីដោះសោរកាបូបចល័ត។</translation>
</message>
<message>
<source>Unlock wallet</source>
- <translation>ដោះសោរកាបូបលុយ</translation>
+ <translation>ដោះសោរកាបូបចល័ត</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>ប្រតិបត្តិការនេះ ត្រូវការឃ្លាសម្ងាត់កាបូបចល័តរបស់អ្នក ដើម្បីឌិគ្រីបកាបូបចល័ត។</translation>
</message>
<message>
<source>Decrypt wallet</source>
- <translation>កាបូប​​ ឌីក្រីព</translation>
+ <translation>ឌិគ្រីបកាបូបចល័ត</translation>
</message>
<message>
<source>Change passphrase</source>
- <translation>ប្ដូរពាក្យសម្ងាត់</translation>
+ <translation>ផ្លាស់ប្ដូរឃ្លាសម្ងាត់</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
- <translation>បញ្ជាក់ការសំរេចចិត្ត​​កាបូប​​ការ​អែនក្រីព</translation>
+ <translation>បញ្ជាក់ការអ៊ិនគ្រីបកាបូបចល័ត</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>ការព្រមាន៖ ប្រសិនបើអ្នកអ៊ិនគ្រីបកាបូបចល័តរបស់អ្នក ហើយអ្នកភ្លេចបាត់ឃ្លាសម្ងាត់ នោះអ្នកនិង &lt;b&gt;បាត់បង់ BITCOINS របស់អ្នកទាំងអស់&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>តើអ្នកពិតជាចង់អ៊ិនគ្រីបកាបូបចល័តរបស់អ្នកឬ?</translation>
</message>
<message>
<source>Wallet encrypted</source>
- <translation>កាបូប ដែលអែនក្រីព</translation>
+ <translation>កាបូបចល័ត ដែលបានអ៊ិនគ្រីប</translation>
+ </message>
+ <message>
+ <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>បញ្ចូលឃ្លាសម្ងាត់សំរាប់កាបូប។ សូមប្រើឃ្លាសម្ងាត់ពី១០ តួរឬច្រើនជាងនេះ, ឬ ៨ពាក្យឬច្រើនជាងនេះ</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>វាយបញ្ចូលឃ្លាសម្ងាត់ចាស់ និងឃ្លាសសម្លាត់ថ្មី សម្រាប់កាបូបចល័តរបស់អ្នក។</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>កាបូបចល័ត ដែលត្រូវបានអ៊ិនគ្រីប</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>កាបូបចល័តរបស់អ្នក ជិតត្រូវបានអ៊ិនគ្រីបហើយ។</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>កាបូបចល័តរបស់អ្នក ឥឡូវត្រូវបានអ៊ិនគ្រីប។</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>កាបូបចល័ត បានអ៊ិនគ្រីបបរាជ័យ</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>ការអ៊ិនគ្រីបកាបូបចល័ត បានបរាជ័យដោយសារកំហុសខាងក្នុង។ កាបូបចល័តរបស់អ្នកមិនត្រូវបានអ៊ិនគ្រីបទេ។</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>ឃ្លាសម្ងាត់ ដែលបានផ្គត់ផ្គង់មិនត្រូវគ្នាទេ។</translation>
</message>
<message>
<source>Wallet unlock failed</source>
- <translation>បរាជ័យដោះសោរកាបូប</translation>
+ <translation>បរាជ័យដោះសោរកាបូបចល័ត</translation>
</message>
- </context>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>ឃ្លាសម្ងាត់ ដែលបានបញ្ចូលសម្រាប់ការអ៊ិនគ្រីបកាបូបចល័តគឺមិនត្រឹមត្រូវទេ។</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>កាបូបចល័ត បានអ៊ិនគ្រីបបរាជ័យ</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>ឃ្លាសម្ងាត់នៃកាបូបចល័ត ត្រូវបានផ្លាស់ប្តូរដោយជោគជ័យ។</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>ការព្រមាន៖ ឃី Caps Lock គឺបើក!</translation>
+ </message>
+</context>
<context>
<name>BanTableModel</name>
<message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmask</translation>
+ </message>
+ <message>
<source>Banned Until</source>
- <translation>ផ្អាកដល់</translation>
+ <translation>បានហាមឃាត់រហូតដល់</translation>
</message>
</context>
<context>
<name>BitcoinGUI</name>
<message>
+ <source>Sign &amp;message...</source>
+ <translation>ស៊ីញ៉េសារ...</translation>
+ </message>
+ <message>
+ <source>Synchronizing with network...</source>
+ <translation>កំពុងធ្វើសមកាលកម្ម ជាមួយបណ្ដាញ...</translation>
+ </message>
+ <message>
+ <source>&amp;Overview</source>
+ <translation>ទិដ្ឋភាពទូទៅ</translation>
+ </message>
+ <message>
+ <source>Show general overview of wallet</source>
+ <translation>បង្ហាញទិដ្ឋភាពទូទៅនៃកាបូបចល័ត</translation>
+ </message>
+ <message>
+ <source>&amp;Transactions</source>
+ <translation>ប្រតិបត្តិការ</translation>
+ </message>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>រកមើលប្រវត្តិប្រតិបត្តិការ</translation>
+ </message>
+ <message>
+ <source>E&amp;xit</source>
+ <translation>ចាកចេញ</translation>
+ </message>
+ <message>
+ <source>Quit application</source>
+ <translation>បោះបង់កម្មវិធី</translation>
+ </message>
+ <message>
+ <source>&amp;About %1</source>
+ <translation>អំពី %1</translation>
+ </message>
+ <message>
+ <source>Show information about %1</source>
+ <translation>បង្ហាញពត៌មានអំពី %1</translation>
+ </message>
+ <message>
+ <source>About &amp;Qt</source>
+ <translation>អំពី Qt</translation>
+ </message>
+ <message>
+ <source>Show information about Qt</source>
+ <translation>បង្ហាញពត៌មានអំពី Qt</translation>
+ </message>
+ <message>
+ <source>&amp;Options...</source>
+ <translation>ជម្រើស...</translation>
+ </message>
+ <message>
+ <source>Modify configuration options for %1</source>
+ <translation>កែប្រែជម្រើសកំណត់រចនាសម្ព័ន្ធសម្រាប់ %1</translation>
+ </message>
+ <message>
+ <source>&amp;Encrypt Wallet...</source>
+ <translation>អ៊ិនគ្រីបកាបូប​ចល័ត...</translation>
+ </message>
+ <message>
+ <source>&amp;Backup Wallet...</source>
+ <translation>បម្រុងទុកកាបូបចល័ត...</translation>
+ </message>
+ <message>
+ <source>&amp;Change Passphrase...</source>
+ <translation>ផ្លាស់ប្ដូរឃ្លាសម្ងាត់...</translation>
+ </message>
+ <message>
+ <source>Open &amp;URI...</source>
+ <translation>បើក URL...</translation>
+ </message>
+ <message>
+ <source>Create Wallet...</source>
+ <translation>បង្កើតកាបូបចល័ត...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>បង្កើតកាបូបចល័តថ្មី</translation>
+ </message>
+ <message>
+ <source>Wallet:</source>
+ <translation>កាបូបចល័ត៖</translation>
+ </message>
+ <message>
+ <source>Click to disable network activity.</source>
+ <translation>ចុចដើម្បីផ្ដាច់សកម្មភាពបណ្ដាញ។</translation>
+ </message>
+ <message>
+ <source>Network activity disabled.</source>
+ <translation>សកម្មភាពបណ្ដាញត្រូវបានផ្ដាច់។</translation>
+ </message>
+ <message>
+ <source>Click to enable network activity again.</source>
+ <translation>ចុចដើម្បីភ្ជាប់សកម្មភាពនៃបណ្ដាញឡើងវិញ។</translation>
+ </message>
+ <message>
+ <source>Syncing Headers (%1%)...</source>
+ <translation>កំពុងសមកាល បឋមកថា (%1%)...</translation>
+ </message>
+ <message>
+ <source>Reindexing blocks on disk...</source>
+ <translation>កំពុងធ្ចើសន្ទស្សន៍ប្លុកឡើងវិញលើថាស...</translation>
+ </message>
+ <message>
+ <source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
+ <translation>ប្រូកស៊ី ត្រូវបាន &lt;b&gt;អនុញ្ញាត&lt;/b&gt;៖ %1</translation>
+ </message>
+ <message>
<source>Send coins to a Bitcoin address</source>
<translation>ផ្ញើកាក់ទៅកាន់ អាសយដ្ឋាន Bitcoin មួយ</translation>
</message>
+ <message>
+ <source>Backup wallet to another location</source>
+ <translation>បម្រុកទុកនូវកាបូបចល័ត ទៅទីតាំងមួយផ្សេងទៀត</translation>
+ </message>
+ <message>
+ <source>Change the passphrase used for wallet encryption</source>
+ <translation>ផ្លាស់ប្ដូរឃ្លាសម្ងាត់ ដែលបានប្រើសម្រាប់ការអ៊ិនគ្រីបកាបូបចល័ត</translation>
+ </message>
+ <message>
+ <source>&amp;Verify message...</source>
+ <translation>ផ្ទៀងផ្ទាត់សារ...</translation>
+ </message>
+ <message>
+ <source>&amp;Send</source>
+ <translation>ផ្ងើ</translation>
+ </message>
+ <message>
+ <source>&amp;Receive</source>
+ <translation>ទទួល</translation>
+ </message>
+ <message>
+ <source>&amp;Show / Hide</source>
+ <translation>បង្ហាញ/លាក់បាំង</translation>
+ </message>
+ <message>
+ <source>Show or hide the main Window</source>
+ <translation>បង្ហាញ រឺលាក់ផ្ទាំងវីនដូដើម</translation>
+ </message>
+ <message>
+ <source>&amp;File</source>
+ <translation>ឯកសារ</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>ការកំណត់</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>បញ្ហា</translation>
+ </message>
</context>
<context>
<name>CoinControlDialog</name>
@@ -163,6 +410,14 @@
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <source>Create Wallet</source>
+ <translation>បង្កើតកាបូប</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>ឈ្មោះកាបូប</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -175,6 +430,18 @@
</context>
<context>
<name>Intro</name>
+ <message>
+ <source>Welcome</source>
+ <translation>សូមស្វាគមន៍</translation>
+ </message>
+ <message>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>បញ្ហា</translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -187,6 +454,10 @@
</context>
<context>
<name>OptionsDialog</name>
+ <message>
+ <source>Error</source>
+ <translation>បញ្ហា</translation>
+ </message>
</context>
<context>
<name>OverviewPage</name>
@@ -274,6 +545,10 @@
<context>
<name>TransactionView</name>
<message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>ឯកសារបំបែកដោយក្បៀស (*.csv)</translation>
+ </message>
+ <message>
<source>Label</source>
<translation>ឡាបែល</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ko.ts b/src/qt/locale/bitcoin_ko.ts
index 7cc6797c21..b79d53a74a 100644
--- a/src/qt/locale/bitcoin_ko.ts
+++ b/src/qt/locale/bitcoin_ko.ts
@@ -188,6 +188,22 @@
<translation>지갑의 이전 비밀번호와 새로운 비밀번호를 입력하세요.</translation>
</message>
<message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>지갑을 암호화 해도 컴퓨터에 바이러스가 있을시 안전하기 않다는 것을 참고하세요.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>암호화할 지갑</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>지갑이 바로 암호화 됩니다.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>지갑이 암호화 되었습니다.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>중요: 본인 지갑 파일에서 만든 예전 백업들은 새로 생성한 암호화된 지갑 파일로 교체해야 합니다. 보안상 이유로, 새 암호화된 지갑을 사용하게 되면 이전에 암호화하지 않은 지갑 파일의 백업은 사용할 수 없게 됩니다.</translation>
</message>
@@ -310,6 +326,14 @@
<translation>&amp;URI 열기...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>지갑 생성하기...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>새로운 지갑 생성하기</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>지갑:</translation>
</message>
@@ -458,6 +482,14 @@
<translation>최신의</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>노드 창</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>노드 디버깅 및 진단 콘솔 열기 </translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>보내는 주소(&amp;S)</translation>
</message>
@@ -466,6 +498,10 @@
<translation>받는 주소(&amp;R)</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>bitcoin: URI 열기</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>지갑 열기</translation>
</message>
@@ -526,6 +562,10 @@
<translation>오류: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>경고: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>날짜: %1
@@ -747,10 +787,50 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>지갑 &lt;b&gt;%1&lt;/b&gt; 생성중...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>지갑 생성하기 실패</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>지갑 생성 경고</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>지갑 생성하기</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>지갑 이름</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>지갑 암호화하기. 해당 지갑은 당신이 설정한 문자열 비밀번호로 암호화될 겁니다.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>지갑 암호화</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>개인키 비활성화 하기</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>빈 지갑 만들기</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>생성하기</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -914,7 +994,11 @@
<source>(of %n GB needed)</source>
<translation><numerusform>(%n GB가 필요)</numerusform></translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(Full 체인이 되려면 %n GB 가 필요합니다)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -962,6 +1046,10 @@
<translation>숨기기</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>알 수 없음. 헤더 동기화 중(%1,%2%)...</translation>
</message>
@@ -969,6 +1057,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>비트코인 URI 열기</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -976,6 +1068,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>지갑 열기 실패</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>지갑 열기 경고</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>기본 지갑</translation>
</message>
@@ -1353,6 +1453,18 @@
<translation>'bitcoin://"은 잘못된 URI입니다. 'bitcoin:'을 사용하십시오.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>BIP70을 지원하지 않아서 지불 요청을 처리할 수 없습니다.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>BIP70의 보안적 결함 때문에 상점에 불문하고 "지갑을 바꾸라"라는 권고 또는 지시는 대부분의 경우 무시하는 방법을 강력하게 권장합니다.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>만약 이 오류 메시지가 보인다면, 상점에 BIP21이 호환되는 URI를 제공해달라고 요청해주세요.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>잘못된 지불 주소 %1</translation>
</message>
@@ -1514,6 +1626,10 @@
<translation>URI를 QR 코드로 인코딩하는 중 오류가 발생했습니다.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>QR 코드를 지원하지 않습니다.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>QR코드 저장</translation>
</message>
@@ -1657,6 +1773,10 @@
<translation>유저 에이전트</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>노드 창</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>%1 디버그 로그파일을 현재 데이터 폴더에서 엽니다. 용량이 큰 로그 파일들은 몇 초가 걸릴 수 있습니다.</translation>
</message>
@@ -1868,6 +1988,10 @@
<translation>요청할 금액 입력칸으로 선택 사항입니다. 빈 칸으로 두거나 특정 금액이 필요하지 않는 경우 0을 입력하세요.</translation>
</message>
<message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;새 받을 주소 생성하기</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>양식의 모든 필드를 지웁니다.</translation>
</message>
@@ -2117,6 +2241,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>더스트:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>거래 수수료 설정 숨기기</translation>
+ </message>
+ <message>
<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>거래량이 블록에 남은 공간보다 적은 경우에는 채굴자나 중계 노드들이 최소 수수료를 허용할 수 있습니다. 최소 수수료만 지불하는건 괜찮지만, 네트워크가 처리할 수 있는 용량을 넘는 비트코인 거래가 있을 경우에는 이 거래가 승인이 안될 수 있다는 점을 유의하세요.</translation>
</message>
@@ -2185,6 +2313,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%1(%2 블록)</translation>
</message>
<message>
+ <source> from wallet '%1'</source>
+ <translation>%1 지갑에서</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1을(를) %2(으)로</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1을(를) %2(으)로</translation>
</message>
@@ -2217,10 +2353,30 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>총액</translation>
</message>
<message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>수령인 목록을 검토하려면 "거래 세부 내역 보기" 를 클릭하십시오</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>코인 전송을 확인</translation>
</message>
<message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>PSBT를 클립보드에 복사</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>보내기</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT 복사됨</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>조회전용 잔액:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>수령인 주소가 정확하지 않습니다. 재확인 바랍니다</translation>
</message>
@@ -2474,6 +2630,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>지갑 잠금 해제를 취소했습니다.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>오류 없음</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>입력한 주소에 대한 개인키가 없습니다.</translation>
</message>
@@ -2648,6 +2808,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>출력 인덱스</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(인증서가 확인되지 않았습니다)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>상점</translation>
</message>
@@ -2971,6 +3135,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>지갑 닫기</translation>
</message>
<message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>정말로 지갑 &lt;i&gt;%1&lt;/i&gt; 을 닫겠습니까?</translation>
+ </message>
+ <message>
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<translation>블록축소를 하고 지갑을 너무 오랫동안 닫으면 체인 전체를 다시 동기화해야 할 수도 있습니다.</translation>
</message>
@@ -3017,6 +3185,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>수수료 상향 승인</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>PSBT 복사됨</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>거래에 서명 할 수 없습니다.</translation>
</message>
@@ -3183,6 +3355,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>손상된 블록 데이터베이스가 감지되었습니다</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>asmap file %s 을/를 찾을 수 없습니다</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>asmap file %s 을/를 파싱할 수 없습니다</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>블록 데이터베이스를 다시 생성하시겠습니까?</translation>
</message>
@@ -3239,6 +3419,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>무결성 확인 초기화가 실패했습니다. %s가 종료됩니다.</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>잘못된 P2P 권한: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>유효하지 않은 금액 -%s=&lt;amount&gt;: '%s'</translation>
</message>
@@ -3255,6 +3439,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>지정한 블록 디렉토리 "%s" 가 존재하지 않습니다.</translation>
</message>
<message>
+ <source>Unknown change type '%s'</source>
+ <translation>알 수 없는 변경 형식 '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>txindex 데이터베이스 업테이트중</translation>
</message>
@@ -3263,6 +3451,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>P2P 주소 불러오는 중...</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>오류: 디스크 공간이 부족합니다!</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>추방리스트를 불러오는 중...</translation>
</message>
@@ -3371,6 +3563,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>-whitebind: '%s' 를 이용하여 포트를 지정해야 합니다</translation>
</message>
<message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>블록 축소 모드는 -blockfileterindex와 호환되지 않습니다.</translation>
+ </message>
+ <message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation>시스템 한계로 인하여 -maxconnections를 %d 에서 %d로 줄였습니다.</translation>
</message>
@@ -3429,6 +3625,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>초기 키값 생성 불가</translation>
</message>
<message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>알 수 없는 -blockfileterindex 값 %s.</translation>
+ </message>
+ <message>
<source>Verifying wallet(s)...</source>
<translation>지갑 검증중...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts
index afdc7ffb8d..7024bff0a2 100644
--- a/src/qt/locale/bitcoin_lt.ts
+++ b/src/qt/locale/bitcoin_lt.ts
@@ -70,6 +70,10 @@
<translation>Tai yra jūsų Bitcoin adresai išeinantiems mokėjimams. Visada pasitikrinkite sumą ir gavėjo adresą prieš siunčiant lėšas.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Tai jūsų Bitcoin mokėjimų gavimo adresai. Naudokite 'Sukurti naują gavimo adresą' migtuką gavimų skirtuke kad sukurtumėt nauja adresą.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopijuoti adresą</translation>
</message>
@@ -176,6 +180,10 @@
<translation>Piniginė užšifruota</translation>
</message>
<message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Įveskite seną ir naują slaptažodį.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>SVARBU: Betkokios ankstesnės jūsų piniginės atsarginės kopijos turėtų būti pakeistos naujai sugeneruotu, užšifruotu piniginės failu. Dėl saugumo sumetimų, anstesnės neužšifruotos piniginės kopijos failas taps nenaudingu nuo momento, kai nauja ir užšifruota piniginė bus pradėta naudoti.</translation>
</message>
@@ -298,6 +306,14 @@
<translation>Atidaryti &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Sukurti piniginę...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Sukurti naują piniginę</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Piniginė</translation>
</message>
@@ -478,6 +494,10 @@
<translation>numatyta piniginė</translation>
</message>
<message>
+ <source>No wallets available</source>
+ <translation>Piniginių nėra</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Langas</translation>
</message>
@@ -510,6 +530,10 @@
<translation>Klaida: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>Įspėjimas: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Data: %1
@@ -731,10 +755,50 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Sukuriama Piniginė &lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Piniginės sukurimas nepavyko</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Piniginės sukurimo įspėjimas</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Sukurti Piniginę</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Piniginės Pavadinimas</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Užkoduoti piniginę. Piniginė bus užkoduota jūsų pasirinkta slapta fraze.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Užkoduoti Piniginę</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Atjungti Privačius Raktus</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Sukurti Tuščia Piniginę</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Sukurti</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -867,6 +931,10 @@
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Ištrinti blokus po patikrinimo, išskyrus paskutinius %1 GB (nukarpimas)</translation>
+ </message>
+ <message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation>Šiame kataloge bus saugomi bent %1 GB duomenų, kurie laikui bėgant didės.</translation>
</message>
@@ -960,6 +1028,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Piniginės atidarymas nepavyko</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Piniginės atidarymo įspėjimas</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>numatyta piniginė</translation>
</message>
@@ -1498,6 +1574,10 @@
<translation>Klaida koduojant URI į QR kodą.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>QR kodas nepalaikomas</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>Įrašyti QR kodą</translation>
</message>
@@ -2169,6 +2249,14 @@ Pastaba: Kadangi mokestis apskaičiuojamas pagal baitą, mokestis už „100 sat
<translation>%1 (%2 blokai)</translation>
</message>
<message>
+ <source> from wallet '%1'</source>
+ <translation>iš piniginės '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>'%1' į '%2'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 iki %2</translation>
</message>
@@ -2918,6 +3006,10 @@ Pastaba: Kadangi mokestis apskaičiuojamas pagal baitą, mokestis už „100 sat
<source>Close wallet</source>
<translation>Uždaryti Piniginę</translation>
</message>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Ar tikrai norite uždaryti piniginę &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
@@ -3091,6 +3183,10 @@ Pastaba: Kadangi mokestis apskaičiuojamas pagal baitą, mokestis už „100 sat
<translation>Importuojama...</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Nežinomas adreso tipas '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Txindex duomenų bazės atnaujinimas</translation>
</message>
@@ -3099,6 +3195,10 @@ Pastaba: Kadangi mokestis apskaičiuojamas pagal baitą, mokestis už „100 sat
<translation>Užkraunami P2P adresai...</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Klaida: Diske mažai vietos!</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>Įkeliamas draudžiamas sąrašas...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_lv.ts b/src/qt/locale/bitcoin_lv.ts
index a5c96c1a2a..360b2735c0 100644
--- a/src/qt/locale/bitcoin_lv.ts
+++ b/src/qt/locale/bitcoin_lv.ts
@@ -50,6 +50,18 @@
<translation>Izvēlies adresi ar kuru saņemt bitcoins</translation>
</message>
<message>
+ <source>C&amp;hoose</source>
+ <translation>Izvēlēties</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Adrešu nosūtīšana</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Adrešu saņemšana</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopēt adresi</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ml.ts b/src/qt/locale/bitcoin_ml.ts
index 299d81bf75..2bb3380659 100644
--- a/src/qt/locale/bitcoin_ml.ts
+++ b/src/qt/locale/bitcoin_ml.ts
@@ -67,6 +67,10 @@
<translation>പേയ്മെന്റുകൾ അയയ്ക്കുന്നതിനുള്ള നിങ്ങളുടെ ബിറ്റ്കോയിൻ വിലാസങ്ങളാണ് ഇവ. നാണയങ്ങൾ അയയ്ക്കുന്നതിനുമുമ്പ് എല്ലായ്പ്പോഴും തുകയും സ്വീകരിക്കുന്ന വിലാസവും പരിശോധിക്കുക.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>ഇവയാണ് പണം സ്വീകരിയ്ക്കുന്നതിനായുള്ള താങ്കളുടെ വിലാസങ്ങൾ. പുതിയ വിലാസങ്ങൾ കൂട്ടിച്ചേർക്കുന്നതിനായി ' പുതിയ വിലാസം സൃഷ്ടിയ്ക്കുക ' എന്ന ബട്ടൺ അമർത്തുക.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;വിലാസം പകർത്തുക</translation>
</message>
@@ -129,6 +133,10 @@
<translation>പുതിയ രഹസ്യപദപ്രയോഗം ആവർത്തിക്കുക</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>രഹസ്യപദം കാണിക്കുക </translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>വാലറ്റ് എൻക്രിപ്റ്റ് ചെയ്യുക</translation>
</message>
@@ -160,16 +168,174 @@
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
<translation>മുന്നറിയിപ്പ്: നിങ്ങളുടെ വാലറ്റ് എൻക്രിപ്റ്റ് ചെയ്ത് പാസ്ഫ്രെയ്സ് നഷ്ടപ്പെടുകയാണെങ്കിൽ, നിങ്ങളുടെ എല്ലാ ബിറ്റ്കൊയിനുകളും നഷ്ടപ്പെടും!</translation>
</message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>വാലറ്റ് എന്ക്രിപ്റ് ചെയ്തു കഴിഞ്ഞു .</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>വാലെറ്റിന്റെ പഴയ രഹസ്യപദവും പുതിയ രഹസ്യപദവും നൽകുക.</translation>
+ </message>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>BitcoinGUI</name>
+ <message>
+ <source>Browse transaction history</source>
+ <translation>ഇടപാടുകളുടെ ചരിത്രം പരിശോധിയ്ക്കുക</translation>
+ </message>
+ <message>
+ <source>Error</source>
+ <translation>പിശക് </translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>മുന്നറിയിപ്പ് </translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>വിവരം </translation>
+ </message>
+ <message>
+ <source>Open Wallet</source>
+ <translation>വാലറ്റ് തുറക്കുക </translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>ഒരു വാലറ്റ് തുറക്കുക </translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>വാലറ്റ് പൂട്ടുക </translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>വാലറ്റ് പൂട്ടുക </translation>
+ </message>
+ <message>
+ <source>default wallet</source>
+ <translation>സ്ഥിരം ആയ വാലറ്റ്</translation>
+ </message>
+ <message>
+ <source>No wallets available</source>
+ <translation>വാലറ്റ് ഒന്നും ലഭ്യം അല്ല </translation>
+ </message>
+ <message>
+ <source>Minimize</source>
+ <translation>ചെറുതാക്കുക </translation>
+ </message>
+ <message>
+ <source>Zoom</source>
+ <translation>വലുതാക്കുക </translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>മുഖ്യ ജാലകം </translation>
+ </message>
+ <message>
+ <source>Connecting to peers...</source>
+ <translation>സുഹൃത്തുക്കളും ആയി കണക്ട് ചെയ്യുന്നു ...</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>തെറ്റ് : %1 </translation>
+ </message>
+ <message>
+ <source>Warning: %1</source>
+ <translation>മുന്നറിയിപ്പ് : %1 </translation>
+ </message>
+ <message>
+ <source>Date: %1
+</source>
+ <translation>തീയതി: %1
+</translation>
+ </message>
+ <message>
+ <source>Amount: %1
+</source>
+ <translation>തുക : %1
+</translation>
+ </message>
+ <message>
+ <source>Wallet: %1
+</source>
+ <translation>വാലറ്റ്: %1
+</translation>
+ </message>
+ <message>
+ <source>Label: %1
+</source>
+ <translation>കുറിപ്പ് : %1
+</translation>
+ </message>
+ <message>
+ <source>Address: %1
+</source>
+ <translation>മേൽവിലാസം : %1
+</translation>
+ </message>
+ <message>
+ <source>Sent transaction</source>
+ <translation>അയച്ച ഇടപാടുകൾ </translation>
+ </message>
+ <message>
+ <source>Incoming transaction</source>
+ <translation>വരവ്വ് വെച്ച ഇടപാടുകൾ </translation>
+ </message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
+ <source>Coin Selection</source>
+ <translation>കോയിൻ തിരഞ്ഞെടുക്കൽ </translation>
+ </message>
+ <message>
+ <source>Quantity:</source>
+ <translation>നിര്‍ദ്ധിഷ്‌ടസംഖ്യ / അളവ് :</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>ബൈറ്റ്സ്:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>തുക:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>ഫീസ്‌ / പ്രതിഫലം :</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>പട്ടിക </translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>തുക </translation>
+ </message>
+ <message>
+ <source>Received with label</source>
+ <translation>അടയാളത്തോടുകൂടി ലഭിച്ചു </translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>മേൽവിലാസത്തോടുകൂടി ലഭിച്ചു </translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>തീയതി </translation>
+ </message>
+ <message>
+ <source>Confirmations</source>
+ <translation>സ്ഥിതീകരണങ്ങൾ </translation>
+ </message>
+ <message>
+ <source>Confirmed</source>
+ <translation>സ്ഥിതീകരിച്ചു</translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(ലേബൽ ഇല്ല)</translation>
</message>
@@ -179,33 +345,77 @@
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <source>Create Wallet</source>
+ <translation>വാലറ്റ് / പണസഞ്ചി സൃഷ്ടിക്കുക :</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
+ <message>
+ <source>name</source>
+ <translation>നാമധേയം / പേര് </translation>
+ </message>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
+ <message>
+ <source>Error</source>
+ <translation>പിശക് </translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
+ <message>
+ <source>Unknown...</source>
+ <translation>അജ്ഞാതമായ </translation>
+ </message>
+ <message>
+ <source>Progress</source>
+ <translation>പുരോഗതി</translation>
+ </message>
+ <message>
+ <source>calculating...</source>
+ <translation>കണക്കായ്ക്കിക്കൊണ്ടിരിക്കുന്നു</translation>
+ </message>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OpenWalletActivity</name>
+ <message>
+ <source>default wallet</source>
+ <translation>സ്ഥിരം ആയ വാലറ്റ്</translation>
+ </message>
</context>
<context>
<name>OptionsDialog</name>
+ <message>
+ <source>Error</source>
+ <translation>പിശക് </translation>
+ </message>
</context>
<context>
<name>OverviewPage</name>
+ <message>
+ <source>Available:</source>
+ <translation>ലഭ്യമായ</translation>
+ </message>
+ <message>
+ <source>Spendable:</source>
+ <translation>വിനിയോഗിക്കാവുന്നത് / ചെലവാക്കാവുന്നത് </translation>
+ </message>
+ <message>
+ <source>Recent transactions</source>
+ <translation>സമീപ കാല ഇടപാടുകൾ</translation>
+ </message>
</context>
<context>
<name>PaymentServer</name>
@@ -215,6 +425,14 @@
</context>
<context>
<name>QObject</name>
+ <message>
+ <source>Amount</source>
+ <translation>തുക </translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>തെറ്റ് : %1 </translation>
+ </message>
</context>
<context>
<name>QRImageWidget</name>
@@ -232,6 +450,10 @@
<translation>വിലാസം</translation>
</message>
<message>
+ <source>Amount</source>
+ <translation>തുക </translation>
+ </message>
+ <message>
<source>Label</source>
<translation>ലേബൽ</translation>
</message>
@@ -239,6 +461,10 @@
<context>
<name>RecentRequestsTableModel</name>
<message>
+ <source>Date</source>
+ <translation>തീയതി </translation>
+ </message>
+ <message>
<source>Label</source>
<translation>ലേബൽ</translation>
</message>
@@ -250,6 +476,26 @@
<context>
<name>SendCoinsDialog</name>
<message>
+ <source>Quantity:</source>
+ <translation>നിര്‍ദ്ധിഷ്‌ടസംഖ്യ / അളവ് :</translation>
+ </message>
+ <message>
+ <source>Bytes:</source>
+ <translation>ബൈറ്റ്സ്:</translation>
+ </message>
+ <message>
+ <source>Amount:</source>
+ <translation>തുക:</translation>
+ </message>
+ <message>
+ <source>Fee:</source>
+ <translation>ഫീസ്‌ / പ്രതിഫലം :</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>പെയ്മെന്റിനുള്ള അഭ്യർത്ഥന കാലഹരണപ്പെട്ടു പോയിരിക്കുന്നു. </translation>
+ </message>
+ <message>
<source>(no label)</source>
<translation>(ലേബൽ ഇല്ല)</translation>
</message>
@@ -268,6 +514,14 @@
</context>
<context>
<name>TransactionDesc</name>
+ <message>
+ <source>Date</source>
+ <translation>തീയതി </translation>
+ </message>
+ <message>
+ <source>Amount</source>
+ <translation>തുക </translation>
+ </message>
</context>
<context>
<name>TransactionDescDialog</name>
@@ -275,6 +529,10 @@
<context>
<name>TransactionTableModel</name>
<message>
+ <source>Date</source>
+ <translation>തീയതി </translation>
+ </message>
+ <message>
<source>Label</source>
<translation>ലേബൽ</translation>
</message>
@@ -290,6 +548,14 @@
<translation>കോമയാൽ വേർതിരിച്ച ഫയൽ (* .csv)</translation>
</message>
<message>
+ <source>Confirmed</source>
+ <translation>സ്ഥിതീകരിച്ചു</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>തീയതി </translation>
+ </message>
+ <message>
<source>Label</source>
<translation>ലേബൽ</translation>
</message>
@@ -307,13 +573,21 @@
</context>
<context>
<name>WalletController</name>
+ <message>
+ <source>Close wallet</source>
+ <translation>വാലറ്റ് പൂട്ടുക </translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
- </context>
+ <message>
+ <source>default wallet</source>
+ <translation>സ്ഥിരം ആയ വാലറ്റ്</translation>
+ </message>
+</context>
<context>
<name>WalletView</name>
<message>
diff --git a/src/qt/locale/bitcoin_mr_IN.ts b/src/qt/locale/bitcoin_mr_IN.ts
index 4a74385b29..05e85ec96c 100644
--- a/src/qt/locale/bitcoin_mr_IN.ts
+++ b/src/qt/locale/bitcoin_mr_IN.ts
@@ -30,6 +30,10 @@
<translation>सध्याचा निवडलेला पत्ता यादीमधून काढून टाका</translation>
</message>
<message>
+ <source>Enter address or label to search</source>
+ <translation>शोधण्यासाठी पत्ता किंवा लेबल दाखल करा</translation>
+ </message>
+ <message>
<source>Export the data in the current tab to a file</source>
<translation>सध्याच्या टॅबमधील डेटा एका फाईलमध्ये एक्स्पोर्ट करा</translation>
</message>
@@ -66,6 +70,10 @@
<translation>पैसे पाठविण्यासाठीचे हे तुमचे बिटकॉईन पत्त्ते आहेत. नाणी पाठविण्यापूर्वी नेहमी रक्कम आणि प्राप्त होणारा पत्ता तपासून पहा.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>पैसे प्राप्त करण्यासाठीचे हे आपले बिटकॉइन पत्ते आहेत. नवीन पत्ते तयार करण्यासाठी प्राप्त टॅबमधील 'नवीन प्राप्त करण्याचा पत्ता तयार करा' बटण वापरा.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;पत्ता कॉपी करा</translation>
</message>
@@ -77,10 +85,30 @@
<source>&amp;Edit</source>
<translation>&amp;संपादित</translation>
</message>
+ <message>
+ <source>Export Address List</source>
+ <translation>पत्त्याची निर्यात करा</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>निर्यात अयशस्वी</translation>
+ </message>
</context>
<context>
<name>AddressTableModel</name>
- </context>
+ <message>
+ <source>Label</source>
+ <translation>लेबल</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>पत्ता</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(लेबल नाही)</translation>
+ </message>
+</context>
<context>
<name>AskPassphraseDialog</name>
</context>
@@ -92,6 +120,10 @@
</context>
<context>
<name>CoinControlDialog</name>
+ <message>
+ <source>(no label)</source>
+ <translation>(लेबल नाही)</translation>
+ </message>
</context>
<context>
<name>CreateWalletActivity</name>
@@ -146,13 +178,33 @@
</context>
<context>
<name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>पत्ता</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>लेबल</translation>
+ </message>
</context>
<context>
<name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>लेबल</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(लेबल नाही)</translation>
+ </message>
</context>
<context>
<name>SendCoinsDialog</name>
- </context>
+ <message>
+ <source>(no label)</source>
+ <translation>(लेबल नाही)</translation>
+ </message>
+</context>
<context>
<name>SendCoinsEntry</name>
</context>
@@ -173,9 +225,29 @@
</context>
<context>
<name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>लेबल</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(लेबल नाही)</translation>
+ </message>
</context>
<context>
<name>TransactionView</name>
+ <message>
+ <source>Label</source>
+ <translation>लेबल</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>पत्ता</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>निर्यात अयशस्वी</translation>
+ </message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
diff --git a/src/qt/locale/bitcoin_my.ts b/src/qt/locale/bitcoin_my.ts
index 1b5248ba1a..ec03afa3b7 100644
--- a/src/qt/locale/bitcoin_my.ts
+++ b/src/qt/locale/bitcoin_my.ts
@@ -49,6 +49,18 @@
</context>
<context>
<name>BitcoinGUI</name>
+ <message>
+ <source>Error</source>
+ <translation>အမှား</translation>
+ </message>
+ <message>
+ <source>Warning</source>
+ <translation>သတိပေးချက်</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>အချက်အလက်</translation>
+ </message>
</context>
<context>
<name>CoinControlDialog</name>
@@ -70,6 +82,10 @@
</context>
<context>
<name>Intro</name>
+ <message>
+ <source>Error</source>
+ <translation>အမှား</translation>
+ </message>
</context>
<context>
<name>ModalOverlay</name>
@@ -82,6 +98,10 @@
</context>
<context>
<name>OptionsDialog</name>
+ <message>
+ <source>Error</source>
+ <translation>အမှား</translation>
+ </message>
</context>
<context>
<name>OverviewPage</name>
diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts
index 7119de29ae..f24c1aa3b6 100644
--- a/src/qt/locale/bitcoin_nb.ts
+++ b/src/qt/locale/bitcoin_nb.ts
@@ -70,6 +70,10 @@
<translation>Dette er dine Bitcoin adresser for å sende å sende betalinger. Husk å sjekke beløp og mottager adresser før du sender mynter.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Dette er dine Bitcoin adresse for å betalinger. Det er anbefalt å bruke en my mottager adresse for hver transakjon.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopier adresse</translation>
</message>
@@ -132,6 +136,10 @@
<translation>Repeter passorsetningen</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Vis adgangsfrase</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Krypter lommeboken</translation>
</message>
@@ -172,10 +180,30 @@
<translation>Lommeboken er kryptert</translation>
</message>
<message>
+ <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>Angi den nye passordfrasen for lommeboken.&lt;br/&gt; Vennglist du bruker en passordfrase &lt;b&gt; ti eller tilfeldige tegn &lt;/b&gt;, eller &lt;b&gt; åtte eller flere ord.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Svriv inn den gamle passfrasen og den nye passordfrasen for lommeboken.</translation>
+ </message>
+ <message>
<source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
<translation>Husk at å kryptere lommeboken ikke vil beskytte dine bitcoins fullstendig fra å bli stjålet av skadevare som infiserer datamaskinen din.</translation>
</message>
<message>
+ <source>Wallet to be encrypted</source>
+ <translation>Lommebok som skal bli kryptert</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Din lommebok er i ferd med å bli kryptert.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Din lommebok er nå kryptert.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>VIKTIG: Alle tidligere sikkerhetskopier du har tatt av lommebokfilen bør erstattes med den nye krypterte lommebokfilen. Av sikkerhetsgrunner vil tidligere sikkerhetskopier av lommebokfilen bli ubrukelige når du begynner å bruke den ny kypterte lommeboken.</translation>
</message>
@@ -298,6 +326,14 @@
<translation>Åpne &amp;URI</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Lag lommebok...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Lag en ny lommebok</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Lommebok:</translation>
</message>
@@ -446,6 +482,14 @@
<translation>Oppdatert</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Nodevindu</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Åpne nodens konsoll for feilsøk og diagnostikk</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Avsender adresser</translation>
</message>
@@ -454,6 +498,10 @@
<translation>&amp;Mottaker adresser</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Åpne en bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Åpne Lommebok</translation>
</message>
@@ -514,6 +562,10 @@
<translation>Feil: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation>Advarsel: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Dato: %1
@@ -735,10 +787,58 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Lager lommebok &lt;b&gt;%1&lt;b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Lage lommebok feilet</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Lag lommebokvarsel</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Lag lommebok</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Lommeboknavn</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Krypter lommeboken. Lommeboken blir kryptert med en passordfrase du velger.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Krypter Lommebok</translation>
+ </message>
+ <message>
+ <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>Deaktiver private nøkler for denne lommeboken. Lommebøker med private nøkler er deaktivert vil ikke ha noen private nøkler og kan ikke ha en HD seed eller importerte private nøkler. Dette er ideelt for loomebøker som kun er klokker.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Deaktiver Private Nøkler</translation>
+ </message>
+ <message>
+ <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>Lag en tom lommebok. Tomme lommebøker har i utgangspunktet ikke private nøkler eller skript. Private nøkler og adresser kan importeres, eller et HD- frø kan angis på et senere tidspunkt.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Lag Tom Lommebok</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Opprett</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -782,6 +882,10 @@
<translation>Adresse "%1" eksisterer allerede som en mottaksadresse merket "%2" og kan derfor ikke bli lagt til som en sendingsadresse.</translation>
</message>
<message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Den oppgitte adressen ''%1'' er allerede i adresseboken med etiketten ''%2''.</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Kunne ikke låse opp lommebok.</translation>
</message>
@@ -847,6 +951,10 @@
<translation>Når du klikker OK, vil %1 starte nedlasting og behandle hele den %4 blokkjeden (%2GB) fra de eldste transaksjonene i %3 når %4 først startet.</translation>
</message>
<message>
+ <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>Gjenoppretting av denne innstillingen krever at du laster ned hele blockchain på nytt. Det er raskere å laste ned hele kjeden først og beskjære den senere Deaktiver noen avanserte funksjoner.</translation>
+ </message>
+ <message>
<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>Den initielle synkroniseringen er svært krevende, og kan forårsake problemer med maskinvaren i datamaskinen din som du tidligere ikke merket. Hver gang du kjører %1 vil den fortsette nedlastingen der den sluttet.</translation>
</message>
@@ -867,6 +975,10 @@
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Kast blokker etter bekreftelse, bortsett fra de siste %1 GB (sviske)</translation>
+ </message>
+ <message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation>Minst %1 GB data vil bli lagret i denne mappen og den vil vokse over tid.</translation>
</message>
@@ -945,10 +1057,26 @@
<source>Hide</source>
<translation>Skjul</translation>
</message>
- </context>
+ <message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
+ <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>%1 synkroniseres for øyeblikket. Den vil laste ned overskrifter og blokker fra jevnaldrende og validere dem til de når spissen av blokkjeden.</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1, %2%)...</source>
+ <translation>Ukjent.Synkroniser overskrifter (%1,%2%)...</translation>
+ </message>
+</context>
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Åpne bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -956,6 +1084,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Åpne lommebok feilet</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Advasel om åpen lommebok.</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>standard lommebok</translation>
</message>
@@ -1039,10 +1175,22 @@
<translation>&amp;Nettverk</translation>
</message>
<message>
+ <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>Deaktiver noen avanserte funksjoner, men alle blokker vil fortsatt være fullglyldig. Gjenoppretting av denne innstillingen krever at du laster ned hele blockchain på nytt. Faktisk diskbruk kan være noe høvere.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Beskjær og blokker lagring til</translation>
+ </message>
+ <message>
<source>GB</source>
<translation>GB</translation>
</message>
<message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Gjenoppretting av denne innstillingen krever at du laster ned hele blockchain på nytt</translation>
+ </message>
+ <message>
<source>MiB</source>
<translation>MiB</translation>
</message>
@@ -1171,6 +1319,10 @@
<translation>Tredjepart transaksjon URLer</translation>
</message>
<message>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation>Alternativer som er satt i denne dialogboksen overstyres av kommandolinjen eller i konfigurasjonsfilen:</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>&amp;OK</translation>
</message>
@@ -1313,6 +1465,22 @@
<translation>URI-håndtering</translation>
</message>
<message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin: //' er ikke en gyldig URI. Bruk 'bitcoin:' i stedet.</translation>
+ </message>
+ <message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Kan ikke behandle betalingsforespørsel fordi BIP70 ikke støttes.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>På grunn av utbredte sikkerhetsfeil i BIP70 anbefales det på det sterkeste at alle selgerinstruksjoner for å bytte lommebok ignoreres.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Hvis du mottar denne feilen, bør du be selgeren gi en BIP21-kompatibel URI.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Ugyldig betalingsadresse %1</translation>
</message>
@@ -1470,6 +1638,10 @@
<translation>Feil ved koding av URI til QR-kode.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>Støtte for QR kode ikke tilgjengelig.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>Lagre QR-kode</translation>
</message>
@@ -1505,6 +1677,10 @@
<translation>Datamappe</translation>
</message>
<message>
+ <source>Blocksdir</source>
+ <translation>Blocksdir</translation>
+ </message>
+ <message>
<source>Startup time</source>
<translation>Oppstartstidspunkt</translation>
</message>
@@ -1597,10 +1773,22 @@
<translation>Synkroniserte Blokker</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Det kartlagte autonome systemet som brukes til å diversifisere valg av fagfeller.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Kartlagt AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Brukeragent</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Nodevindu</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Åpne %1-feilrettingsloggfila fra gjeldende datamappe. Dette kan ta et par sekunder for store loggfiler.</translation>
</message>
@@ -1741,6 +1929,14 @@
<translation>Nettverksaktivitet avskrudd</translation>
</message>
<message>
+ <source>Executing command without any wallet</source>
+ <translation>Utfør kommando uten noen lommebok</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Utfør kommando med lommebok "%1"</translation>
+ </message>
+ <message>
<source>(node id: %1)</source>
<translation>(node id: %1)</translation>
</message>
@@ -1804,6 +2000,18 @@
<translation>Et valgfritt beløp å etterspørre. La stå tomt eller null for ikke å etterspørre et spesifikt beløp.</translation>
</message>
<message>
+ <source>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>
+ <translation>En valgfri etikett for å knytte til den nye mottaksadressen (brukt av deg for å identifisere en faktura). Det er også knyttet til betalingsforespørselen.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>En valgfri melding som er knyttet til betalingsforespørselen og kan vises til avsenderen.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Lag ny mottakeradresse</translation>
+ </message>
+ <message>
<source>Clear all fields of the form.</source>
<translation>Fjern alle felter fra skjemaet.</translation>
</message>
@@ -1812,6 +2020,14 @@
<translation>Fjern</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Innfødte segwit-adresser (også kalt Bech32 eller BIP-173) reduserer transaksjonsgebyrene senere og gir bedre beskyttelse mot skrivefeil, men gamle lommebøker støtter dem ikke. Når du ikke har merket av, opprettes en adresse som er kompatibel med eldre lommebøker.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Generer nativ segwit (Bech32) adresse</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Etterspurt betalingshistorikk</translation>
</message>
@@ -2001,6 +2217,14 @@
<translation>Advarsel: Gebyroverslag er ikke tilgjengelig for tiden.</translation>
</message>
<message>
+ <source>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.</source>
+ <translation>Spesifiser en tilpasset avgift per kB (1000 byte) av transaksjonens virtuelle størrelse.
+
+Merk: Siden avgiften er beregnet per byte-basis, vil et gebyr på "100 satoshis per kB" for en transaksjonsstørrelse på 500 byte (halvparten av 1 kB) til slutt gi et gebyr på bare 50 satoshis.</translation>
+ </message>
+ <message>
<source>per kilobyte</source>
<translation>per kilobyte</translation>
</message>
@@ -2037,6 +2261,18 @@
<translation>Støv:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Skjul innstillinger for transaksjonsgebyr</translation>
+ </message>
+ <message>
+ <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>Når det er mindre transaksjonsvolum enn plass i blokkene, kan gruvearbeidere så vel som videresende noder håndheve et minimumsgebyr. Å betale bare denne minsteavgiften er helt greit, men vær klar over at dette kan resultere i en aldri bekreftende transaksjon når det er større etterspørsel etter bitcoin-transaksjoner enn nettverket kan behandle.</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>For lavt gebyr kan føre til en transaksjon som aldri bekreftes (les verktøytips)</translation>
+ </message>
+ <message>
<source>Confirmation time target:</source>
<translation>Bekreftelsestidsmål:</translation>
</message>
@@ -2097,10 +2333,18 @@
<translation>%1 (%2 blokker)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Cr &amp; eate Usignert</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 til %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Vil du utarbeide denne transaksjonen?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Er du sikker på at du vil sende?</translation>
</message>
@@ -2129,10 +2373,34 @@
<translation>Totalbeløp</translation>
</message>
<message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>For å se gjennom mottakerlisten, klikk "Vis detaljer ..."</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Bekreft forsendelse av mynter</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Bekreft transaksjonsforslaget</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Kopier PSBT til utklippstavlen</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Send</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopiert</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Kun-observer balanse:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Mottakeradressen er ikke gyldig. Sjekk den igjen.</translation>
</message>
@@ -2228,6 +2496,10 @@
<translation>Fjern denne oppføringen</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>beløpet som skal sendes inn den valgte enheten.</translation>
+ </message>
+ <message>
<source>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>
<translation>Gebyret vil bli trukket fra beløpet som blir sendt. Mottakeren vil motta mindre bitcoins enn det du skriver inn i beløpsfeltet. Hvis det er valgt flere mottakere, deles gebyret likt.</translation>
</message>
@@ -2354,6 +2626,14 @@
<translation>Bitcoin-adressen meldingen ble signert med</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Den signerte meldingen for å bekfrefte</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>signaturen som ble gitt da meldingen ble signert</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verifiser meldingen for å være sikker på at den ble signert av den angitte Bitcoin-adressen</translation>
</message>
@@ -2386,6 +2666,10 @@
<translation>Opplåsning av lommebok ble avbrutt.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Ingen feil</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Privat nøkkel for den angitte adressen er ikke tilgjengelig.</translation>
</message>
@@ -2560,6 +2844,10 @@
<translation>Utdatainndeks</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(sertifikatet ble ikke bekreftet)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Forretningsdrivende</translation>
</message>
@@ -2882,7 +3170,11 @@
<source>Close wallet</source>
<translation>Lukk lommebok</translation>
</message>
- </context>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Å lukke lommeboken for lenge kan føre til at du må synkronisere hele kjeden hvis beskjæring er aktivert.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
<message>
@@ -2909,6 +3201,10 @@
<translation>Ønsker du å øke gebyret?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Vil du utarbeide en transaksjon med gebyrøkning?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Nåværede gebyr:</translation>
</message>
@@ -2925,6 +3221,14 @@
<translation>Bekreft gebyrøkning</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Kan ikke utarbeide transaksjon.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopiert</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Kan ikke signere transaksjon</translation>
</message>
@@ -3007,6 +3311,10 @@
<translation>%s-utviklerne</translation>
</message>
<message>
+ <source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
+ <translation>Kan ikke generere en ledelse-nøkkel. Ingen taster i den interne tasten og kan ikke generere noen nøkler.</translation>
+ </message>
+ <message>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
<translation>Kan ikke låse datamappen %s. %s kjører antagelig allerede.</translation>
</message>
@@ -3083,6 +3391,14 @@
<translation>Oppdaget korrupt blokkdatabase</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Kunne ikke finne asmap filen %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Kunne ikke analysere asmap filen %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Ønsker du å gjenopprette blokkdatabasen nå?</translation>
</message>
@@ -3155,6 +3471,10 @@
<translation>Laster maskin-til-maskin -adresser…</translation>
</message>
<message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Feil: For lite diskplass!</translation>
+ </message>
+ <message>
<source>Loading banlist...</source>
<translation>Laster inn bannlysningsliste…</translation>
</message>
@@ -3263,6 +3583,10 @@
<translation>Må oppgi en port med -whitebind: '%s'</translation>
</message>
<message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Beskjæringsmodus er inkompatibel med -blokkfilterindex.</translation>
+ </message>
+ <message>
<source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
<translation>Reduserer -maxconnections fra %d til %d, pga. systembegrensninger.</translation>
</message>
@@ -3385,6 +3709,14 @@
<translation>Utilstrekkelige midler</translation>
</message>
<message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>Kan ikke oppgradere en delt lommebok uten HD uten å oppgradere til støtte for forhåndsdelt tastatur. Bruk -upgradewallet = 169900 eller -upgradewallet uten versjon spesifisert.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Avgiftsberegning mislyktes. Fallbackfee er deaktivert. Vent et par blokker eller aktiver -fallbackfee.</translation>
+ </message>
+ <message>
<source>Loading block index...</source>
<translation>Laster blokkindeks...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ne.ts b/src/qt/locale/bitcoin_ne.ts
index 0a291dd95e..7f74cb5d50 100644
--- a/src/qt/locale/bitcoin_ne.ts
+++ b/src/qt/locale/bitcoin_ne.ts
@@ -55,8 +55,7 @@
</message>
<message>
<source>&amp;Copy Address</source>
- <translation>ठेगाना कपी गर्नुहोस्
-</translation>
+ <translation>ठेगाना कपी गर्नुहोस्</translation>
</message>
</context>
<context>
@@ -191,8 +190,7 @@
</message>
<message>
<source>Copy address</source>
- <translation>ठेगाना कपी गर्नुहोस्
-</translation>
+ <translation>ठेगाना कपी गर्नुहोस्</translation>
</message>
</context>
<context>
@@ -396,8 +394,7 @@
<name>TransactionView</name>
<message>
<source>Copy address</source>
- <translation>ठेगाना कपी गर्नुहोस्
-</translation>
+ <translation>ठेगाना कपी गर्नुहोस्</translation>
</message>
</context>
<context>
@@ -416,8 +413,7 @@
<name>WalletView</name>
<message>
<source>&amp;Export</source>
- <translation>&amp;amp;निर्यात गर्नुहोस्
-</translation>
+ <translation>&amp;amp;निर्यात गर्नुहोस्</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts
index 73c18cd4d8..ad713a8cc8 100644
--- a/src/qt/locale/bitcoin_nl.ts
+++ b/src/qt/locale/bitcoin_nl.ts
@@ -1790,6 +1790,14 @@ Dit is ideaal voor alleen-lezen portommonees.</translation>
<translation>Gesynchroniseerde blokken</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Het in kaart gebrachte autonome systeem dat wordt gebruikt voor het diversifiëren van peer-selectie.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>AS in kaart gebracht.</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User Agent</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts
index c09e0d41ac..552d3e1ca7 100644
--- a/src/qt/locale/bitcoin_pl.ts
+++ b/src/qt/locale/bitcoin_pl.ts
@@ -482,6 +482,14 @@
<translation>Aktualny</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Okno węzła</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Otwórz konsolę diagnostyczną i debugowanie węzłów</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Adresy wysyłania</translation>
</message>
@@ -490,6 +498,10 @@
<translation>&amp;Adresy odbioru</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Otwórz URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Otwórz Portfel</translation>
</message>
@@ -1023,7 +1035,7 @@
</message>
<message>
<source>Unknown...</source>
- <translation>Nienznane...</translation>
+ <translation>Nieznany...</translation>
</message>
<message>
<source>Last block time</source>
@@ -1050,6 +1062,14 @@
<translation>Ukryj</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Wyjdź</translation>
+ </message>
+ <message>
+ <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>%1 jest w trakcie synchronizacji. Trwa pobieranie i weryfikacja nagłówków oraz bloków z sieci w celu uzyskania aktualnego stanu łańcucha.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Nieznane. Synchronizowanie nagłówków (%1, %2%)...</translation>
</message>
@@ -1057,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Otwórz URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1449,6 +1473,10 @@
<translation>'bitcoin://' nie jest poprawnym URI. Użyj 'bitcoin:'.</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Nie można przetworzyć żądania zapłaty z powodu braku wsparcia BIP70.</translation>
+ </message>
+ <message>
<source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
<translation>Z powodu znanych błędów bezpieczeństwa w BIP70 zaleca się ignorować wszelkie polecenie od sprzedawcy dotyczące zmiany portfela.</translation>
</message>
@@ -1761,10 +1789,22 @@
<translation>Zsynchronizowane bloki</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Zmapowany autonomiczny system (ang. asmap) używany do dywersyfikacji wyboru węzłów.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Zmapowany autonomiczny system (ang. asmap)</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Aplikacja kliencka</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Okno węzła</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Otwórz plik dziennika debugowania %1 z obecnego katalogu z danymi. Może to potrwać kilka sekund przy większych plikach.</translation>
</message>
@@ -1976,6 +2016,14 @@
<translation>Opcjonalna kwota by zażądać. Zostaw puste lub zero by nie zażądać konkretnej kwoty.</translation>
</message>
<message>
+ <source>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>
+ <translation>Dodatkowa etykieta powiązana z nowym adresem do odbierania płatności (używanym w celu odnalezienia faktury). Jest również powiązana z żądaniem płatności.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Dodatkowa wiadomość dołączana do żądania zapłaty, która może być odczytana przez płacącego.</translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
<translation>&amp;Stwórz nowy adres odbiorczy</translation>
</message>
@@ -2230,6 +2278,10 @@ Uwaga: Ponieważ opłata jest naliczana za każdy bajt, opłata "100 satoshi za
<translation>Pył:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Ukryj ustawienia opłat transakcyjnych</translation>
+ </message>
+ <message>
<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>Gdy ilość transakcji jest mniejsza niż ilość miejsca w bloku, górnicy i węzły przekazujące wymagają minimalnej opłaty. Zapłata tylko tej wartości jest dopuszczalna, lecz może skutkować transakcją która nigdy nie zostanie potwierdzona w sytuacji, gdy ilość transakcji przekroczy przepustowość sieci.</translation>
</message>
@@ -2298,6 +2350,14 @@ Uwaga: Ponieważ opłata jest naliczana za każdy bajt, opłata "100 satoshi za
<translation>%1 (%2 bloków)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>&amp;Utwórz niepodpisaną transakcję</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Tworzy częściowo podpisaną transakcję (ang. PSBT) używaną np. offline z portfelem %1 lub z innym portfelem zgodnym z PSBT.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation>z portfela '%1'</translation>
</message>
@@ -2310,10 +2370,18 @@ Uwaga: Ponieważ opłata jest naliczana za każdy bajt, opłata "100 satoshi za
<translation>%1 do %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Czy chcesz zapisać szkic tej transakcji?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Czy na pewno chcesz wysłać?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Proszę przejrzeć propozycję transakcji. Zostanie utworzona częściowo podpisana transakcja (ang. PSBT), którą można skopiować, a następnie podpisać np. offline z portfelem %1 lub z innym portfelem zgodnym z PSBT.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>lub</translation>
</message>
@@ -2346,6 +2414,26 @@ Uwaga: Ponieważ opłata jest naliczana za każdy bajt, opłata "100 satoshi za
<translation>Potwierdź wysyłanie monet</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Potwierdź propozycję transakcji</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Skopiuj PSBT do schowka</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Wyślij</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>Skopiowano PSBT</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Kwota na obserwowanych kontach:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Adres odbiorcy jest nieprawidłowy, proszę sprawić ponownie.</translation>
</message>
@@ -2441,6 +2529,10 @@ Uwaga: Ponieważ opłata jest naliczana za każdy bajt, opłata "100 satoshi za
<translation>Usuń ten wpis</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Kwota do wysłania w wybranej jednostce</translation>
+ </message>
+ <message>
<source>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>
<translation>Opłata zostanie odjęta od kwoty wysyłane.Odbiorca otrzyma mniej niż bitcoins wpisz w polu kwoty. Jeśli wybrano kilku odbiorców, opłata jest podzielona równo.</translation>
</message>
@@ -2568,6 +2660,14 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Adres Bitcoin, którym została podpisana wiadomość</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Podpisana wiadomość do weryfikacji</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Sygnatura podawana przy podpisywaniu wiadomości</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Zweryfikuj wiadomość, aby upewnić się, że została podpisana odpowiednim adresem Bitcoin.</translation>
</message>
@@ -2600,6 +2700,10 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Odblokowanie portfela zostało anulowane.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Brak błędów</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Klucz prywatny dla podanego adresu nie jest dostępny.</translation>
</message>
@@ -3135,6 +3239,10 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Czy chcesz zwiększyć prowizję?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Czy chcesz zapisać szkic transakcji ze zwiększoną opłatą transakcyjną?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Aktualna opłata:</translation>
</message>
@@ -3151,6 +3259,14 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Potwierdź zwiększenie opłaty</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Nie można zapisać szkicu transakcji.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>Skopiowano PSBT</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Nie można podpisać transakcji.</translation>
</message>
@@ -3317,6 +3433,14 @@ Zwróć uwagę, że poprawnie zweryfikowana wiadomość potwierdza to, że nadaw
<translation>Wykryto uszkodzoną bazę bloków</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Nie można odnaleźć pliku asmap %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Nie można przetworzyć pliku asmap %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Czy chcesz teraz przebudować bazę bloków?</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pt.ts b/src/qt/locale/bitcoin_pt.ts
index 9fc9170501..4df412cfad 100644
--- a/src/qt/locale/bitcoin_pt.ts
+++ b/src/qt/locale/bitcoin_pt.ts
@@ -1790,6 +1790,14 @@
<translation>Blocos Sincronizados</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>O sistema autonômo mapeado usado para diversificar a seleção de pares.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapeado como</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User Agent</translation>
</message>
diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts
index b4ae9eeee5..efd437fb1d 100644
--- a/src/qt/locale/bitcoin_pt_BR.ts
+++ b/src/qt/locale/bitcoin_pt_BR.ts
@@ -1789,6 +1789,14 @@
<translation>Blocos Sincronizados</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>O sistema autônomo delineado usado para a diversificação da seleção de pares.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapeado como</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User Agent</translation>
</message>
@@ -1862,7 +1870,7 @@
</message>
<message>
<source>&amp;Network Traffic</source>
- <translation>&amp;Tráfico de Rede</translation>
+ <translation>&amp;Tráfego da Rede</translation>
</message>
<message>
<source>Totals</source>
@@ -1930,7 +1938,7 @@
</message>
<message>
<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>
- <translation>ATENÇÃO: Fraudadores solicitam a usuários que digitem comandos aqui, e assim roubão o conteúdo de suas carteiras. Não utilize este console sem antes conhecer os comandos e seus efeitos.</translation>
+ <translation>ATENÇÃO: Fraudadores solicitam a usuários que digitem comandos aqui, e assim roubam o conteúdo de suas carteiras. Não utilize este console sem antes conhecer os comandos e seus efeitos.</translation>
</message>
<message>
<source>Network activity disabled</source>
diff --git a/src/qt/locale/bitcoin_ro.ts b/src/qt/locale/bitcoin_ro.ts
index 333a191ed3..310aa000e0 100644
--- a/src/qt/locale/bitcoin_ro.ts
+++ b/src/qt/locale/bitcoin_ro.ts
@@ -15,7 +15,7 @@
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
- <translation>Copiază adresa selectată în clipboard</translation>
+ <translation>Copiază adresa selectată curent în clipboard</translation>
</message>
<message>
<source>&amp;Copy</source>
@@ -27,7 +27,7 @@
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>Şterge adresa selectată din listă</translation>
+ <translation>Şterge adresa selectată curent din listă</translation>
</message>
<message>
<source>Enter address or label to search</source>
@@ -132,6 +132,10 @@
<translation>Repetaţi noua frază de acces</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Arată fraza de acces</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Criptare portofel</translation>
</message>
@@ -172,6 +176,18 @@
<translation>Portofel criptat</translation>
</message>
<message>
+ <source>Wallet to be encrypted</source>
+ <translation>Portofel de criptat</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Portofelul tău urmează să fie criptat.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Protofelul tău este criptat.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>IMPORTANT: Orice copie de siguranţă făcută anterior portofelului dumneavoastră ar trebui înlocuită cu cea generată cel mai recent, fişier criptat al portofelului. Pentru siguranţă, copiile de siguranţă vechi ale portofelului ne-criptat vor deveni inutile imediat ce veţi începe folosirea noului fişier criptat al portofelului.</translation>
</message>
@@ -294,6 +310,14 @@
<translation>Deschide &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Crează portofel...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Crează un portofel nou</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Portofel:</translation>
</message>
@@ -442,6 +466,38 @@
<translation>Actualizat</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Fereastra nodului</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Deschide consola pentru depanare şi diagnosticare a nodului</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses</source>
+ <translation>&amp;Adresele de destinatie</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses</source>
+ <translation>&amp;Adresele de primire</translation>
+ </message>
+ <message>
+ <source>Open Wallet</source>
+ <translation>Deschide portofel</translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>Deschide un portofel</translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>Inchide portofel...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>Inchide portofel</translation>
+ </message>
+ <message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
<translation>Arată mesajul de ajutor %1 pentru a obţine o listă cu opţiunile posibile de linii de comandă Bitcoin</translation>
</message>
@@ -450,6 +506,10 @@
<translation>portofel implicit</translation>
</message>
<message>
+ <source>No wallets available</source>
+ <translation>Niciun portofel disponibil</translation>
+ </message>
+ <message>
<source>&amp;Window</source>
<translation>&amp;Fereastră</translation>
</message>
@@ -458,6 +518,14 @@
<translation>Minimizare</translation>
</message>
<message>
+ <source>Zoom</source>
+ <translation>Zoom</translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>Fereastra principală</translation>
+ </message>
+ <message>
<source>%1 client</source>
<translation>Client %1</translation>
</message>
@@ -474,6 +542,10 @@
<translation>Eroare: %1</translation>
</message>
<message>
+ <source>Warning: %1</source>
+ <translation> Atenționare: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Data: %1
@@ -526,6 +598,10 @@
<translation>Generarea de chei HD este &lt;b&gt;dezactivata&lt;/b&gt;</translation>
</message>
<message>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Cheia privată &lt;b&gt;dezactivată&lt;/b&gt;</translation>
+ </message>
+ <message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<translation>Portofelul este &lt;b&gt;criptat&lt;/b&gt; iar în momentul de faţă este &lt;b&gt;deblocat&lt;/b&gt;</translation>
</message>
@@ -691,10 +767,46 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Crearea portofelului a eşuat</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Atentionare la crearea portofelului</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Crează portofel</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Numele portofelului</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Criptează portofelul. Portofelul va fi criptat cu fraza de acces aleasă.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Criptează portofelul.</translation>
+ </message>
+ <message>
+ <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>Dezactivează cheile private pentru acest portofel. Portofelele cu cheile private dezactivate nu vor avea chei private şi nu vor putea avea samanţă HD sau chei private importate. Ideal pentru portofele marcate doar pentru citire.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Dezactivează cheile private</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Creează</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -1573,6 +1685,10 @@
<translation>Agent utilizator</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Fereastra nodului</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Deschide fişierul jurnal depanare %1 din directorul curent. Aceasta poate dura cateva secunde pentru fişierele mai mari.</translation>
</message>
@@ -2093,6 +2209,10 @@ Nota: Cum taxa este calculata per byte, o taxa de "100 satoshi per kB" pentru o
<translation>%1(%2 blocuri)</translation>
</message>
<message>
+ <source> from wallet '%1'</source>
+ <translation>din portofelul '%1'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 la %2</translation>
</message>
@@ -2556,6 +2676,10 @@ Nota: Cum taxa este calculata per byte, o taxa de "100 satoshi per kB" pentru o
<translation>Index debit</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Certificatul nu a fost verificat)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Comerciant</translation>
</message>
@@ -2874,6 +2998,10 @@ Nota: Cum taxa este calculata per byte, o taxa de "100 satoshi per kB" pentru o
</context>
<context>
<name>WalletController</name>
+ <message>
+ <source>Close wallet</source>
+ <translation>Inchide portofel</translation>
+ </message>
</context>
<context>
<name>WalletFrame</name>
diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts
index b7515b1a7c..4b063eb201 100644
--- a/src/qt/locale/bitcoin_ru.ts
+++ b/src/qt/locale/bitcoin_ru.ts
@@ -181,7 +181,7 @@
</message>
<message>
<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>Введите новый пароль для кошелька.&lt;br/&gt;Используйте пароль, состоящий из &lt;b&gt;десяти или более случайных символов&lt;/b&gt;, или &lt;b&gt;восьми или более слов&lt;/b&gt;.</translation>
+ <translation>Введите новый пароль для кошелька.&lt;br/&gt;Используйте пароль, состоящий из &lt;b&gt;десяти или более случайных символов&lt;/b&gt; или &lt;b&gt;восьми или более слов&lt;/b&gt;.</translation>
</message>
<message>
<source>Enter the old passphrase and new passphrase for the wallet.</source>
@@ -205,7 +205,7 @@
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
- <translation>ВАЖНО: любые предыдущие резервные копия вашего кошелька, выполненные вами, необходимо заменить новым сгенерированным, зашифрованным файлом кошелька. В целях безопасности, предыдущие резервные копии незашифрованного файла кошелька утратят пригодность после начала использования нового зашифрованного кошелька.</translation>
+ <translation>ВАЖНО: любые предыдущие резервные копия вашего кошелька, выполненные вами, необходимо заменить новым сгенерированным, зашифрованным файлом кошелька. В целях безопасности предыдущие резервные копии незашифрованного файла кошелька утратят пригодность после начала использования нового зашифрованного кошелька.</translation>
</message>
<message>
<source>Wallet encryption failed</source>
@@ -399,11 +399,11 @@
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
- <translation>Подписывайте сообщения Биткойн-адресами чтобы подтвердить что это написали именно Вы</translation>
+ <translation>Подписывайте сообщения Биткойн-адресами, чтобы подтвердить, что это написали именно вы</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Bitcoin addresses</source>
- <translation>Проверяйте сообщения чтобы убедиться что они подписаны конкретными Биткойн-адресами</translation>
+ <translation>Проверяйте сообщения, чтобы убедиться, что они подписаны конкретными Биткойн-адресами</translation>
</message>
<message>
<source>&amp;File</source>
@@ -482,6 +482,14 @@
<translation>Готов</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Окно узла</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Открыть консоль отладки и диагностики узла</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Адреса для отправлений</translation>
</message>
@@ -490,6 +498,10 @@
<translation>&amp;Адреса для получений</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Открыть биткойн: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Открыть Кошелёк</translation>
</message>
@@ -808,7 +820,7 @@
</message>
<message>
<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>Отключить приватные ключи для этого кошелька. Кошельки с отключенными приватными ключами не будут иметь приватных ключей и HD мастер-ключ или импортированные приватные ключи. Это подходит только кошелькам для часов.</translation>
+ <translation>Отключить приватные ключи для этого кошелька. Кошельки с отключенными приватными ключами не будут иметь приватных ключей и HD мастер-ключа или импортированных приватных ключей. Это подходит только кошелькам для часов.</translation>
</message>
<message>
<source>Disable Private Keys</source>
@@ -816,7 +828,7 @@
</message>
<message>
<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>Сделать пустой кошелёк. Чистые кошельки изначально не имеют приватных ключей или скриптов. Позже можно импортировать приватные ключи и адреса, или установить HD мастер-ключ.</translation>
+ <translation>Сделать пустой кошелёк. Чистые кошельки изначально не имеют приватных ключей или скриптов. Позже можно импортировать приватные ключи и адреса или установить HD мастер-ключ.</translation>
</message>
<message>
<source>Make Blank Wallet</source>
@@ -944,7 +956,7 @@
</message>
<message>
<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>Первоначальная синхронизация очень сложна и может выявить проблемы с оборудованием вашего компьютера, которые ранее оставались незамеченными. Каждый раз, когда вы запускаете %1, будет продолжена загрузка с того места, где остановился.</translation>
+ <translation>Первоначальная синхронизация очень сложна и может выявить проблемы с оборудованием вашего компьютера, которые ранее оставались незамеченными. Каждый раз, когда вы запускаете %1, будет продолжена загрузка с места остановки.</translation>
</message>
<message>
<source>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>
@@ -1050,6 +1062,14 @@
<translation>Спрятать</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Выйти</translation>
+ </message>
+ <message>
+ <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>%1 синхронизировано. Заголовки и блоки будут скачиваться с узлов сети и проверяться до тех пор пока не будет достигнут конец цепи блоков.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Неизвестно. Синхронизация заголовков (%1, %2%)...</translation>
</message>
@@ -1057,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Открыть URI биткойна</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1112,7 +1136,7 @@
</message>
<message>
<source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
- <translation>Показывает, используется ли прокси SOCKS5 по умолчанию, для доступа к узлам через этот тип сети.</translation>
+ <translation>Показывает, используется ли прокси SOCKS5 по умолчанию для доступа к узлам через этот тип сети.</translation>
</message>
<message>
<source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
@@ -1132,7 +1156,7 @@
</message>
<message>
<source>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>
- <translation>Сторонние URL-адреса (например, обозреватель блоков) , которые отображаются на вкладке транзакции как элементы контекстного меню. %s в URL заменяется хэшем транзакции. Несколько URL-адресов разделены вертикальной чертой |.</translation>
+ <translation>Сторонние URL-адреса (например, обозреватель блоков), которые отображаются на вкладке транзакции как элементы контекстного меню. %s в URL заменяется хэшем транзакции. Несколько URL-адресов разделены вертикальной чертой |.</translation>
</message>
<message>
<source>Open the %1 configuration file from the working directory.</source>
@@ -1156,7 +1180,7 @@
</message>
<message>
<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>Отключает некоторые дополнительные функции, но все блоки по-прежнему будут полностью проверены. Для возврата к этому параметру необходимо повторно загрузить весь блокчейн. Фактическое использование диска может быть несколько выше.</translation>
+ <translation>Отключает некоторые дополнительные функции, но все блоки по-прежнему будут полностью проверены. Для возврата к этому параметру необходимо повторно загрузить весь блокчейн. Фактическое использование диска может быть несколько больше.</translation>
</message>
<message>
<source>Prune &amp;block storage to</source>
@@ -1192,7 +1216,7 @@
</message>
<message>
<source>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>
- <translation>При отключении траты неподтверждённой сдачи, сдача от транзакции не может быть использована до тех пор пока у этой транзакции не будет хотя бы одно подтверждение. Это также влияет как ваш баланс рассчитывается.</translation>
+ <translation>При отключении траты неподтверждённой сдачи сдача от транзакции не может быть использована до тех пор пока у этой транзакции не будет хотя бы одно подтверждение. Это также влияет как ваш баланс рассчитывается.</translation>
</message>
<message>
<source>&amp;Spend unconfirmed change</source>
@@ -1200,7 +1224,7 @@
</message>
<message>
<source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
- <translation>Автоматически открыть порт для Биткойн-клиента на маршрутизаторе. Работает только если Ваш маршрутизатор поддерживает UPnP, и данная функция включена.</translation>
+ <translation>Автоматически открыть порт для Биткойн-клиента на маршрутизаторе. Работает, если ваш маршрутизатор поддерживает UPnP, и данная функция включена.</translation>
</message>
<message>
<source>Map port using &amp;UPnP</source>
@@ -1216,7 +1240,7 @@
</message>
<message>
<source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
- <translation>Подключится к сети Биткойн через прокси SOCKS5.</translation>
+ <translation>Подключиться к сети Биткойн через прокси SOCKS5.</translation>
</message>
<message>
<source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
@@ -1252,7 +1276,7 @@
</message>
<message>
<source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
- <translation>Подключатся к Биткойн-сети через отдельный прокси SOCKS5 для скрытых сервисов Tor.</translation>
+ <translation>Подключаться к Биткойн-сети через отдельный прокси SOCKS5 для скрытых сервисов Tor.</translation>
</message>
<message>
<source>&amp;Window</source>
@@ -1383,7 +1407,7 @@
</message>
<message>
<source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
- <translation>Общая сумма всех транзакций, которые до сих пор не подтверждены, и до сих пор не учитываются в расходном балансе</translation>
+ <translation>Общая сумма всех транзакций, которые до сих пор не подтверждены и не учитываются в расходном балансе</translation>
</message>
<message>
<source>Immature:</source>
@@ -1446,7 +1470,11 @@
</message>
<message>
<source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
- <translation>'bitcoin://' не верный URI. Используйте 'bitcoin:' вместо этого.</translation>
+ <translation>'bitcoin://' неверный URI. Используйте 'bitcoin:' вместо этого.</translation>
+ </message>
+ <message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Невозможно обработать запрос платежа, потому что BIP70 не поддерживается.</translation>
</message>
<message>
<source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
@@ -1552,7 +1580,7 @@
</message>
<message numerus="yes">
<source>%n week(s)</source>
- <translation><numerusform>%n недели</numerusform><numerusform>%n недель</numerusform><numerusform>%n недель</numerusform><numerusform>%n недель</numerusform></translation>
+ <translation><numerusform>%n неделя</numerusform><numerusform>%n недель</numerusform><numerusform>%n недель</numerusform><numerusform>%n недель</numerusform></translation>
</message>
<message>
<source>%1 and %2</source>
@@ -1560,7 +1588,7 @@
</message>
<message numerus="yes">
<source>%n year(s)</source>
- <translation><numerusform>%n год</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform></translation>
+ <translation><numerusform>%n год</numerusform><numerusform>%n года</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform></translation>
</message>
<message>
<source>%1 B</source>
@@ -1584,7 +1612,7 @@
</message>
<message>
<source>Error: Cannot parse configuration file: %1.</source>
- <translation>Ошибка : Не возможно разобрать файл конфигурации: %1.</translation>
+ <translation>Ошибка : Невозможно разобрать файл конфигурации: %1.</translation>
</message>
<message>
<source>Error: %1</source>
@@ -1619,7 +1647,7 @@
</message>
<message>
<source>QR code support not available.</source>
- <translation>Поддержка QR кодов не доступна.</translation>
+ <translation>Поддержка QR кодов недоступна.</translation>
</message>
<message>
<source>Save QR Code</source>
@@ -1627,7 +1655,7 @@
</message>
<message>
<source>PNG Image (*.png)</source>
- <translation>PNG Картинка (*.png)</translation>
+ <translation>PNG Image (*.png)</translation>
</message>
</context>
<context>
@@ -1761,10 +1789,22 @@
<translation>Синхронизировано блоков</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>The mapped Autonomous System used for diversifying peer selection.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapped AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Пользовательский агент</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Окно узла</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Открыть отладочный лог-файл %1 с текущего каталога данных. Для больших лог-файлов это может занять несколько секунд.</translation>
</message>
@@ -1870,7 +1910,7 @@
</message>
<message>
<source>&amp;Disconnect</source>
- <translation>&amp;Отключится</translation>
+ <translation>&amp;Отключиться</translation>
</message>
<message>
<source>Ban for</source>
@@ -1882,7 +1922,7 @@
</message>
<message>
<source>Welcome to the %1 RPC console.</source>
- <translation>Добро пожаловать в %1 RPC консоль</translation>
+ <translation>Добро пожаловать в %1 RPC-консоль</translation>
</message>
<message>
<source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
@@ -1894,7 +1934,7 @@
</message>
<message>
<source>For more information on using this console type %1.</source>
- <translation>Для получения дополнительных сведений об использовании этой консоли, введите %1.</translation>
+ <translation>Для получения дополнительных сведений об использовании этой консоли введите %1.</translation>
</message>
<message>
<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>
@@ -1973,7 +2013,15 @@
</message>
<message>
<source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
- <translation>Необязательная сумма для запроса. Оставьте пустым или укажите ноль, чтобы запросить неопределённую сумму.</translation>
+ <translation>Необязательная сумма для запроса. Оставьте пустым или укажите ноль, чтобы не запрашивать определённую сумму.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Необязательная метка, ассоциированная с новым адресом приёма (используется вами, чтобы идентифицировать выставленный счёт). Также она присоединяется к запросу платежа.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Необязательное сообщение, которое присоединяется к запросу платежа и может быть показано отправителю.</translation>
</message>
<message>
<source>&amp;Create new receiving address</source>
@@ -1989,7 +2037,7 @@
</message>
<message>
<source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
- <translation>"Родные" segwit адреса (Bech32 или BIP-173) в дальнейшем уменьшат комиссии ваших транзакций и предоставят улучшенную защиту от опечаток, однако старые кошельки не поддерживают эти адреса. Если не выбрано, будет создан совместимый со старыми кошелёк.</translation>
+ <translation>"Родные" segwit-адреса (Bech32 или BIP-173) в дальнейшем уменьшат комиссии ваших транзакций и предоставят улучшенную защиту от опечаток, однако старые кошельки не поддерживают эти адреса. Если не выбрано, будет создан совместимый со старыми кошелёк.</translation>
</message>
<message>
<source>Generate native segwit (Bech32) address</source>
@@ -2190,7 +2238,7 @@
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.</source>
<translation>Укажите пользовательскую плату за килобайт (1000 байт) виртуального размера транзакции.
-Примечание: Так как комиссия рассчитывается на основе каждого байта, комиссия "100 сатошей за КБ " для транзакции размером 500 байт (половина 1 КБ) в конечном счете, приведет к сбору только 50 сатошей.</translation>
+Примечание: Так как комиссия рассчитывается на основе каждого байта, комиссия "100 сатошей за КБ " для транзакции размером 500 байт (половина 1 КБ) в конечном счете приведет к сбору только 50 сатошей.</translation>
</message>
<message>
<source>per kilobyte</source>
@@ -2198,7 +2246,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Hide</source>
- <translation>Спрятать</translation>
+ <translation>Скрыть</translation>
</message>
<message>
<source>Recommended:</source>
@@ -2301,6 +2349,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>%1 (%2 блоков)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Создать Без Подписи</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation>с кошелька '%1'</translation>
</message>
@@ -2313,10 +2369,18 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>С %1 на %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Вы хотите подготовить черновик транзакции?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Вы действительно хотите выполнить отправку?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>или</translation>
</message>
@@ -2349,10 +2413,26 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>Подтвердить отправку монет</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Подтвердите предложенную транзакцию</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Копировать PSBT в буфер обмена</translation>
+ </message>
+ <message>
<source>Send</source>
<translation>Отправить</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>PSBT скопирована</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Баланс только для просмотра:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Адрес получателя неверный. Пожалуйста, перепроверьте.</translation>
</message>
@@ -2448,6 +2528,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>Удалить эту запись</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>The amount to send in the selected unit</translation>
+ </message>
+ <message>
<source>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>
<translation>С отправляемой суммы будет удержана комиссия. Получателю придёт меньше биткойнов, чем вы вводите в поле количества. Если выбрано несколько получателей, комиссия распределяется поровну.</translation>
</message>
@@ -2465,7 +2549,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>This is an unauthenticated payment request.</source>
- <translation>Это не проверенный запрос на оплату.</translation>
+ <translation>Это непроверенный запрос на оплату.</translation>
</message>
<message>
<source>This is an authenticated payment request.</source>
@@ -2477,7 +2561,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>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>
- <translation>Сообщение прикрепленное к биткойн идентификатору будет сохранено вместе с транзакцией для вашего сведения. Заметьте: Сообщение не будет отправлено через сеть Биткойн.</translation>
+ <translation>Сообщение, прикрепленное к биткойн-идентификатору, будет сохранено вместе с транзакцией для вашего сведения. Заметьте: Сообщение не будет отправлено через сеть Биткойн.</translation>
</message>
<message>
<source>Pay To:</source>
@@ -2567,13 +2651,21 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Enter the receiver'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>
- <translation>Введите ниже адрес получателя, сообщение (убедитесь, что переводы строк, пробелы, табы и т.п. в точности скопированы) и подпись, чтобы проверить сообщение. Убедитесь, что не скопировали лишнего в подпись, по сравнению с самим подписываемым сообщением, чтобы не стать жертвой атаки "man-in-the-middle". Заметьте, что эта операция удостоверяет лишь авторство подписавшего, но не может удостоверить отправителя транзакции.</translation>
+ <translation>Введите ниже адрес получателя, сообщение (убедитесь, что переводы строк, пробелы, табы и т.п. в точности скопированы) и подпись, чтобы проверить сообщение. Убедитесь, что не скопировали лишнего в подпись, сравнив с самим подписываемым сообщением, чтобы не стать жертвой атаки "man-in-the-middle". Заметьте, что эта операция удостоверяет лишь авторство подписавшего, но не может удостоверить отправителя транзакции.</translation>
</message>
<message>
<source>The Bitcoin address the message was signed with</source>
<translation>Биткойн-адрес, которым было подписано сообщение</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Подписанное сообщение для проверки</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>The signature given when the message was signed</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Проверить сообщение, чтобы убедиться, что оно было подписано указанным Биткойн-адресом</translation>
</message>
@@ -2646,7 +2738,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<name>TrafficGraphWidget</name>
<message>
<source>KB/s</source>
- <translation>КБ/сек</translation>
+ <translation>КБ/с</translation>
</message>
</context>
<context>
@@ -2661,7 +2753,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>conflicted with a transaction with %1 confirmations</source>
- <translation>конфликт с транзакцией с %1 подтверждений</translation>
+ <translation>конфликт с транзакцией с %1 подтверждениями</translation>
</message>
<message>
<source>0/unconfirmed, %1</source>
@@ -3070,7 +3162,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>ID</source>
- <translation>ИН</translation>
+ <translation>ID</translation>
</message>
<message>
<source>Exporting Failed</source>
@@ -3145,6 +3237,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>Желаете увеличить комиссию?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Do you want to draft a transaction with fee increase?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Текущая комиссия:</translation>
</message>
@@ -3161,6 +3257,14 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>Подтвердите оплату</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Невозможно подготовить черновик транзакции.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT скопирована</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Невозможно подписать транзакцию</translation>
</message>
@@ -3248,7 +3352,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
- <translation>Невозможно заблокировать каталог данных %s. %s возможно уже работает.</translation>
+ <translation>Невозможно заблокировать каталог данных %s. %s, возможно, уже работает.</translation>
</message>
<message>
<source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
@@ -3260,7 +3364,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
- <translation>Пожалуйста убедитесь в корректности установки времени и даты на вашем компьютере! Если время установлено неверно, %s не будет работать правильно.</translation>
+ <translation>Пожалуйста, убедитесь в корректности установки времени и даты на вашем компьютере! Если время установлено неверно, %s не будет работать правильно.</translation>
</message>
<message>
<source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
@@ -3268,7 +3372,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
- <translation>База данных блоков содержит блок, который появляется из будущего. Это может из-за некорректно установленных даты и времени на вашем компьютере. Остается только перестраивать базу блоков, если вы уверены, что дата и время корректны.</translation>
+ <translation>База данных блоков содержит блок, который появляется из будущего. Это может произойти из-за некорректно установленных даты и времени на вашем компьютере. Остается только перестраивать базу блоков, если вы уверены, что дата и время корректны.</translation>
</message>
<message>
<source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
@@ -3276,7 +3380,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
- <translation>Это плата за транзакцию, которую вы можете отменить, если изменения меньше чем пыль</translation>
+ <translation>Это плата за транзакцию, которую вы можете отменить, если изменения меньше, чем пыль</translation>
</message>
<message>
<source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
@@ -3372,7 +3476,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Failed to listen on any port. Use -listen=0 if you want this.</source>
- <translation>Не удалось начать прослушивание на порту. Используйте -listen=0 если вас это устраивает.</translation>
+ <translation>Не удалось начать прослушивание на порту. Используйте -listen=0, если вас это устраивает.</translation>
</message>
<message>
<source>Failed to rescan the wallet during initialization</source>
@@ -3436,7 +3540,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Not enough file descriptors available.</source>
- <translation>Недоступно достаточного количества дескрипторов файла.</translation>
+ <translation>Недоступно достаточное количество дескрипторов файла.</translation>
</message>
<message>
<source>Prune cannot be configured with a negative value.</source>
@@ -3646,7 +3750,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>The wallet will avoid paying less than the minimum relay fee.</source>
- <translation>Кошелёк будет избегать оплат меньших, нежели минимальная комиссия передачи.</translation>
+ <translation>Кошелёк будет избегать оплат меньше минимальной комиссии передачи.</translation>
</message>
<message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
@@ -3678,7 +3782,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
- <translation>Невозможно обновить не разделенный HD кошелёк без обновления для поддержки предварительно разделенного пула ключей. Пожалуйста, используйте -upgradewallet=169900 или -upgradeallet без указания версии.</translation>
+ <translation>Невозможно обновить неразделенный HD кошелёк без обновления для поддержки предварительно разделенного пула ключей. Пожалуйста, используйте -upgradewallet=169900 или -upgradeallet без указания версии.</translation>
</message>
<message>
<source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
diff --git a/src/qt/locale/bitcoin_si.ts b/src/qt/locale/bitcoin_si.ts
index fbcc8ae7bb..bf3f976e1e 100644
--- a/src/qt/locale/bitcoin_si.ts
+++ b/src/qt/locale/bitcoin_si.ts
@@ -2,10 +2,38 @@
<context>
<name>AddressBookPage</name>
<message>
+ <source>Right-click to edit address or label</source>
+ <translation>ලිපිනය හෝ ලේබලය සංස්කරණය කිරීමට දකුණු මූසික බොත්තම ක්ලික් කරන්න</translation>
+ </message>
+ <message>
<source>Create a new address</source>
<translation>නව ලිපිනයක් සාදන්න</translation>
</message>
<message>
+ <source>&amp;New</source>
+ <translation>නව</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>දැනට තෝරාගෙන ඇති ලිපිනය පද්ධති පසුරු පුවරුවට (clipboard) පිටපත් කරන්න</translation>
+ </message>
+ <message>
+ <source>&amp;Copy</source>
+ <translation>පිටපත් කරන්න</translation>
+ </message>
+ <message>
+ <source>C&amp;lose</source>
+ <translation>වසා දමන්න</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>දැනට තෝරාගත් ලිපිනය ලැයිස්තුවෙන් ඉවත් කරන්න</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>සෙවීමට ලිපිනය හෝ ලේබලය ඇතුළත් කරන්න</translation>
+ </message>
+ <message>
<source>Choose the address to send coins to</source>
<translation>කාසි යැවිය යුතු ලිපිනය තෝරන්න</translation>
</message>
@@ -21,7 +49,23 @@
<source>Receiving addresses</source>
<translation>ලබන ලිපින</translation>
</message>
- </context>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>මේවා ඔබගේ ගෙවීම් යැවීම සඳහා වන බිට්කොයින් ලිපින වේ. කාසි යැවීමට පෙර සෑම විටම මුදල සහ ලැබීමේ ලිපිනය පරීක්ෂා කරන්න.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation> මේවා ඔබගේ ගෙවීම් ලැබීම සඳහා වන බිට්කොයින් ලිපින වේ. නව ලිපින සෑදීම සඳහා ලැබීම් ටැබ් එකෙහි ඇති 'නව ලැබීමේ ලිපිනයක් සාදන්න' බොත්තම භාවිතා කරන්න.</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>කොමා වලින් වෙන් කරන ලද ගොනුව (* .csv)</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the address list to %1. Please try again.</source>
+ <translation>ලිපින ලැයිස්තුව %1 ට සුරැකීමට උත්සාහ කිරීමේදී දෝෂයක් ඇතිවිය. කරුණාකර නැවත උත්සාහ කරන්න.</translation>
+ </message>
+</context>
<context>
<name>AddressTableModel</name>
<message>
@@ -39,13 +83,141 @@
</context>
<context>
<name>AskPassphraseDialog</name>
- </context>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>මුරපද කවුළුව</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>මුරපදය ඇතුල් කරන්න</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>නව මුරපදය</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>නව මුරපදය නැවත ඇතුලත් කරන්න</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>මුරපදය පෙන්වන්න</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>පසුම්බිය සංකේතනය කරන්න</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>පසුම්බිය අගුළු ඇරීමේ මෙම ක්‍රියාවලියට ඔබේ පසුම්බියේ මුරපදය අවශ්‍ය වේ.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>පසුම්බිය අගුළු අරින්න</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>පසුම්බිය විකේතනය කිරීමේ මෙම ක්‍රියාවලියට ඔබේ පසුම්බියේ මුරපදය අවශ්‍ය වේ.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>පසුම්බිය විකේතනය කරන්න</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>මුරපදය වෙනස් කරන්න</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>පසුම්බි සංකේතනය තහවුරු කරන්න</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>අවවාදයයි: ඔබ ඔබේ මුදල් පසුම්බිය සංකේතනය කල පසු ඔබගේ මුරපදය නැති වුවහොත්, ඔබේ &lt;b&gt;බිට්කොයින් සියල්ලම ඔබට අහිමි වනු ඇත&lt;/b&gt;!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>ඔබේ මුදල් පසුම්බිය සංකේතනය කිරීමේ අවශ්‍යතාව තහවුරු කරන්න?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>පසුම්බිය සංකේතනය කර ඇත</translation>
+ </message>
+ <message>
+ <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>පසුම්බිය සඳහා නව මුරපදය ඇතුළත් කරන්න.&lt;br/&gt;කරුණාකර මුරපදය සඳහා &lt;b&gt;අහඹු අක්ෂර දහයක් හෝ වැඩි ගණනක්&lt;/b&gt;, හෝ &lt;b&gt;වචන අටක් හෝ වැඩි ගණනක්&lt;/b&gt;භාවිතා කරන්න.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>පසුම්බිය සඳහා පැරණි මුරපදය සහ නව මුරපදය ඇතුළත් කරන්න.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>ඔබේ මුදල් පසුම්බිය සංකේතනය කිරීමෙන් ඔබේ පරිගණකයට අනිෂ්ට මෘදුකාංග (malware) ඇතුලු වීමෙන් කෙරෙන බිට්කොයින් සොරකම් කිරීම් වලින් සම්පූර්ණයෙන්ම වැළැක්වීම කළ නොහැකි බව මතක තබා ගන්න.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>සංකේතනය කළ යුතු පසුම්බිය</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>ඔබේ මුදල් පසුම්බිය සංකේතනය කිරීමට ආසන්නයි.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>ඔබගේ මුදල් පසුම්බිය දැන් සංකේතනය කර ඇත.</translation>
+ </message>
+ <message>
+ <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
+ <translation>වැදගත්: ඔබගේ පසුම්බි ගොනුවෙන් ඔබ විසින් සාදන ලද පෙර උපස්ථයන්(backups) අලුතින් ජනනය කරන ලද, සංකේතනය කළ පසුම්බි ගොනුව සමඟ ප්‍රතිස්ථාපනය(replace) කළ යුතුය. ආරක්ෂක හේතූන් මත, ඔබ නව, සංකේතනය කළ පසුම්බිය භාවිතා කිරීමට පටන් ගත් වහාම සංකේතනය නොකළ පසුම්බි ගොනුවේ පෙර උපස්ථ අක්‍රීය වනු ඇත.</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed</source>
+ <translation>පසුම්බි සංකේතනය අසාර්ථක විය</translation>
+ </message>
+ <message>
+ <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
+ <translation>අභ්‍යන්තර දෝෂයක් හේතුවෙන් පසුම්බි සංකේතනය අසාර්ථක විය. ඔබගේ මුදල් පසුම්බිය සංකේතනය වී නොමැත.</translation>
+ </message>
+ <message>
+ <source>The supplied passphrases do not match.</source>
+ <translation>සපයන ලද මුරපද නොගැලපේ.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock failed</source>
+ <translation>පසුම්බි අගුළු ඇරීම අසාර්ථක විය</translation>
+ </message>
+ <message>
+ <source>The passphrase entered for the wallet decryption was incorrect.</source>
+ <translation>පසුම්බිය විකේතනය සඳහා ඇතුළත් කළ මුරපදය වැරදිය.</translation>
+ </message>
+ <message>
+ <source>Wallet decryption failed</source>
+ <translation>පසුම්බි විකේතනය අසාර්ථකයි.</translation>
+ </message>
+ <message>
+ <source>Wallet passphrase was successfully changed.</source>
+ <translation>පසුම්බි මුරපදය සාර්ථකව වෙනස් කරන ලදි.</translation>
+ </message>
+ <message>
+ <source>Warning: The Caps Lock key is on!</source>
+ <translation>අවවාදයයි: කැප්ස් ලොක් යතුර ක්‍රියාත්මකයි!</translation>
+ </message>
+</context>
<context>
<name>BanTableModel</name>
+ <message>
+ <source>IP/Netmask</source>
+ <translation>IP/Netmask</translation>
+ </message>
</context>
<context>
<name>BitcoinGUI</name>
<message>
+ <source>Browse transaction history</source>
+ <translation>ගනුදෙනු ඉතිහාසය පිරික්සන්න</translation>
+ </message>
+ <message>
<source>Warning</source>
<translation>අවවාදය</translation>
</message>
@@ -277,6 +449,10 @@
<context>
<name>TransactionView</name>
<message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>කොමා වලින් වෙන් කරන ලද ගොනුව (* .csv)</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>දිනය</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts
index 8435a49319..0b2b26c0c7 100644
--- a/src/qt/locale/bitcoin_sk.ts
+++ b/src/qt/locale/bitcoin_sk.ts
@@ -482,6 +482,14 @@
<translation>Aktualizovaný</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Uzlové okno</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Otvor konzolu pre ladenie a diagnostiku uzlu</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Odosielajúce adresy</translation>
</message>
@@ -490,6 +498,10 @@
<translation>&amp;Prijímajúce adresy</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Otvoriť bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Otvoriť peňaženku</translation>
</message>
@@ -944,7 +956,7 @@
</message>
<message>
<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>Prvá synchronizácia je veľmi náročná a môžu sa tak vďaka nej začat na Vašom počítači projavovať doteraz skryté hárdwarové problémy. Vždy, keď spustíte %1, bude sťahovanie pokračovať tam, kde skončilo.</translation>
+ <translation>Prvá synchronizácia je veľmi náročná a môžu sa tak vďaka nej začat na Vašom počítači prejavovať doteraz skryté hardwarové problémy. Vždy, keď spustíte %1, bude sťahovanie pokračovať tam, kde naposledy skončilo.</translation>
</message>
<message>
<source>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>
@@ -1050,6 +1062,14 @@
<translation>Skryť</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc - úniková klávesa</translation>
+ </message>
+ <message>
+ <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>%1 sa práve synchronizuje. Sťahujú sa hlavičky a bloky od partnerov. Tie sa budú sa overovať až sa kompletne overí celý reťazec blokov - blockchain.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Neznámy. Synchronizujú sa hlavičky (%1, %2%)...</translation>
</message>
@@ -1057,6 +1077,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Otvoriť bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1449,6 +1473,10 @@
<translation>'bitcoin://' je neplatná URI. Použite 'bitcoin:'</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Nemožno spracovať žiadosť o platbu, pretože podpora pre BIP70 nieje podporovaná.</translation>
+ </message>
+ <message>
<source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
<translation>Kvôli mnohým bezpečnostným chybám v BIP70 sa dôrazne odporúča ignorovať inštrukcie na prepínanie peňaženiek od akýchkoľvek obchodníkov.</translation>
</message>
@@ -1762,10 +1790,22 @@
<translation>Synchronizované bloky</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Mapovaný nezávislý - Autonómny Systém používaný na rozšírenie vzájomného výberu partnerov.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapovaný AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Aplikácia</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Uzlové okno</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Otvoriť %1 ladiaci výpis z aktuálnej zložky. Pre veľké súbory to môže chvíľu trvať.</translation>
</message>
@@ -1977,8 +2017,16 @@
<translation>Voliteľná požadovaná suma. Nechajte prázdne alebo nulu ak nepožadujete určitú sumu.</translation>
</message>
<message>
+ <source>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>
+ <translation>Voliteľný popis ktorý sa pridá k tejto novej prijímajúcej adrese (pre jednoduchšiu identifikáciu). Tento popis je taktiež pridaný do výzvy k platbe.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Voliteľná správa ktorá bude pridaná k tejto platobnej výzve a môže byť zobrazená odosielateľovi.</translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
- <translation>Vytvoriť novú adresu pre prijímanie</translation>
+ <translation>&amp;Vytvoriť novú príjmaciu adresu</translation>
</message>
<message>
<source>Clear all fields of the form.</source>
@@ -2230,6 +2278,10 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Prach:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Skryť nastavenie poplatkov transakcie</translation>
+ </message>
+ <message>
<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>Ak je v blokoch menej objemu transakcií ako priestoru, ťažiari ako aj vysielacie uzly, môžu uplatniť minimálny poplatok. Platiť iba minimálny poplatok je v poriadku, ale uvedomte si, že to môže mať za následok transakciu, ktorá sa nikdy nepotvrdí, akonáhle je väčší dopyt po bitcoinových transakciách, než dokáže sieť spracovať.</translation>
</message>
@@ -2298,6 +2350,14 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>%1 (%2 blokov)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Vytvoriť bez podpisu</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Vytvorí čiastočne podpísanú Bitcoin transakciu (Partially Signed Bitcoin Transaction - PSBT) na použitie napríklad s offline %1 peňaženkou alebo v hardvérovej peňaženke kompatibilnej s PSBT.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation> z peňaženky '%1'</translation>
</message>
@@ -2310,6 +2370,10 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>%1 do %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Chcete naplánovať túto transakciu?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Určite chcete odoslať transakciu?</translation>
</message>
@@ -2346,6 +2410,26 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Potvrďte odoslanie mincí</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Potvrdiť návrh transakcie</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Skopírovať PSBT do schránky</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Odoslať</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT skopírovaný</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Iba sledovaný zostatok:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Adresa príjemcu je neplatná. Prosím, overte ju.</translation>
</message>
@@ -2441,6 +2525,10 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Odstrániť túto položku</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Suma na odoslanie vo vybranej mene</translation>
+ </message>
+ <message>
<source>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>
<translation>Poplatok sa odpočíta od čiastky, ktorú odosielate. Príjemca dostane menej bitcoinov ako zadáte. Ak je vybraných viacero príjemcov, poplatok je rozdelený rovným dielom.</translation>
</message>
@@ -2567,6 +2655,14 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Adresa Bitcoin, ktorou bola podpísaná správa</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Podpísaná správa na overenie</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Poskytnutý podpis pri podpísaní správy</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Overím správy sa uistiť že bola podpísaná označenou Bitcoin adresou</translation>
</message>
@@ -2599,6 +2695,10 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Odomknutie peňaženky bolo zrušené.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Bez chyby</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Súkromný kľúč pre zadanú adresu nieje k dispozícii.</translation>
</message>
@@ -3131,7 +3231,11 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
</message>
<message>
<source>Do you want to increase the fee?</source>
- <translation>Chceš poplatok navýšiť?</translation>
+ <translation>Chcete navýšiť poplatok?</translation>
+ </message>
+ <message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Chcete naplánovať túto transakciu s navýšením poplatkov.</translation>
</message>
<message>
<source>Current fee:</source>
@@ -3150,6 +3254,14 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Potvrď navýšenie poplatku</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Nemožno naplánovať túto transakciu.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT skopírovaný</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Nemôzeme podpíaať transakciu.</translation>
</message>
@@ -3316,6 +3428,14 @@ Poznámka: Keďže poplatok je počítaný za bajt, poplatok o hodnote "100 sato
<translation>Zistená poškodená databáza blokov</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Nepodarilo sa nájsť asmap súbor %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Nepodarilo sa analyzovať asmap súbor %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Chcete znovu zostaviť databázu blokov?</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sl.ts b/src/qt/locale/bitcoin_sl.ts
index 251c040a22..fffb181e76 100644
--- a/src/qt/locale/bitcoin_sl.ts
+++ b/src/qt/locale/bitcoin_sl.ts
@@ -267,7 +267,7 @@
</message>
<message>
<source>Show general overview of wallet</source>
- <translation>Oglejte si splošne informacije o vaši denarnici</translation>
+ <translation>Oglejte si splošne informacije o svoji denarnici</translation>
</message>
<message>
<source>&amp;Transactions</source>
@@ -1435,7 +1435,7 @@
</message>
<message>
<source>Spendable:</source>
- <translation>Na voljo:</translation>
+ <translation>Na voljo za pošiljanje:</translation>
</message>
<message>
<source>Recent transactions</source>
@@ -1789,6 +1789,14 @@
<translation>Sinhronizirani bloki</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Mapirani Avtonomski Sistem, uporabljan za diverzificiranje izbire soležnikov.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Mapirani AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Ime agenta</translation>
</message>
@@ -2350,15 +2358,15 @@ Opomba: Ker se provizija izračuna na bajt, bi provizija "100 satoshijev na kB"
</message>
<message>
<source> from wallet '%1'</source>
- <translation>iz denarnice '%1'</translation>
+ <translation> iz denarnice '%1'</translation>
</message>
<message>
<source>%1 to '%2'</source>
- <translation>%1 do '%2'</translation>
+ <translation>%1 v '%2'</translation>
</message>
<message>
<source>%1 to %2</source>
- <translation>%1 do %2</translation>
+ <translation>%1 v %2</translation>
</message>
<message>
<source>Do you want to draft this transaction?</source>
@@ -2366,7 +2374,7 @@ Opomba: Ker se provizija izračuna na bajt, bi provizija "100 satoshijev na kB"
</message>
<message>
<source>Are you sure you want to send?</source>
- <translation>Ali ste prepričani, da želite poslati?</translation>
+ <translation>Ali ste prepričani, da želite poslati sredstva?</translation>
</message>
<message>
<source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
@@ -2382,7 +2390,7 @@ Opomba: Ker se provizija izračuna na bajt, bi provizija "100 satoshijev na kB"
</message>
<message>
<source>Please, review your transaction.</source>
- <translation>Prosimo, preglejte vaše transakcije.</translation>
+ <translation>Prosimo, preglejte svojo transakcijo.</translation>
</message>
<message>
<source>Transaction fee</source>
diff --git a/src/qt/locale/bitcoin_sq.ts b/src/qt/locale/bitcoin_sq.ts
index da3551e241..3a9949488f 100644
--- a/src/qt/locale/bitcoin_sq.ts
+++ b/src/qt/locale/bitcoin_sq.ts
@@ -116,6 +116,10 @@
<translation>Përsërisni fjalëkalimin e ri</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Shfaqe fjalëkalimin</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Kripto portofolin</translation>
</message>
@@ -152,6 +156,14 @@
<translation>Portofoli u enkriptua</translation>
</message>
<message>
+ <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>Jepe fjalëkalimin e ri për portofolin. Ju lutemi të përdorni një fjalkalim prej dhjetë ose më shumë shkronjave të rëndomta, ose tetë e më shumë fjalë.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Jepe fjalëkalimin e vjetër dhe fjalkalimin e ri për portofolin.</translation>
+ </message>
+ <message>
<source>Wallet encryption failed</source>
<translation>Enkriptimi i portofolit dështoi</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts
index fecdb44c86..8e511ba027 100644
--- a/src/qt/locale/bitcoin_sr.ts
+++ b/src/qt/locale/bitcoin_sr.ts
@@ -7,7 +7,7 @@
</message>
<message>
<source>Create a new address</source>
- <translation>Направите нову адресу</translation>
+ <translation>Направи нову адресу</translation>
</message>
<message>
<source>&amp;New</source>
@@ -27,15 +27,15 @@
</message>
<message>
<source>Delete the currently selected address from the list</source>
- <translation>Обришите тренутно одабрану адресу са листе</translation>
+ <translation>Обриши тренутно одабрану адресу са листе</translation>
</message>
<message>
<source>Enter address or label to search</source>
- <translation>Navedite adresu ili naziv koji bi ste potražili</translation>
+ <translation>Унеси адресу или назив ознаке за претрагу</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
- <translation>Извези податке из одабране картице у фајлj</translation>
+ <translation>Извези податке из одабране картице у датотеку</translation>
</message>
<message>
<source>&amp;Export</source>
@@ -47,15 +47,15 @@
</message>
<message>
<source>Choose the address to send coins to</source>
- <translation>Изаберите адресу за слање</translation>
+ <translation>Одабери адресу за слање</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
- <translation>Изаберите адресу за примање</translation>
+ <translation>Одабери адресу за примање</translation>
</message>
<message>
<source>C&amp;hoose</source>
- <translation>&amp;Изабери</translation>
+ <translation>&amp;Одабери</translation>
</message>
<message>
<source>Sending addresses</source>
@@ -67,7 +67,11 @@
</message>
<message>
<source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
- <translation>Ово су ваше Биткоин адресе за слање уплата. Увек добро проверите износ и адресу на коју шаљете пре него што пошаљете уплату.</translation>
+ <translation>Ово су твоје Биткоин адресе за слање уплата. Увек добро провери износ и адресу на коју шаљеш пре него што пошаљеш уплату.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Ово су твоје Биткоин адресе за примање уплата. Користи дугме „Направи нову адресу за примање” у картици за примање за креирање нових адреса.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
@@ -102,7 +106,7 @@
<name>AddressTableModel</name>
<message>
<source>Label</source>
- <translation>Етикета</translation>
+ <translation>Ознака</translation>
</message>
<message>
<source>Address</source>
@@ -110,7 +114,7 @@
</message>
<message>
<source>(no label)</source>
- <translation>(без етикете)</translation>
+ <translation>(без ознаке)</translation>
</message>
</context>
<context>
@@ -121,7 +125,7 @@
</message>
<message>
<source>Enter passphrase</source>
- <translation>Унесите лозинку</translation>
+ <translation>Унеси лозинку</translation>
</message>
<message>
<source>New passphrase</source>
@@ -129,7 +133,11 @@
</message>
<message>
<source>Repeat new passphrase</source>
- <translation>Поновите нову лозинку</translation>
+ <translation>Понови нову лозинку</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>Прикажи лозинку</translation>
</message>
<message>
<source>Encrypt wallet</source>
@@ -137,7 +145,7 @@
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
- <translation>Ова операција захтева да унесете лозинку новчаника како би откључали новчаник.</translation>
+ <translation>Ова операција захтева да унесеш лозинку новчаника како би се новчаник откључао.</translation>
</message>
<message>
<source>Unlock wallet</source>
@@ -145,7 +153,7 @@
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
- <translation>Ова операција захтева да унесете лозинку новчаника како би дешифровали новчаник.</translation>
+ <translation>Ова операција захтева да унесеш лозинку новчаника како би новчаник био дешифрован.</translation>
</message>
<message>
<source>Decrypt wallet</source>
@@ -153,7 +161,7 @@
</message>
<message>
<source>Change passphrase</source>
- <translation>Измену лозинку</translation>
+ <translation>Измени лозинку</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
@@ -161,7 +169,7 @@
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
- <translation>Упозорење: Уколико шифрирате новчаник и изгубите своју лозинку, &lt;b&gt;ИЗГУБИЋЕТЕ СВЕ СВОЈЕ БИТКОИНЕ&lt;/b&gt;!</translation>
+ <translation>Упозорење: Уколико шифрираш новчаник и изгубиш своју лозинку, &lt;b&gt;ИЗГУБИЋЕШ СВЕ СВОЈЕ БИТКОИНЕ&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
@@ -172,6 +180,30 @@
<translation>Новчаник шифриран</translation>
</message>
<message>
+ <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>Унеси нову лозинку за новчаник&lt;br/&gt;Молимо користи лозинку од десет или више насумичних карактера&lt;b&gt;,или&lt;b&gt;осам или више речи&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Унеси стару лозинку и нову лозинку новчаника.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Упамти, шифрирање новчаника не може у потуности заштити твоје биткоине од крађе од стране малвера инфицира твој рачунар.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Новчаник за шифрирање</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Твој новчаник биће шифриран.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Твој новчаник сада је шифриран.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>ВАЖНО: Свакa претходнa резерва новчаника коју сте имали треба да се замени новим, шифрираним фајлом новчаника. Из сигурносних разлога, свака претходна резерва нешифрираног фајла новчаника постаће сувишна, чим почнете да користите нови, шифрирани новчаник.</translation>
</message>
@@ -205,7 +237,7 @@
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
- <translation>Упозорање Caps Lock дугме укључено.</translation>
+ <translation>Упозорање Caps Lock дугме укључено!</translation>
</message>
</context>
<context>
@@ -267,7 +299,7 @@
</message>
<message>
<source>Show information about Qt</source>
- <translation>Прегледајте информације о Qt-у</translation>
+ <translation>Прегледај информације о Qt-у</translation>
</message>
<message>
<source>&amp;Options...</source>
@@ -287,15 +319,23 @@
</message>
<message>
<source>&amp;Change Passphrase...</source>
- <translation>Промени &amp;лозинку...</translation>
+ <translation>&amp; Промени лозинку...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
- <translation>Отвори &amp;УРИ...</translation>
+ <translation>Отвори &amp;URI...</translation>
+ </message>
+ <message>
+ <source>Create Wallet...</source>
+ <translation>Направи Новчаник...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Направи нови ночаник</translation>
</message>
<message>
<source>Wallet:</source>
- <translation>Новчаник</translation>
+ <translation>Новчаник:</translation>
</message>
<message>
<source>Click to disable network activity.</source>
@@ -315,7 +355,7 @@
</message>
<message>
<source>Reindexing blocks on disk...</source>
- <translation>Поново идексирање блокова на диску.</translation>
+ <translation>Поново идексирање блокова на диску...</translation>
</message>
<message>
<source>Proxy is &lt;b&gt;enabled&lt;/b&gt;: %1</source>
@@ -323,11 +363,11 @@
</message>
<message>
<source>Send coins to a Bitcoin address</source>
- <translation>Пошаљите новац на Биткоин адресу</translation>
+ <translation>Пошаљи новац на Биткоин адресу</translation>
</message>
<message>
<source>Backup wallet to another location</source>
- <translation>Направите резервну копију новчаника на другој локацији</translation>
+ <translation>Направи резервну копију новчаника на другој локацији</translation>
</message>
<message>
<source>Change the passphrase used for wallet encryption</source>
@@ -427,7 +467,7 @@
</message>
<message>
<source>Error</source>
- <translation>Greška</translation>
+ <translation>Грешка</translation>
</message>
<message>
<source>Warning</source>
@@ -439,7 +479,43 @@
</message>
<message>
<source>Up to date</source>
- <translation>Ажурно</translation>
+ <translation>Ажурирано</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>Ноде прозор</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Отвори конзолу за ноде дебуг и дијагностику</translation>
+ </message>
+ <message>
+ <source>&amp;Sending addresses</source>
+ <translation>&amp;Адресе за слање</translation>
+ </message>
+ <message>
+ <source>&amp;Receiving addresses</source>
+ <translation>&amp;Адресе за примање</translation>
+ </message>
+ <message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Отвори биткоин: URI</translation>
+ </message>
+ <message>
+ <source>Open Wallet</source>
+ <translation>Отвори новчаник</translation>
+ </message>
+ <message>
+ <source>Open a wallet</source>
+ <translation>Отвори новчаник</translation>
+ </message>
+ <message>
+ <source>Close Wallet...</source>
+ <translation>Затвори новчаник...</translation>
+ </message>
+ <message>
+ <source>Close wallet</source>
+ <translation>Затвори новчаник</translation>
</message>
<message>
<source>Show the %1 help message to get a list with possible Bitcoin command-line options</source>
@@ -450,14 +526,42 @@
<translation>подразумевани новчаник</translation>
</message>
<message>
+ <source>No wallets available</source>
+ <translation>Нема доступних новчаника</translation>
+ </message>
+ <message>
+ <source>Minimize</source>
+ <translation>Умањи</translation>
+ </message>
+ <message>
+ <source>Zoom</source>
+ <translation>Увећај</translation>
+ </message>
+ <message>
+ <source>Main Window</source>
+ <translation>Главни прозор</translation>
+ </message>
+ <message>
<source>%1 client</source>
<translation>%1 клијент</translation>
</message>
<message>
+ <source>Connecting to peers...</source>
+ <translation>Повезивање са клијентима...</translation>
+ </message>
+ <message>
<source>Catching up...</source>
<translation>Ажурирање у току...</translation>
</message>
<message>
+ <source>Error: %1</source>
+ <translation>Грешка: %1</translation>
+ </message>
+ <message>
+ <source>Warning: %1</source>
+ <translation>Упозорење: %1</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>Датум: %1
@@ -484,7 +588,7 @@
<message>
<source>Label: %1
</source>
- <translation>Етикета: %1
+ <translation>Ознака: %1
</translation>
</message>
<message>
@@ -495,11 +599,11 @@
</message>
<message>
<source>Sent transaction</source>
- <translation>Послана трансакција</translation>
+ <translation>Послата трансакција</translation>
</message>
<message>
<source>Incoming transaction</source>
- <translation>Придошла трансакција</translation>
+ <translation>Долазна трансакција</translation>
</message>
<message>
<source>HD key generation is &lt;b&gt;enabled&lt;/b&gt;</source>
@@ -510,8 +614,12 @@
<translation>Генерисање ХД кључа је &lt;b&gt;онеомогућено&lt;/b&gt;</translation>
</message>
<message>
+ <source>Private key &lt;b&gt;disabled&lt;/b&gt;</source>
+ <translation>Приватни кључ &lt;b&gt;онемогућен&lt;/b&gt;</translation>
+ </message>
+ <message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
- <translation>Новчаник јс &lt;b&gt;шифрован&lt;/b&gt; и тренутно &lt;b&gt;откључан&lt;/b&gt;</translation>
+ <translation>Новчаник јс &lt;b&gt;шифриран&lt;/b&gt; и тренутно &lt;b&gt;откључан&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
@@ -558,8 +666,15 @@
</message>
<message>
<source>(un)select all</source>
- <translation>изаберите / поништите све
-</translation>
+ <translation>(Де)Селектуј све</translation>
+ </message>
+ <message>
+ <source>Tree mode</source>
+ <translation>Прикажи као стабло</translation>
+ </message>
+ <message>
+ <source>List mode</source>
+ <translation>Прикажи као листу</translation>
</message>
<message>
<source>Amount</source>
@@ -567,7 +682,7 @@
</message>
<message>
<source>Received with label</source>
- <translation>Примљено са етикетом</translation>
+ <translation>Примљено са ознаком</translation>
</message>
<message>
<source>Received with address</source>
@@ -575,7 +690,7 @@
</message>
<message>
<source>Date</source>
- <translation>datum</translation>
+ <translation>Датум</translation>
</message>
<message>
<source>Confirmations</source>
@@ -583,7 +698,7 @@
</message>
<message>
<source>Confirmed</source>
- <translation>Potvrdjen</translation>
+ <translation>Потврђено</translation>
</message>
<message>
<source>Copy address</source>
@@ -591,7 +706,7 @@
</message>
<message>
<source>Copy label</source>
- <translation>Копирај налепницу</translation>
+ <translation>Копирај ознаку</translation>
</message>
<message>
<source>Copy amount</source>
@@ -631,7 +746,7 @@
</message>
<message>
<source>Copy change</source>
- <translation>Копирај промену</translation>
+ <translation>Копирај кусур</translation>
</message>
<message>
<source>(%1 locked)</source>
@@ -646,8 +761,20 @@
<translation>не</translation>
</message>
<message>
+ <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source>
+ <translation>Ознака постаје црвена уколико прималац прими износ мањи од износа прашине - сићушног износа.</translation>
+ </message>
+ <message>
+ <source>Can vary +/- %1 satoshi(s) per input.</source>
+ <translation>Може варирати +/- %1 сатоши(ја) по инпуту.</translation>
+ </message>
+ <message>
<source>(no label)</source>
- <translation>(без налепнице)</translation>
+ <translation>(без ознаке)</translation>
+ </message>
+ <message>
+ <source>change from %1 (%2)</source>
+ <translation>Измени од %1 (%2)</translation>
</message>
<message>
<source>(change)</source>
@@ -656,10 +783,58 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Креирање новчаника&lt;b&gt;%1... &lt;/b&gt;...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Креирање новчаника неуспешно</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Направи упозорење за новчаник</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Направи новчаник</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Име Новчаника</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Шифрирај новчаник. Новчаник ће бити шифриран лозинком коју одаберете.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Шифрирај новчаник</translation>
+ </message>
+ <message>
+ <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>Онемогући приватни кључ за овај новчаник. Новчаници са онемогућеним приватним кључем неће имати приватни кључ и не могу имати HD семе или увезени приватни кључ. Ова опција идеална је за новчанике који су искључиво за посматрање.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Онемогући Приватне Кључеве</translation>
+ </message>
+ <message>
+ <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>Направи празан новчаник. Празни новчанци немају приватане кључеве или скрипте. Приватни кључеви могу се увести, или HD семе може бити постављено касније.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Направи Празан Новчаник</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Направи</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -668,11 +843,11 @@
</message>
<message>
<source>&amp;Label</source>
- <translation>&amp;Етикета</translation>
+ <translation>&amp;Ознака</translation>
</message>
<message>
<source>The label associated with this address list entry</source>
- <translation>Етикета повезана са овом ставком из листе адреса</translation>
+ <translation>Ознака повезана са овом ставком из листе адреса</translation>
</message>
<message>
<source>The address associated with this address list entry. This can only be modified for sending addresses.</source>
@@ -699,6 +874,14 @@
<translation>Унета адреса "%1" није важећа Биткоин адреса.</translation>
</message>
<message>
+ <source>Address "%1" already exists as a receiving address with label "%2" and so cannot be added as a sending address.</source>
+ <translation>Адреса "%1" већ постоји као примајућа адреса са ознаком "%2" и не може бити додата као адреса за слање.</translation>
+ </message>
+ <message>
+ <source>The entered address "%1" is already in the address book with label "%2".</source>
+ <translation>Унета адреса "%1" већ постоји у адресару са ознаком "%2".</translation>
+ </message>
+ <message>
<source>Could not unlock wallet.</source>
<translation>Новчаник није могуће откључати.</translation>
</message>
@@ -711,7 +894,7 @@
<name>FreespaceChecker</name>
<message>
<source>A new data directory will be created.</source>
- <translation>Нови директоријум података ће бити креиран.</translation>
+ <translation>Нови директоријум података биће креиран.</translation>
</message>
<message>
<source>name</source>
@@ -761,11 +944,15 @@
</message>
<message>
<source>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>
- <translation>Када кликнете на ОК, %1 ће почети с преузимањем и процесирањем целокупног ланца блокова %4 (%2GB), почевши од најранијих трансакција у %3 када је %4 покренут.</translation>
+ <translation>Када кликнете на ОК, %1 ће почети с преузимањем и процесуирањем целокупног ланца блокова %4 (%2GB), почевши од најранијих трансакција у %3 када је %4 покренут.</translation>
+ </message>
+ <message>
+ <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>Враћање ове опције захтева поновно преузимање целокупног блокчејна - ланца блокова. Брже је преузети цели ланац и касније га скратити. Онемогућава неке напредне опције.</translation>
</message>
<message>
<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>Ова иницијална синхронизација је веома захтевна и може изложити ваш рачунар хардверским проблемима који раније нису били примећени. Сваки пут када покренете %1, преузимање ће се наставити тамо где је било прекинуто.</translation>
+ <translation>Првобитна синхронизација веома је захтевна и може изложити ваш рачунар хардверским проблемима који раније нису били примећени. Сваки пут када покренете %1, преузимање ће се наставити тамо где је било прекинуто.</translation>
</message>
<message>
<source>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>
@@ -784,10 +971,46 @@
<translation>Биткоин</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Обриши блокове након верификације, осим најновије %1 GB (скраћено)</translation>
+ </message>
+ <message>
+ <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
+ <translation>Најмање %1 GB подататака биће складиштен у овај директорјиум који ће временом порасти.</translation>
+ </message>
+ <message>
+ <source>Approximately %1 GB of data will be stored in this directory.</source>
+ <translation>Најмање %1 GB подататака биће складиштен у овај директорјиум.</translation>
+ </message>
+ <message>
+ <source>%1 will download and store a copy of the Bitcoin block chain.</source>
+ <translation>%1 биће преузеће и складиштити копију Биткоин ланца блокова.</translation>
+ </message>
+ <message>
+ <source>The wallet will also be stored in this directory.</source>
+ <translation>Новчаник ће бити складиштен у овом директоријуму.</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" cannot be created.</source>
+ <translation>Грешка: Одабрана датотека "%1" не може бити креирана.</translation>
+ </message>
+ <message>
<source>Error</source>
- <translation>Greška</translation>
+ <translation>Грешка</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n GB of free space available</source>
+ <translation><numerusform>Доступно %n GB слободног простора</numerusform><numerusform>Доступно %n GB слободног простора</numerusform><numerusform>Доступно %n GB слободног простора</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>(of %n GB needed)</source>
+ <translation><numerusform>(од потребних %n GB)</numerusform><numerusform>(од потребних %n GB)</numerusform><numerusform>(од потребних %n GB)</numerusform></translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n GB потребно за цео ланац)</numerusform><numerusform>(%n GB потребно за цео ланац)</numerusform><numerusform>(%n GB потребно за цео ланац)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -795,8 +1018,16 @@
<translation>Форма</translation>
</message>
<message>
+ <source>Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the bitcoin network, as detailed below.</source>
+ <translation>Недавне трансакције можда не буду видљиве, зато салдо твог новчаника можда буде нетачан. Ова информација биђе тачна када новчаник заврши са синхронизацијом биткоин мреже, приказаној испод.</translation>
+ </message>
+ <message>
+ <source>Attempting to spend bitcoins that are affected by not-yet-displayed transactions will not be accepted by the network.</source>
+ <translation>Покушај слања биткоина који су под утицајем још не приказаних трансакција неће бити прихваћен од стране мреже.</translation>
+ </message>
+ <message>
<source>Number of blocks left</source>
- <translation>Остала количина блокова</translation>
+ <translation>Преостала количина блокова</translation>
</message>
<message>
<source>Unknown...</source>
@@ -811,24 +1042,64 @@
<translation>Напредак</translation>
</message>
<message>
+ <source>Progress increase per hour</source>
+ <translation>Пораст напретка по часу</translation>
+ </message>
+ <message>
<source>calculating...</source>
- <translation>Рачунање</translation>
+ <translation>рачунање...</translation>
+ </message>
+ <message>
+ <source>Estimated time left until synced</source>
+ <translation>Оквирно време до краја синхронизације</translation>
</message>
<message>
<source>Hide</source>
<translation>Сакриј</translation>
</message>
- </context>
+ <message>
+ <source>Esc</source>
+ <translation>Есц</translation>
+ </message>
+ <message>
+ <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>%1 се синхронузује. Преузеће заглавља и блокове од клијената и потврдити их док не стигне на крај ланца блокова.</translation>
+ </message>
+ <message>
+ <source>Unknown. Syncing Headers (%1, %2%)...</source>
+ <translation>Непознато. Синхронизација заглавља (%1, %2%)...</translation>
+ </message>
+</context>
<context>
<name>OpenURIDialog</name>
- </context>
+ <message>
+ <source>Open bitcoin URI</source>
+ <translation>Отвори биткоин URI</translation>
+ </message>
+ <message>
+ <source>URI:</source>
+ <translation>URI:</translation>
+ </message>
+</context>
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Отварање новчаника неуспешно</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Упозорење приликом отварања новчаника</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>подразумевани новчаник</translation>
</message>
- </context>
+ <message>
+ <source>Opening Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Отварање новчаника&lt;b&gt;%1&lt;/b&gt;...</translation>
+ </message>
+</context>
<context>
<name>OptionsDialog</name>
<message>
@@ -836,18 +1107,158 @@
<translation>Поставке</translation>
</message>
<message>
+ <source>&amp;Main</source>
+ <translation>&amp;Главни</translation>
+ </message>
+ <message>
+ <source>Automatically start %1 after logging in to the system.</source>
+ <translation>Аутоматски почети %1 након пријање на систем.</translation>
+ </message>
+ <message>
+ <source>&amp;Start %1 on system login</source>
+ <translation>&amp;Покрени %1 приликом пријаве на систем</translation>
+ </message>
+ <message>
+ <source>Size of &amp;database cache</source>
+ <translation>Величина кеша базе података</translation>
+ </message>
+ <message>
+ <source>Number of script &amp;verification threads</source>
+ <translation>Број скрипти и CPU за верификацију</translation>
+ </message>
+ <message>
+ <source>IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)</source>
+ <translation>ИП адреса проксија (нпр. IPv4: 127.0.0.1 / IPv6: ::1)</translation>
+ </message>
+ <message>
+ <source>Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source>
+ <translation>Приказује се ако је испоручени уобичајени SOCKS5 проxy коришћен ради проналажења клијената преко овог типа мреже. </translation>
+ </message>
+ <message>
+ <source>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</source>
+ <translation>Користи посебан SOCKS&amp;5 како би пронашли клијенте преко Тор-а.</translation>
+ </message>
+ <message>
+ <source>Hide the icon from the system tray.</source>
+ <translation>Сакриј икону са системске траке.</translation>
+ </message>
+ <message>
+ <source>&amp;Hide tray icon</source>
+ <translation>&amp;Сакриј икону</translation>
+ </message>
+ <message>
+ <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>Минимизирање уместо искључивања апликације када се прозор затвори. Када је ова опција омогућена, апликација ће бити затворена тек након одабира Излаз у менију. </translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>URL треће стране (нпр блок претраживач) који се појављује у менију трансакције. %s у URL  замењен је хашом трансакције. Више URL-ова поделено је вертикалом |.</translation>
+ </message>
+ <message>
+ <source>Open the %1 configuration file from the working directory.</source>
+ <translation>Отвори %1 конфигурациони фајл из директоријума у употреби.</translation>
+ </message>
+ <message>
<source>Open Configuration File</source>
<translation>Отвори Конфигурациону Датотеку</translation>
</message>
<message>
+ <source>Reset all client options to default.</source>
+ <translation>Ресетуј све опције клијента на почетна подешавања.</translation>
+ </message>
+ <message>
+ <source>&amp;Reset Options</source>
+ <translation>&amp;Ресет Опције</translation>
+ </message>
+ <message>
+ <source>&amp;Network</source>
+ <translation>&amp;Мрежа</translation>
+ </message>
+ <message>
+ <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>Онемогућава поједина напредна својства, али ће сви блокови у потпуности бити валидирани. Враћање ове опције захтева да поновно преузимање целокупонг блокчејна.</translation>
+ </message>
+ <message>
+ <source>Prune &amp;block storage to</source>
+ <translation>Сакрати &amp;block складиштење на</translation>
+ </message>
+ <message>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <source>Reverting this setting requires re-downloading the entire blockchain.</source>
+ <translation>Враћање ове опције захтева да поновно преузимање целокупонг блокчејна.</translation>
+ </message>
+ <message>
+ <source>MiB</source>
+ <translation>MiB</translation>
+ </message>
+ <message>
+ <source>(0 = auto, &lt;0 = leave that many cores free)</source>
+ <translation>(0 = аутоматски одреди, &lt;0 = остави слободно толико језгара)</translation>
+ </message>
+ <message>
<source>W&amp;allet</source>
- <translation>новчаник</translation>
+ <translation>Н&amp;овчаник</translation>
</message>
<message>
<source>Expert</source>
<translation>Експерт</translation>
</message>
<message>
+ <source>Enable coin &amp;control features</source>
+ <translation>Омогући опцију контроле новчића</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Уколико онемогућиш трошење непотврђеног кусура, кусур трансакције неће моћи да се користи док транскација нема макар једну потврду. Ово такође утиче како ће се салдо рачунати.</translation>
+ </message>
+ <message>
+ <source>&amp;Spend unconfirmed change</source>
+ <translation>&amp;Троши непотврђени кусур</translation>
+ </message>
+ <message>
+ <source>Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.</source>
+ <translation>Аутоматски отвори Биткоин клијент порт на рутеру. Ова опција ради само уколико твој рутер подржава и има омогућен UPnP.</translation>
+ </message>
+ <message>
+ <source>Map port using &amp;UPnP</source>
+ <translation>Мапирај порт користећи &amp;UPnP</translation>
+ </message>
+ <message>
+ <source>Accept connections from outside.</source>
+ <translation>Прихвати спољашње концекције.</translation>
+ </message>
+ <message>
+ <source>Allow incomin&amp;g connections</source>
+ <translation>Дозволи долазеће конекције.</translation>
+ </message>
+ <message>
+ <source>Connect to the Bitcoin network through a SOCKS5 proxy.</source>
+ <translation>Конектуј се на Биткоин мрежу кроз SOCKS5 проксијем.</translation>
+ </message>
+ <message>
+ <source>&amp;Connect through SOCKS5 proxy (default proxy):</source>
+ <translation>&amp;Конектуј се кроз SOCKS5 прокси (уобичајени прокси):</translation>
+ </message>
+ <message>
+ <source>Proxy &amp;IP:</source>
+ <translation>Прокси &amp;IP:</translation>
+ </message>
+ <message>
+ <source>&amp;Port:</source>
+ <translation>&amp;Порт:</translation>
+ </message>
+ <message>
+ <source>Port of the proxy (e.g. 9050)</source>
+ <translation>Прокси порт (нпр. 9050)</translation>
+ </message>
+ <message>
+ <source>Used for reaching peers via:</source>
+ <translation>Коришћен за приступ другим чворовима преко:</translation>
+ </message>
+ <message>
<source>IPv4</source>
<translation>IPv4</translation>
</message>
@@ -860,10 +1271,54 @@
<translation>Тор</translation>
</message>
<message>
+ <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source>
+ <translation>Конектуј се на Биткоин мрежу кроз SOCK5 прокси за Тор скривене сервисе.</translation>
+ </message>
+ <message>
+ <source>Show only a tray icon after minimizing the window.</source>
+ <translation>Покажи само иконицу у панелу након минимизирања прозора</translation>
+ </message>
+ <message>
+ <source>&amp;Minimize to the tray instead of the taskbar</source>
+ <translation>&amp;минимизирај у доњу линију, уместо у програмску траку</translation>
+ </message>
+ <message>
+ <source>M&amp;inimize on close</source>
+ <translation>Минимизирај при затварању</translation>
+ </message>
+ <message>
+ <source>&amp;Display</source>
+ <translation>&amp;Прикажи</translation>
+ </message>
+ <message>
+ <source>User Interface &amp;language:</source>
+ <translation>&amp;Језик корисничког интерфејса:</translation>
+ </message>
+ <message>
+ <source>The user interface language can be set here. This setting will take effect after restarting %1.</source>
+ <translation>Језик корисничког интерфејса може се овде поставити. Ово својство биће на снази након поновног покреања %1.</translation>
+ </message>
+ <message>
<source>&amp;Unit to show amounts in:</source>
<translation>&amp;Јединица за приказивање износа:</translation>
</message>
<message>
+ <source>Choose the default subdivision unit to show in the interface and when sending coins.</source>
+ <translation>Одабери уобичајену подјединицу која се приказује у интерфејсу и када се шаљу новчићи.</translation>
+ </message>
+ <message>
+ <source>Whether to show coin control features or not.</source>
+ <translation>Да ли да се прикажу опције контроле новчића или не.</translation>
+ </message>
+ <message>
+ <source>&amp;Third party transaction URLs</source>
+ <translation>&amp;URL-ови трансакција трећих страна</translation>
+ </message>
+ <message>
+ <source>Options set in this dialog are overridden by the command line or in the configuration file:</source>
+ <translation>Опције постављене у овом диалогу су поништене командном линијом или у конфигурационој датотеци:</translation>
+ </message>
+ <message>
<source>&amp;OK</source>
<translation>&amp;Уреду</translation>
</message>
@@ -872,10 +1327,50 @@
<translation>&amp;Откажи</translation>
</message>
<message>
+ <source>default</source>
+ <translation>подразумевано</translation>
+ </message>
+ <message>
+ <source>none</source>
+ <translation>ниједно</translation>
+ </message>
+ <message>
+ <source>Confirm options reset</source>
+ <translation>Потврди ресет опција</translation>
+ </message>
+ <message>
+ <source>Client restart required to activate changes.</source>
+ <translation>Рестарт клијента захтеван како би се промене активирале.</translation>
+ </message>
+ <message>
+ <source>Client will be shut down. Do you want to proceed?</source>
+ <translation>Клијент ће се искључити. Да ли желите да наставите?</translation>
+ </message>
+ <message>
+ <source>Configuration options</source>
+ <translation>Конфигурација својстава</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Конфигурациона датотека се користи да одреди напредне корисничке опције које поништају подешавања у графичком корисничком интерфејсу.</translation>
+ </message>
+ <message>
<source>Error</source>
- <translation>Greška</translation>
+ <translation>Грешка</translation>
</message>
- </context>
+ <message>
+ <source>The configuration file could not be opened.</source>
+ <translation>Ова конфигурациона датотека не може бити отворена.</translation>
+ </message>
+ <message>
+ <source>This change would require a client restart.</source>
+ <translation>Ова промена захтева да се рачунар поново покрене.</translation>
+ </message>
+ <message>
+ <source>The supplied proxy address is invalid.</source>
+ <translation>Достављена прокси адреса није валидна.</translation>
+ </message>
+</context>
<context>
<name>OverviewPage</name>
<message>
@@ -883,90 +1378,729 @@
<translation>Форма</translation>
</message>
<message>
+ <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>Приказана информација може бити застарела. Ваш новчаник се аутоматски синхронизује са Биткоин мрежом након успостављања конекције, али овај процес је још увек у току.</translation>
+ </message>
+ <message>
+ <source>Watch-only:</source>
+ <translation>Само гледање:</translation>
+ </message>
+ <message>
<source>Available:</source>
<translation>Доступно:</translation>
</message>
<message>
+ <source>Your current spendable balance</source>
+ <translation>Салдо који можете потрошити</translation>
+ </message>
+ <message>
<source>Pending:</source>
<translation>На чекању:</translation>
</message>
<message>
+ <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
+ <translation>Укупан број трансакција које још увек нису потврђене, и не рачунају се у салдо рачуна који је могуће потрошити</translation>
+ </message>
+ <message>
+ <source>Immature:</source>
+ <translation>Недоспело:</translation>
+ </message>
+ <message>
+ <source>Mined balance that has not yet matured</source>
+ <translation>Салдо рударења који још увек није доспео</translation>
+ </message>
+ <message>
+ <source>Balances</source>
+ <translation>Салдо</translation>
+ </message>
+ <message>
<source>Total:</source>
<translation>Укупно:</translation>
</message>
- </context>
+ <message>
+ <source>Your current total balance</source>
+ <translation>Твој тренутни салдо</translation>
+ </message>
+ <message>
+ <source>Your current balance in watch-only addresses</source>
+ <translation>Твој тренутни салдо са гледај-само адресама</translation>
+ </message>
+ <message>
+ <source>Spendable:</source>
+ <translation>Могуће потрошити:</translation>
+ </message>
+ <message>
+ <source>Recent transactions</source>
+ <translation>Недавне трансакције</translation>
+ </message>
+ <message>
+ <source>Unconfirmed transactions to watch-only addresses</source>
+ <translation>Трансакције за гледај-само адресе које нису потврђене</translation>
+ </message>
+ <message>
+ <source>Mined balance in watch-only addresses that has not yet matured</source>
+ <translation>Салдорударења у адресама које су у моду само гледање, који још увек није доспео</translation>
+ </message>
+ <message>
+ <source>Current total balance in watch-only addresses</source>
+ <translation>Тренутни укупни салдо у адресама у опцији само-гледај</translation>
+ </message>
+</context>
<context>
<name>PaymentServer</name>
- </context>
+ <message>
+ <source>Payment request error</source>
+ <translation>Грешка у захтеву за плаћање</translation>
+ </message>
+ <message>
+ <source>Cannot start bitcoin: click-to-pay handler</source>
+ <translation>Не могу покренути биткоин: "кликни-да-платиш" механизам</translation>
+ </message>
+ <message>
+ <source>URI handling</source>
+ <translation>URI руковање</translation>
+ </message>
+ <message>
+ <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source>
+ <translation>'bitcoin://' није важећи URI. Уместо тога користити 'bitcoin:'.</translation>
+ </message>
+ <message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Захтев за плаћање не може се обрадити, јер BIP70 није подржан.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Због великог броја безбедносних пропуста у BIP70, препоручено је да се све инструкције трговаца за промену новчаника игноришу.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Уколико добијате грешку овог типа, потребно је да захтевате од трговца BIP21 компатибилан URI.</translation>
+ </message>
+ <message>
+ <source>Invalid payment address %1</source>
+ <translation>Неважећа адреса за плаћање %1</translation>
+ </message>
+ <message>
+ <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source>
+ <translation>URI се не може рашчланити! Ово може бити проузроковано неважећом Биткоин адресом или погрешно форматираним URI параметрима.</translation>
+ </message>
+ <message>
+ <source>Payment request file handling</source>
+ <translation>Руковање датотеком захтева за плаћање</translation>
+ </message>
+</context>
<context>
<name>PeerTableModel</name>
- </context>
+ <message>
+ <source>User Agent</source>
+ <translation>Кориснички агент</translation>
+ </message>
+ <message>
+ <source>Node/Service</source>
+ <translation>Ноде/Сервис</translation>
+ </message>
+ <message>
+ <source>NodeId</source>
+ <translation>НодеИД</translation>
+ </message>
+ <message>
+ <source>Ping</source>
+ <translation>Пинг</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>Послато</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Примљено</translation>
+ </message>
+</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
- <translation>iznos</translation>
+ <translation>Износ</translation>
+ </message>
+ <message>
+ <source>Enter a Bitcoin address (e.g. %1)</source>
+ <translation>Унеси Биткоин адресу, (нпр %1)</translation>
+ </message>
+ <message>
+ <source>%1 d</source>
+ <translation>%1 d</translation>
+ </message>
+ <message>
+ <source>%1 h</source>
+ <translation>%1 h</translation>
+ </message>
+ <message>
+ <source>%1 m</source>
+ <translation>%1 m</translation>
+ </message>
+ <message>
+ <source>%1 s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <source>None</source>
+ <translation>Nijedan</translation>
+ </message>
+ <message>
+ <source>N/A</source>
+ <translation>Није применљиво</translation>
+ </message>
+ <message>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n second(s)</source>
+ <translation><numerusform>%n секунда</numerusform><numerusform>%n секунди</numerusform><numerusform>%n секунди</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n minute(s)</source>
+ <translation><numerusform>%n минут</numerusform><numerusform>%n минута</numerusform><numerusform>%n минута</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n hour(s)</source>
+ <translation><numerusform>%n час</numerusform><numerusform>%n часа</numerusform><numerusform>%n часова</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n day(s)</source>
+ <translation><numerusform>%n минут</numerusform><numerusform>%n минута</numerusform><numerusform>%n минута</numerusform></translation>
+ </message>
+ <message numerus="yes">
+ <source>%n week(s)</source>
+ <translation><numerusform>%n недеља</numerusform><numerusform>%n недеље</numerusform><numerusform>%n недеља</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 and %2</source>
+ <translation>%1 и %2</translation>
+ </message>
+ <message numerus="yes">
+ <source>%n year(s)</source>
+ <translation><numerusform>%n година</numerusform><numerusform>%n године</numerusform><numerusform>%n година</numerusform></translation>
+ </message>
+ <message>
+ <source>%1 B</source>
+ <translation>%1 B</translation>
+ </message>
+ <message>
+ <source>%1 KB</source>
+ <translation>%1 KB</translation>
+ </message>
+ <message>
+ <source>%1 MB</source>
+ <translation>%1 MB</translation>
+ </message>
+ <message>
+ <source>%1 GB</source>
+ <translation>%1 GB</translation>
+ </message>
+ <message>
+ <source>Error: Specified data directory "%1" does not exist.</source>
+ <translation>Грешка: Одабрани директорјиум датотеке "%1" не постоји.</translation>
+ </message>
+ <message>
+ <source>Error: %1</source>
+ <translation>Грешка: %1</translation>
+ </message>
+ <message>
+ <source>%1 didn't yet exit safely...</source>
+ <translation>%1 није изашао безбедно...</translation>
</message>
<message>
<source>unknown</source>
- <translation>nepoznato</translation>
+ <translation>непознато</translation>
</message>
</context>
<context>
<name>QRImageWidget</name>
- </context>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Сачувај Слику...</translation>
+ </message>
+ <message>
+ <source>&amp;Copy Image</source>
+ <translation>&amp;Копирај Слику</translation>
+ </message>
+ <message>
+ <source>Resulting URI too long, try to reduce the text for label / message.</source>
+ <translation>Дати резултат URI  предуг, покушај да сманиш текст за ознаку / поруку.</translation>
+ </message>
+ <message>
+ <source>Error encoding URI into QR Code.</source>
+ <translation>Грешка током енкодирања URI у QR Код.</translation>
+ </message>
+ <message>
+ <source>QR code support not available.</source>
+ <translation>QR код подршка није доступна.</translation>
+ </message>
+ <message>
+ <source>Save QR Code</source>
+ <translation>Упамти QR Код</translation>
+ </message>
+ <message>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG Слка (*.png)</translation>
+ </message>
+</context>
<context>
<name>RPCConsole</name>
<message>
+ <source>N/A</source>
+ <translation>Није применљиво</translation>
+ </message>
+ <message>
+ <source>Client version</source>
+ <translation>Верзија клијента</translation>
+ </message>
+ <message>
+ <source>&amp;Information</source>
+ <translation>&amp;Информације</translation>
+ </message>
+ <message>
+ <source>General</source>
+ <translation>Опште</translation>
+ </message>
+ <message>
+ <source>Using BerkeleyDB version</source>
+ <translation>Коришћење BerkeleyDB верзије.</translation>
+ </message>
+ <message>
+ <source>Datadir</source>
+ <translation>Datadir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the data directory use the '%1' option.</source>
+ <translation>Да би сте одредили локацију која није унапред задата за директоријум података користите '%1' опцију.</translation>
+ </message>
+ <message>
+ <source>Blocksdir</source>
+ <translation>Blocksdir</translation>
+ </message>
+ <message>
+ <source>To specify a non-default location of the blocks directory use the '%1' option.</source>
+ <translation>Да би сте одредили локацију која није унапред задата за директоријум блокова користите '%1' опцију.</translation>
+ </message>
+ <message>
+ <source>Startup time</source>
+ <translation>Време подизања система</translation>
+ </message>
+ <message>
+ <source>Network</source>
+ <translation>Мрежа</translation>
+ </message>
+ <message>
+ <source>Name</source>
+ <translation>Име</translation>
+ </message>
+ <message>
+ <source>Number of connections</source>
+ <translation>Број конекција</translation>
+ </message>
+ <message>
+ <source>Block chain</source>
+ <translation>Блокчејн</translation>
+ </message>
+ <message>
+ <source>Current number of blocks</source>
+ <translation>Тренутни број блокова</translation>
+ </message>
+ <message>
+ <source>Memory Pool</source>
+ <translation>Удружена меморија</translation>
+ </message>
+ <message>
+ <source>Current number of transactions</source>
+ <translation>Тренутни број трансакција</translation>
+ </message>
+ <message>
+ <source>Memory usage</source>
+ <translation>Употреба меморије</translation>
+ </message>
+ <message>
+ <source>Wallet: </source>
+ <translation>Новчаник</translation>
+ </message>
+ <message>
+ <source>(none)</source>
+ <translation>(ниједан)</translation>
+ </message>
+ <message>
+ <source>&amp;Reset</source>
+ <translation>&amp;Ресетуј</translation>
+ </message>
+ <message>
+ <source>Received</source>
+ <translation>Примљено</translation>
+ </message>
+ <message>
+ <source>Sent</source>
+ <translation>Послато</translation>
+ </message>
+ <message>
+ <source>&amp;Peers</source>
+ <translation>&amp;Колеге</translation>
+ </message>
+ <message>
+ <source>Banned peers</source>
+ <translation>Забрањене колеге на мрежи</translation>
+ </message>
+ <message>
+ <source>Select a peer to view detailed information.</source>
+ <translation>Одабери колегу да би видели детаљне информације</translation>
+ </message>
+ <message>
+ <source>Whitelisted</source>
+ <translation>На списку познатих</translation>
+ </message>
+ <message>
+ <source>Direction</source>
+ <translation>Правац</translation>
+ </message>
+ <message>
+ <source>Version</source>
+ <translation>Верзија</translation>
+ </message>
+ <message>
+ <source>Starting Block</source>
+ <translation>Почетни блок</translation>
+ </message>
+ <message>
+ <source>Synced Headers</source>
+ <translation>Синхронизована заглавља</translation>
+ </message>
+ <message>
+ <source>Synced Blocks</source>
+ <translation>Синхронизовани блокови</translation>
+ </message>
+ <message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Мапирани аутономни систем који се користи за диверсификацију селекције колега чворова.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Мапирани АС</translation>
+ </message>
+ <message>
+ <source>User Agent</source>
+ <translation>Кориснички агент</translation>
+ </message>
+ <message>
+ <source>Node window</source>
+ <translation>Ноде прозор</translation>
+ </message>
+ <message>
+ <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
+ <translation>Отворите %1 датотеку са записима о отклоњеним грешкама из тренутног директоријума датотека. Ово може потрајати неколико секунди за велике датотеке записа.</translation>
+ </message>
+ <message>
+ <source>Decrease font size</source>
+ <translation>Смањи величину фонта</translation>
+ </message>
+ <message>
+ <source>Increase font size</source>
+ <translation>Увећај величину фонта</translation>
+ </message>
+ <message>
+ <source>Services</source>
+ <translation>Услуге</translation>
+ </message>
+ <message>
+ <source>Ban Score</source>
+ <translation>Ниво забране</translation>
+ </message>
+ <message>
+ <source>Connection Time</source>
+ <translation>Време конекције</translation>
+ </message>
+ <message>
+ <source>Last Send</source>
+ <translation>Последње послато</translation>
+ </message>
+ <message>
+ <source>Last Receive</source>
+ <translation>Последње примљено</translation>
+ </message>
+ <message>
+ <source>Ping Time</source>
+ <translation>Пинг време</translation>
+ </message>
+ <message>
+ <source>The duration of a currently outstanding ping.</source>
+ <translation>Трајање тренутно неразрешеног пинга.</translation>
+ </message>
+ <message>
+ <source>Ping Wait</source>
+ <translation>Чекање на пинг</translation>
+ </message>
+ <message>
+ <source>Min Ping</source>
+ <translation>Мин Пинг</translation>
+ </message>
+ <message>
+ <source>Time Offset</source>
+ <translation>Помак времена</translation>
+ </message>
+ <message>
<source>Last block time</source>
<translation>Време последњег блока</translation>
</message>
<message>
+ <source>&amp;Open</source>
+ <translation>&amp;Отвори</translation>
+ </message>
+ <message>
+ <source>&amp;Console</source>
+ <translation>&amp;Конзола</translation>
+ </message>
+ <message>
+ <source>&amp;Network Traffic</source>
+ <translation>&amp; Саобраћај Мреже</translation>
+ </message>
+ <message>
+ <source>Totals</source>
+ <translation>Укупно</translation>
+ </message>
+ <message>
+ <source>In:</source>
+ <translation>Долазно:</translation>
+ </message>
+ <message>
+ <source>Out:</source>
+ <translation>Одлазно:</translation>
+ </message>
+ <message>
+ <source>Debug log file</source>
+ <translation>Дебугуј лог фајл</translation>
+ </message>
+ <message>
+ <source>Clear console</source>
+ <translation>Очисти конзолу</translation>
+ </message>
+ <message>
+ <source>1 &amp;hour</source>
+ <translation>1 &amp;Сат</translation>
+ </message>
+ <message>
+ <source>1 &amp;day</source>
+ <translation>1 &amp;дан</translation>
+ </message>
+ <message>
+ <source>1 &amp;week</source>
+ <translation>1 &amp;недеља</translation>
+ </message>
+ <message>
+ <source>1 &amp;year</source>
+ <translation>1 &amp;година</translation>
+ </message>
+ <message>
+ <source>&amp;Disconnect</source>
+ <translation>&amp;Прекини везу</translation>
+ </message>
+ <message>
+ <source>Ban for</source>
+ <translation>Забрани за</translation>
+ </message>
+ <message>
+ <source>&amp;Unban</source>
+ <translation>&amp;Уклони забрану</translation>
+ </message>
+ <message>
+ <source>Welcome to the %1 RPC console.</source>
+ <translation>Добродошли на %1 RPC конзоле.</translation>
+ </message>
+ <message>
+ <source>Use up and down arrows to navigate history, and %1 to clear screen.</source>
+ <translation>Користи стрелице горе и доле за навигацију историје, и %1 зa чишћење екрана.</translation>
+ </message>
+ <message>
+ <source>Type %1 for an overview of available commands.</source>
+ <translation>Укуцај %1 за преглед доступних команди.</translation>
+ </message>
+ <message>
+ <source>For more information on using this console type %1.</source>
+ <translation>За више информација о коришћењу конзиле укуцај %1.</translation>
+ </message>
+ <message>
+ <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>
+ <translation>УПОЗОРЕЊЕ: Преваранти активно говоре корисницима да овде укуцају команде, том приликом краду садржај новчаника. Немојте користити конзолу без претходног разумевања последица коришћења команди.</translation>
+ </message>
+ <message>
+ <source>Network activity disabled</source>
+ <translation>Активност мреже онемогућена</translation>
+ </message>
+ <message>
+ <source>Executing command without any wallet</source>
+ <translation>Извршење команде без новчаника</translation>
+ </message>
+ <message>
+ <source>Executing command using "%1" wallet</source>
+ <translation>Извршење команде коришћењем "%1" новчаника</translation>
+ </message>
+ <message>
+ <source>(node id: %1)</source>
+ <translation>(node id: %1)</translation>
+ </message>
+ <message>
+ <source>via %1</source>
+ <translation>преко %1</translation>
+ </message>
+ <message>
+ <source>never</source>
+ <translation>никад</translation>
+ </message>
+ <message>
+ <source>Inbound</source>
+ <translation>Долазеће</translation>
+ </message>
+ <message>
+ <source>Outbound</source>
+ <translation>Одлазеће</translation>
+ </message>
+ <message>
<source>Yes</source>
- <translation>Da</translation>
+ <translation>Да</translation>
</message>
<message>
<source>No</source>
- <translation>Ne</translation>
+ <translation>Не</translation>
</message>
- </context>
+ <message>
+ <source>Unknown</source>
+ <translation>Непознато</translation>
+ </message>
+</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>&amp;Amount:</source>
- <translation>Iznos:</translation>
+ <translation>&amp;Износ:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Етикета</translation>
+ <translation>&amp;Ознака</translation>
</message>
<message>
<source>&amp;Message:</source>
<translation>Poruka:</translation>
</message>
<message>
+ <source>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>
+ <translation>Опциона порука коју можеш прикачити уз захтев за плаћање, која ће бити приказана када захтев буде отворен. Напомена: Порука неће бити послата са уплатом на Биткоин мрежи.</translation>
+ </message>
+ <message>
+ <source>An optional label to associate with the new receiving address.</source>
+ <translation>Опционална ознака за поистовећивање са новом примајућом адресом.</translation>
+ </message>
+ <message>
+ <source>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</source>
+ <translation>Користи ову форму како би захтевао уплату. Сва поља су &lt;b&gt;опционална&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source>
+ <translation>Опциони износ за захтев. Остави празно или нула уколико не желиш прецизирати износ.</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Опционална ознака за поистовећивање са новом адресом примаоца (користите је за идентификацију рачуна). Она је такође придодата захтеву за плаћање.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Опциона порука која је придодата захтеву за плаћање и може бити приказана пошиљаоцу.</translation>
+ </message>
+ <message>
+ <source>&amp;Create new receiving address</source>
+ <translation>&amp;Направи нову адресу за примање</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>Очисти сва пола форме.</translation>
+ </message>
+ <message>
+ <source>Clear</source>
+ <translation>Очисти</translation>
+ </message>
+ <message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Природне segwit адресе (нпр Bech32 или BIP-173) касније смањују трошкове трансакција и нуде бољу заштиту од грешака у куцању, али их стари новчаници не подржавају. Када није одабрано, биће креирана адреса компатибилна са старијим новчаницима.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Направи segwit (Bech32) адресу</translation>
+ </message>
+ <message>
+ <source>Requested payments history</source>
+ <translation>Историја захтева за плаћање</translation>
+ </message>
+ <message>
+ <source>Show the selected request (does the same as double clicking an entry)</source>
+ <translation>Прикажи селектовани захтев (има исту сврху као и дупли клик на одговарајући унос)</translation>
+ </message>
+ <message>
<source>Show</source>
- <translation>Prikaži</translation>
+ <translation>Прикажи</translation>
+ </message>
+ <message>
+ <source>Remove the selected entries from the list</source>
+ <translation>Уклони одабрани унос из листе</translation>
+ </message>
+ <message>
+ <source>Remove</source>
+ <translation>Уклони</translation>
+ </message>
+ <message>
+ <source>Copy URI</source>
+ <translation>Копирај URI</translation>
</message>
<message>
<source>Copy label</source>
- <translation>Копирај налепницу
-</translation>
+ <translation>Копирај ознаку</translation>
+ </message>
+ <message>
+ <source>Copy message</source>
+ <translation>Копирај поруку</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>к</translation>
+ <translation>Копирај износ</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
+ <source>QR Code</source>
+ <translation>QR Код</translation>
+ </message>
+ <message>
+ <source>Copy &amp;URI</source>
+ <translation>Копирај &amp;URI</translation>
+ </message>
+ <message>
<source>Copy &amp;Address</source>
- <translation>Kopirajte adresu</translation>
+ <translation>Копирај &amp;Адресу</translation>
+ </message>
+ <message>
+ <source>&amp;Save Image...</source>
+ <translation>&amp;Сачувај Слику...</translation>
+ </message>
+ <message>
+ <source>Request payment to %1</source>
+ <translation>Захтевај уплату ка %1</translation>
+ </message>
+ <message>
+ <source>Payment information</source>
+ <translation>Информације о плаћању</translation>
+ </message>
+ <message>
+ <source>URI</source>
+ <translation>URI</translation>
</message>
<message>
<source>Address</source>
- <translation>Adresa</translation>
+ <translation>Адреса</translation>
</message>
<message>
<source>Amount</source>
@@ -974,11 +2108,11 @@
</message>
<message>
<source>Label</source>
- <translation>Налепница</translation>
+ <translation>Ознака</translation>
</message>
<message>
<source>Message</source>
- <translation>Poruka</translation>
+ <translation>Порука</translation>
</message>
<message>
<source>Wallet</source>
@@ -989,11 +2123,11 @@
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
- <translation>datum</translation>
+ <translation>Датум</translation>
</message>
<message>
<source>Label</source>
- <translation>Налепница</translation>
+ <translation>Ознака</translation>
</message>
<message>
<source>Message</source>
@@ -1001,14 +2135,42 @@
</message>
<message>
<source>(no label)</source>
- <translation>(без налепнице)</translation>
+ <translation>(без ознаке)</translation>
</message>
- </context>
+ <message>
+ <source>(no message)</source>
+ <translation>(нема поруке)</translation>
+ </message>
+ <message>
+ <source>(no amount requested)</source>
+ <translation>(нема захтеваног износа)</translation>
+ </message>
+ <message>
+ <source>Requested</source>
+ <translation>Захтевано</translation>
+ </message>
+</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Send Coins</source>
- <translation>Слање новца</translation>
+ <translation>Пошаљи новчиће</translation>
+ </message>
+ <message>
+ <source>Coin Control Features</source>
+ <translation>Опција контроле новчића</translation>
+ </message>
+ <message>
+ <source>Inputs...</source>
+ <translation>Инпути...</translation>
+ </message>
+ <message>
+ <source>automatically selected</source>
+ <translation>аутоматски одабрано</translation>
+ </message>
+ <message>
+ <source>Insufficient funds!</source>
+ <translation>Недовољно средстава!</translation>
</message>
<message>
<source>Quantity:</source>
@@ -1020,7 +2182,7 @@
</message>
<message>
<source>Amount:</source>
- <translation>Iznos:</translation>
+ <translation>Износ:</translation>
</message>
<message>
<source>Fee:</source>
@@ -1032,17 +2194,109 @@
</message>
<message>
<source>Change:</source>
- <translation>Промени:</translation>
+ <translation>Кусур:</translation>
+ </message>
+ <message>
+ <source>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source>
+ <translation>Уколико је ово активирано, али је промењена адреса празна или неважећа, промена ће бити послата на ново-генерисану адресу.</translation>
+ </message>
+ <message>
+ <source>Custom change address</source>
+ <translation>Прилагођена промењена адреса</translation>
+ </message>
+ <message>
+ <source>Transaction Fee:</source>
+ <translation>Провизија за трансакцију:</translation>
+ </message>
+ <message>
+ <source>Choose...</source>
+ <translation>Одабери...</translation>
+ </message>
+ <message>
+ <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>Коришћење безбедносне накнаде може резултовати у времену потребно за потврду трансакције од неколико сати или дана (или никад). Размислите о ручном одабиру провизије или сачекајте док нисте потврдили комплетан ланац.</translation>
+ </message>
+ <message>
+ <source>Warning: Fee estimation is currently not possible.</source>
+ <translation>Упозорење: Процена провизије тренутно није могућа.</translation>
+ </message>
+ <message>
+ <source>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.</source>
+ <translation>Одредити прилагођену провизију по kB (1,000 битова) виртуелне величине трансакције.
+
+Напомена: С обзиром да се провизија рачуна на основу броја бајтова, провизија за "100 сатошија по kB" за величину трансакције од 500 бајтова (пола од 1 kB) ће аутоматски износити само 50 сатошија.</translation>
+ </message>
+ <message>
+ <source>per kilobyte</source>
+ <translation>по килобајту</translation>
</message>
<message>
<source>Hide</source>
<translation>Сакриј</translation>
</message>
<message>
+ <source>Recommended:</source>
+ <translation>Препоручено:</translation>
+ </message>
+ <message>
+ <source>Custom:</source>
+ <translation>Прилагођено:</translation>
+ </message>
+ <message>
+ <source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
+ <translation>(Паметна накнада још није покренута. Ово уобичајено траје неколико блокова...)</translation>
+ </message>
+ <message>
+ <source>Send to multiple recipients at once</source>
+ <translation>Пошаљи већем броју примаоца одједанпут</translation>
+ </message>
+ <message>
+ <source>Add &amp;Recipient</source>
+ <translation>Додај &amp;Примаоца</translation>
+ </message>
+ <message>
+ <source>Clear all fields of the form.</source>
+ <translation>Очисти сва поља форме.</translation>
+ </message>
+ <message>
<source>Dust:</source>
<translation>Прашина:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Сакријте износ накнаде за трансакцију</translation>
+ </message>
+ <message>
+ <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>Када је мањи обим трансакција од простора у блоку, рудари, као и повезани нодови могу применити минималну провизију. Плаћање само минималне накнаде - провизије је добро, али треба бити свестан да ово може резултовати трансакцијом која неће никада бити потврђена, у случају када је број захтева за биткоин трансакцијама већи од могућности мреже да обради.</translation>
+ </message>
+ <message>
+ <source>A too low fee might result in a never confirming transaction (read the tooltip)</source>
+ <translation>Сувише ниска накнада може резултовати у трансакцији која никад неће бити потврђена (прочитајте опис)</translation>
+ </message>
+ <message>
+ <source>Confirmation time target:</source>
+ <translation>Циљно време потврде:</translation>
+ </message>
+ <message>
+ <source>Enable Replace-By-Fee</source>
+ <translation>Омогући Замени-за-Провизију</translation>
+ </message>
+ <message>
+ <source>With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source>
+ <translation>Са Замени-за-Провизију (BIP-125) се може повећати висина провизије за трансакцију након што је послата. Без овога, виша провизија може бити препоручена да се смањи ризик од кашњења трансакције. </translation>
+ </message>
+ <message>
+ <source>Clear &amp;All</source>
+ <translation>Очисти &amp;Све</translation>
+ </message>
+ <message>
+ <source>Balance:</source>
+ <translation>Салдо:</translation>
+ </message>
+ <message>
<source>Confirm the send action</source>
<translation>Потврди акцију слања</translation>
</message>
@@ -1056,7 +2310,7 @@
</message>
<message>
<source>Copy amount</source>
- <translation>к</translation>
+ <translation>Копирај износ</translation>
</message>
<message>
<source>Copy fee</source>
@@ -1079,141 +2333,649 @@
<translation>Копирај промену</translation>
</message>
<message>
+ <source>%1 (%2 blocks)</source>
+ <translation>%1 (%2 блокови)</translation>
+ </message>
+ <message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Креирај непотписано</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Креира делимично потписану Биткоин трансакцију (PSBT) за коришћење са нпр. офлајн %1 новчаником, или PSBT компатибилним хардверским новчаником. </translation>
+ </message>
+ <message>
+ <source> from wallet '%1'</source>
+ <translation>из новчаника '%1'</translation>
+ </message>
+ <message>
+ <source>%1 to '%2'</source>
+ <translation>%1 до '%2'</translation>
+ </message>
+ <message>
+ <source>%1 to %2</source>
+ <translation>%1 до %2</translation>
+ </message>
+ <message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Да ли желите да саставите ову трансакцију?</translation>
+ </message>
+ <message>
+ <source>Are you sure you want to send?</source>
+ <translation>Да ли сте сигурни да желите да пошаљете?</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Молим, проверите ваш предлог трансакције. Ово ће произвести делимично потписану Биткоин трансакцију (PSBT) коју можете копирати и онда потписати са нпр. офлајн %1 новчаником, или PSBT компатибилним хардверским новчаником.</translation>
+ </message>
+ <message>
+ <source>or</source>
+ <translation>или</translation>
+ </message>
+ <message>
+ <source>You can increase the fee later (signals Replace-By-Fee, BIP-125).</source>
+ <translation>Можете повећати провизију касније (сигнали Замени-са-Провизијом, BIP-125).</translation>
+ </message>
+ <message>
+ <source>Please, review your transaction.</source>
+ <translation>Молим, размотрите вашу трансакцију.</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>Провизија за трансакцију</translation>
+ </message>
+ <message>
+ <source>Not signalling Replace-By-Fee, BIP-125.</source>
+ <translation>Не сигнализира Замени-са-Провизијом, BIP-125.</translation>
+ </message>
+ <message>
+ <source>Total Amount</source>
+ <translation>Укупан износ</translation>
+ </message>
+ <message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>Да би сте размотрили листу примаоца кликните на "Прикажи детаље..."</translation>
+ </message>
+ <message>
+ <source>Confirm send coins</source>
+ <translation>Потврдите слање новчића</translation>
+ </message>
+ <message>
+ <source>Confirm transaction proposal</source>
+ <translation>Потврдите предлог трансакције</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Копирајте PSBT у базу за копирање</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Пошаљи</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT је копиран</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Само-гледање Стање:</translation>
+ </message>
+ <message>
+ <source>The recipient address is not valid. Please recheck.</source>
+ <translation>Адреса примаоца није валидна. Молим проверите поново.</translation>
+ </message>
+ <message>
+ <source>The amount to pay must be larger than 0.</source>
+ <translation>Овај износ за плаћање мора бити већи од 0.</translation>
+ </message>
+ <message>
+ <source>The amount exceeds your balance.</source>
+ <translation>Овај износ је већи од вашег салда.</translation>
+ </message>
+ <message>
+ <source>The total exceeds your balance when the %1 transaction fee is included.</source>
+ <translation>Укупни износ премашује ваш салдо, када се %1 провизија за трансакцију укључи у износ.</translation>
+ </message>
+ <message>
+ <source>Duplicate address found: addresses should only be used once each.</source>
+ <translation>Пронађена је дуплирана адреса: адресе се требају користити само једном.</translation>
+ </message>
+ <message>
+ <source>Transaction creation failed!</source>
+ <translation>Израда трансакције није успела!</translation>
+ </message>
+ <message>
+ <source>A fee higher than %1 is considered an absurdly high fee.</source>
+ <translation>Провизија већа од %1 се сматра апсурдно високом провизијом.</translation>
+ </message>
+ <message>
+ <source>Payment request expired.</source>
+ <translation>Захтев за плаћање је истекао.</translation>
+ </message>
+ <message numerus="yes">
+ <source>Estimated to begin confirmation within %n block(s).</source>
+ <translation><numerusform>Процењује се да ће започети потврду унутар %n блока.</numerusform><numerusform>Процењује се да ће започети потврду унутар %n блока.</numerusform><numerusform>Процењује се да ће започети потврду унутар %n блокова.</numerusform></translation>
+ </message>
+ <message>
+ <source>Warning: Invalid Bitcoin address</source>
+ <translation>Упозорење: Неважећа Биткоин адреса</translation>
+ </message>
+ <message>
+ <source>Warning: Unknown change address</source>
+ <translation>Упозорење: Непозната адреса за промену</translation>
+ </message>
+ <message>
+ <source>Confirm custom change address</source>
+ <translation>Потврдите прилагођену адресу за промену</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Адреса коју сте одабрали за промену није део овог новчаника. Део или цео износ вашег новчаника може бити послат на ову адресу. Да ли сте сигурни?</translation>
+ </message>
+ <message>
<source>(no label)</source>
- <translation>(без налепнице)</translation>
+ <translation>(без ознаке)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>A&amp;mount:</source>
- <translation>Iznos:</translation>
+ <translation>&amp;Износ:</translation>
+ </message>
+ <message>
+ <source>Pay &amp;To:</source>
+ <translation>Плати &amp;За:</translation>
</message>
<message>
<source>&amp;Label:</source>
- <translation>&amp;Етикета</translation>
+ <translation>&amp;Ознака</translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>Одабери претходно коришћену адресу</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to send the payment to</source>
+ <translation>Биткоин адреса на коју се шаље уплата</translation>
</message>
<message>
<source>Alt+A</source>
- <translation>Alt+</translation>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Paste address from clipboard</source>
+ <translation>Налепите адресу из базе за копирање</translation>
</message>
<message>
<source>Alt+P</source>
<translation>Alt+П</translation>
</message>
<message>
+ <source>Remove this entry</source>
+ <translation>Уклоните овај унос</translation>
+ </message>
+ <message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Износ који ће бити послат у одабрану јединицу</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Провизија ће бити одузета од износа који је послат. Примаоц ће добити мање биткоина него што је унесено у поље за износ. Уколико је одабрано више примаоца, провизија се дели равномерно.</translation>
+ </message>
+ <message>
+ <source>S&amp;ubtract fee from amount</source>
+ <translation>&amp;Одузми провизију од износа</translation>
+ </message>
+ <message>
+ <source>Use available balance</source>
+ <translation>Користи расположиви салдо</translation>
+ </message>
+ <message>
<source>Message:</source>
- <translation>Poruka:</translation>
+ <translation>Порука:</translation>
+ </message>
+ <message>
+ <source>This is an unauthenticated payment request.</source>
+ <translation>Ово је неовлашћени захтев за плаћање.</translation>
+ </message>
+ <message>
+ <source>This is an authenticated payment request.</source>
+ <translation>Ово је овлашћени захтев за плаћање.</translation>
</message>
- </context>
+ <message>
+ <source>Enter a label for this address to add it to the list of used addresses</source>
+ <translation>Унесите ознаку за ову адресу да бисте је додали на листу коришћених адреса</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Порука која је приложена биткоину: URI која ће бити сачувана уз трансакцију ради референце. Напомена: Ова порука се шаље преко Биткоин мреже.</translation>
+ </message>
+ <message>
+ <source>Pay To:</source>
+ <translation>Плати ка:</translation>
+ </message>
+ <message>
+ <source>Memo:</source>
+ <translation>Мемо:</translation>
+ </message>
+</context>
<context>
<name>ShutdownWindow</name>
- </context>
+ <message>
+ <source>%1 is shutting down...</source>
+ <translation>%1 се искључује</translation>
+ </message>
+ <message>
+ <source>Do not shut down the computer until this window disappears.</source>
+ <translation>Немојте искључити рачунар док овај прозор не нестане.</translation>
+ </message>
+</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
+ <source>Signatures - Sign / Verify a Message</source>
+ <translation>Потписи - Потпиши / Потврди поруку</translation>
+ </message>
+ <message>
+ <source>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>
+ <translation>Можете потписати поруку/споразум са вашом адресом да би сте доказали да можете примити биткоин послат ка њима. Будите опрезни да не потписујете ништа нејасно или случајно, јер се може десити напад крађе идентитета, да потпишете ваш идентитет нападачу. Потпишите само потпуно детаљне изјаве са којима се слажете.</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address to sign the message with</source>
+ <translation>Биткоин адреса са којом ћете потписати поруку</translation>
+ </message>
+ <message>
+ <source>Choose previously used address</source>
+ <translation>Промени претходно коришћену адресу</translation>
+ </message>
+ <message>
<source>Alt+A</source>
- <translation>Alt+</translation>
+ <translation>Alt+A</translation>
+ </message>
+ <message>
+ <source>Paste address from clipboard</source>
+ <translation>Налепите адресу из базе за копирање</translation>
</message>
<message>
<source>Alt+P</source>
- <translation>Alt+П</translation>
+ <translation>Alt+P</translation>
+ </message>
+ <message>
+ <source>Enter the message you want to sign here</source>
+ <translation>Унесите поруку коју желите да потпишете овде</translation>
+ </message>
+ <message>
+ <source>Signature</source>
+ <translation>Потпис</translation>
+ </message>
+ <message>
+ <source>Copy the current signature to the system clipboard</source>
+ <translation>Копирајте тренутни потпис у системску базу за копирање</translation>
+ </message>
+ <message>
+ <source>Sign the message to prove you own this Bitcoin address</source>
+ <translation>Потпишите поруку да докажете да сте власник ове Биткоин адресе</translation>
+ </message>
+ <message>
+ <source>Sign &amp;Message</source>
+ <translation>Потпис &amp;Порука</translation>
+ </message>
+ <message>
+ <source>Reset all sign message fields</source>
+ <translation>Поништите сва поља за потписивање поруке</translation>
+ </message>
+ <message>
+ <source>Clear &amp;All</source>
+ <translation>Очисти &amp;Све</translation>
+ </message>
+ <message>
+ <source>&amp;Verify Message</source>
+ <translation>&amp;Потврди поруку</translation>
+ </message>
+ <message>
+ <source>Enter the receiver'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>
+ <translation>Унесите адресу примаоца, поруку (осигурајте да тачно копирате прекиде линија, размаке, картице итд) и потпишите испод да потврдите поруку. Будите опрезни да не убаците више у потпис од онога што је у потписаној поруци, да би сте избегли напад посредника. Имајте на уму да потпис само доказује да потписник прима са потписаном адресом, а не може да докаже слање било које трансакције!</translation>
+ </message>
+ <message>
+ <source>The Bitcoin address the message was signed with</source>
+ <translation>Биткоин адреса са којом је потписана порука</translation>
+ </message>
+ <message>
+ <source>The signed message to verify</source>
+ <translation>Потписана порука за потврду</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Потпис који је дат приликом потписивања поруке</translation>
+ </message>
+ <message>
+ <source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
+ <translation>Потврдите поруку да осигурате да је потписана са одговарајућом Биткоин адресом</translation>
+ </message>
+ <message>
+ <source>Verify &amp;Message</source>
+ <translation>Потврди &amp;Поруку</translation>
+ </message>
+ <message>
+ <source>Reset all verify message fields</source>
+ <translation>Поништите сва поља за потврду поруке</translation>
+ </message>
+ <message>
+ <source>Click "Sign Message" to generate signature</source>
+ <translation>Притисни "Потпиши поруку" за израду потписа</translation>
+ </message>
+ <message>
+ <source>The entered address is invalid.</source>
+ <translation>Унесена адреса није важећа.</translation>
+ </message>
+ <message>
+ <source>Please check the address and try again.</source>
+ <translation>Молим проверите адресу и покушајте поново.</translation>
+ </message>
+ <message>
+ <source>The entered address does not refer to a key.</source>
+ <translation>Унесена адреса се не односи на кључ.</translation>
+ </message>
+ <message>
+ <source>Wallet unlock was cancelled.</source>
+ <translation>Откључавање новчаника је отказано.</translation>
+ </message>
+ <message>
+ <source>No error</source>
+ <translation>Нема грешке</translation>
</message>
- </context>
+ <message>
+ <source>Private key for the entered address is not available.</source>
+ <translation>Приватни кључ за унесену адресу није доступан.</translation>
+ </message>
+ <message>
+ <source>Message signing failed.</source>
+ <translation>Потписивање поруке није успело.</translation>
+ </message>
+ <message>
+ <source>Message signed.</source>
+ <translation>Порука је потписана.</translation>
+ </message>
+ <message>
+ <source>The signature could not be decoded.</source>
+ <translation>Потпис не може бити декодиран.</translation>
+ </message>
+ <message>
+ <source>Please check the signature and try again.</source>
+ <translation>Молим проверите потпис и покушајте поново.</translation>
+ </message>
+ <message>
+ <source>The signature did not match the message digest.</source>
+ <translation>Потпис се не подудара са прегледом порука.</translation>
+ </message>
+ <message>
+ <source>Message verification failed.</source>
+ <translation>Провера поруке није успела.</translation>
+ </message>
+ <message>
+ <source>Message verified.</source>
+ <translation>Порука је проверена.</translation>
+ </message>
+</context>
<context>
<name>TrafficGraphWidget</name>
- </context>
+ <message>
+ <source>KB/s</source>
+ <translation>KB/s</translation>
+ </message>
+</context>
<context>
<name>TransactionDesc</name>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Отворено за још %n блок.</numerusform><numerusform>Отворено за још %n блока</numerusform><numerusform>Отворено за још %n блокова</numerusform></translation>
+ </message>
<message>
<source>Open until %1</source>
<translation>Otvoreno do %1</translation>
</message>
<message>
+ <source>0/unconfirmed, %1</source>
+ <translation>0/непотврђено, %1</translation>
+ </message>
+ <message>
+ <source>in memory pool</source>
+ <translation>у удруженој меморији</translation>
+ </message>
+ <message>
+ <source>not in memory pool</source>
+ <translation>није у удруженој меморији</translation>
+ </message>
+ <message>
+ <source>abandoned</source>
+ <translation>напуштено</translation>
+ </message>
+ <message>
<source>%1/unconfirmed</source>
- <translation>%1/nepotvrdjeno</translation>
+ <translation>%1/непотврђено</translation>
</message>
<message>
<source>%1 confirmations</source>
- <translation>%1 potvrde</translation>
+ <translation>%1 порврде</translation>
+ </message>
+ <message>
+ <source>Status</source>
+ <translation>Статус</translation>
</message>
<message>
<source>Date</source>
- <translation>datum</translation>
+ <translation>Датум</translation>
+ </message>
+ <message>
+ <source>Source</source>
+ <translation>Извор</translation>
+ </message>
+ <message>
+ <source>Generated</source>
+ <translation>Генерисано</translation>
+ </message>
+ <message>
+ <source>From</source>
+ <translation>Од</translation>
</message>
<message>
<source>unknown</source>
- <translation>nepoznato</translation>
+ <translation>непознато</translation>
+ </message>
+ <message>
+ <source>To</source>
+ <translation>За</translation>
+ </message>
+ <message>
+ <source>own address</source>
+ <translation>сопствена адреса</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>гледај-само</translation>
</message>
<message>
<source>label</source>
- <translation>етикета</translation>
+ <translation>ознака</translation>
+ </message>
+ <message>
+ <source>Credit</source>
+ <translation>Заслуге</translation>
+ </message>
+ <message numerus="yes">
+ <source>matures in %n more block(s)</source>
+ <translation><numerusform>сазрева за %n блок</numerusform><numerusform>сазрева за %n блока</numerusform><numerusform>сазрева за %n блокова</numerusform></translation>
+ </message>
+ <message>
+ <source>not accepted</source>
+ <translation>није прихваћено</translation>
+ </message>
+ <message>
+ <source>Debit</source>
+ <translation>Задужење</translation>
+ </message>
+ <message>
+ <source>Total debit</source>
+ <translation>Укупно задужење</translation>
+ </message>
+ <message>
+ <source>Total credit</source>
+ <translation>Укупни кредит</translation>
+ </message>
+ <message>
+ <source>Transaction fee</source>
+ <translation>Провизија за трансакцију</translation>
+ </message>
+ <message>
+ <source>Net amount</source>
+ <translation>Нето износ</translation>
</message>
<message>
<source>Message</source>
- <translation>Poruka</translation>
+ <translation>Порука</translation>
+ </message>
+ <message>
+ <source>Comment</source>
+ <translation>Коментар</translation>
+ </message>
+ <message>
+ <source>Transaction ID</source>
+ <translation>ID Трансакције</translation>
+ </message>
+ <message>
+ <source>Transaction total size</source>
+ <translation>Укупна величина трансакције</translation>
+ </message>
+ <message>
+ <source>Transaction virtual size</source>
+ <translation>Виртуелна величина трансакције</translation>
+ </message>
+ <message>
+ <source>Output index</source>
+ <translation>Излазни индекс</translation>
+ </message>
+ <message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Сертификат још није проверен)</translation>
+ </message>
+ <message>
+ <source>Merchant</source>
+ <translation>Трговац</translation>
+ </message>
+ <message>
+ <source>Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source>
+ <translation>Генерисани новчићи морају доспети %1 блокова пре него што могу бити потрошени. Када генеришете овај блок, он се емитује у мрежу, да би био придодат на ланац блокова. Укупно не успе да се придода на ланац, његово стање се мења у "није прихваћен" и неће га бити могуће потрошити. Ово се може повремено десити уколико други чвор генерише блок у периоду од неколико секунди од вашег.</translation>
+ </message>
+ <message>
+ <source>Debug information</source>
+ <translation>Информације о оклањању грешака</translation>
</message>
<message>
<source>Transaction</source>
- <translation>transakcije</translation>
+ <translation>Трансакције</translation>
+ </message>
+ <message>
+ <source>Inputs</source>
+ <translation>Инпути</translation>
</message>
<message>
<source>Amount</source>
<translation>Износ</translation>
</message>
- </context>
+ <message>
+ <source>true</source>
+ <translation>тачно</translation>
+ </message>
+ <message>
+ <source>false</source>
+ <translation>нетачно</translation>
+ </message>
+</context>
<context>
<name>TransactionDescDialog</name>
<message>
<source>This pane shows a detailed description of the transaction</source>
- <translation>Ovaj odeljak pokazuje detaljan opis transakcije</translation>
+ <translation>Овај одељак приказује детањан приказ трансакције</translation>
+ </message>
+ <message>
+ <source>Details for %1</source>
+ <translation>Детаљи за %1</translation>
</message>
- </context>
+</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
- <translation>datum</translation>
+ <translation>Датум</translation>
</message>
<message>
<source>Type</source>
- <translation>tip</translation>
+ <translation>Тип</translation>
</message>
<message>
<source>Label</source>
- <translation>Налепница</translation>
+ <translation>Ознака</translation>
+ </message>
+ <message numerus="yes">
+ <source>Open for %n more block(s)</source>
+ <translation><numerusform>Отворено за још %n блок </numerusform><numerusform>Отворено за још %n блока</numerusform><numerusform> Отворено за још %n блокова</numerusform></translation>
</message>
<message>
<source>Open until %1</source>
- <translation>Otvoreno do %1</translation>
+ <translation>Отворено до %1</translation>
+ </message>
+ <message>
+ <source>Unconfirmed</source>
+ <translation>Непотврђено</translation>
+ </message>
+ <message>
+ <source>Abandoned</source>
+ <translation>Напуштено</translation>
+ </message>
+ <message>
+ <source>Confirming (%1 of %2 recommended confirmations)</source>
+ <translation>Потврђивање у току (%1 од %2 препоручене потврде)</translation>
</message>
<message>
<source>Confirmed (%1 confirmations)</source>
<translation>Potvrdjena (%1 potvrdjenih)</translation>
</message>
<message>
+ <source>Conflicted</source>
+ <translation>Неуслагашен</translation>
+ </message>
+ <message>
+ <source>Immature (%1 confirmations, will be available after %2)</source>
+ <translation>Није доспео (%1 потврде, биће доступан након %2)</translation>
+ </message>
+ <message>
<source>Generated but not accepted</source>
- <translation>Generisan ali nije prihvaćen</translation>
+ <translation>Генерисан али није прихваћен</translation>
</message>
<message>
<source>Received with</source>
- <translation>Primljen sa</translation>
+ <translation>Примљен са</translation>
</message>
<message>
<source>Received from</source>
- <translation>Primljeno od</translation>
+ <translation>Примљено од</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Poslat ka</translation>
+ <translation>Послато ка</translation>
</message>
<message>
<source>Payment to yourself</source>
- <translation>Isplata samom sebi</translation>
+ <translation>Уплата самом себи</translation>
</message>
<message>
<source>Mined</source>
- <translation>Minirano</translation>
+ <translation>Рударено</translation>
+ </message>
+ <message>
+ <source>watch-only</source>
+ <translation>гледај-само</translation>
</message>
<message>
<source>(n/a)</source>
@@ -1221,78 +2983,98 @@
</message>
<message>
<source>(no label)</source>
- <translation>(без налепнице)</translation>
+ <translation>(без ознаке)</translation>
</message>
<message>
<source>Transaction status. Hover over this field to show number of confirmations.</source>
- <translation>Status vaše transakcije. Predjite mišem preko ovog polja da bi ste videli broj konfirmacija</translation>
+ <translation>Статус трансакције. Пређи мишем преко поља за приказ броја трансакција.</translation>
</message>
<message>
<source>Date and time that the transaction was received.</source>
- <translation>Datum i vreme primljene transakcije.</translation>
+ <translation>Датум и време пријема трансакције</translation>
</message>
<message>
<source>Type of transaction.</source>
- <translation>Tip transakcije</translation>
+ <translation>Тип трансакције.</translation>
+ </message>
+ <message>
+ <source>Whether or not a watch-only address is involved in this transaction.</source>
+ <translation>Без обзира да ли је у ову трансакције укључена или није - адреса само за гледање.</translation>
+ </message>
+ <message>
+ <source>User-defined intent/purpose of the transaction.</source>
+ <translation>Намена / сврха трансакције коју одређује корисник.</translation>
</message>
<message>
<source>Amount removed from or added to balance.</source>
- <translation>Iznos odbijen ili dodat balansu.</translation>
+ <translation>Износ одбијен или додат салду.</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
- <translation>Sve</translation>
+ <translation>Све</translation>
</message>
<message>
<source>Today</source>
- <translation>Danas</translation>
+ <translation>Данас</translation>
</message>
<message>
<source>This week</source>
- <translation>ove nedelje</translation>
+ <translation>Oве недеље</translation>
</message>
<message>
<source>This month</source>
- <translation>Ovog meseca</translation>
+ <translation>Овог месеца</translation>
</message>
<message>
<source>Last month</source>
- <translation>Prošlog meseca</translation>
+ <translation>Претходног месеца</translation>
</message>
<message>
<source>This year</source>
- <translation>Ove godine</translation>
+ <translation>Ове године</translation>
</message>
<message>
<source>Range...</source>
- <translation>Opseg...</translation>
+ <translation>Опсег...</translation>
</message>
<message>
<source>Received with</source>
- <translation>Primljen sa</translation>
+ <translation>Примљен са...</translation>
</message>
<message>
<source>Sent to</source>
- <translation>Poslat ka</translation>
+ <translation>Послат ка</translation>
</message>
<message>
<source>To yourself</source>
- <translation>Vama - samom sebi</translation>
+ <translation>Теби</translation>
</message>
<message>
<source>Mined</source>
- <translation>Minirano</translation>
+ <translation>Рударено</translation>
</message>
<message>
<source>Other</source>
- <translation>Drugi</translation>
+ <translation>Други</translation>
+ </message>
+ <message>
+ <source>Enter address, transaction id, or label to search</source>
+ <translation>Унесите адресу, ознаку трансакције, или назив за претрагу</translation>
</message>
<message>
<source>Min amount</source>
- <translation>Min iznos</translation>
+ <translation>Минимални износ</translation>
+ </message>
+ <message>
+ <source>Abandon transaction</source>
+ <translation>Напусти трансакцију</translation>
+ </message>
+ <message>
+ <source>Increase transaction fee</source>
+ <translation>Повећај провизију трансакције</translation>
</message>
<message>
<source>Copy address</source>
@@ -1300,20 +3082,35 @@
</message>
<message>
<source>Copy label</source>
- <translation>Копирај налепницу
-</translation>
+ <translation>Копирај ознаку</translation>
</message>
<message>
<source>Copy amount</source>
- <translation>к</translation>
+ <translation>Копирај износ</translation>
</message>
<message>
<source>Copy transaction ID</source>
<translation>Копирај идентификациони број трансакције</translation>
</message>
<message>
+ <source>Copy raw transaction</source>
+ <translation>Копирајте необрађену трансакцију</translation>
+ </message>
+ <message>
+ <source>Copy full transaction details</source>
+ <translation>Копирајте потпуне детаље трансакције</translation>
+ </message>
+ <message>
<source>Edit label</source>
- <translation>promeni naziv</translation>
+ <translation>Измени ознаку</translation>
+ </message>
+ <message>
+ <source>Show transaction details</source>
+ <translation>Прикажи детаље транакције</translation>
+ </message>
+ <message>
+ <source>Export Transaction History</source>
+ <translation>Извези Детаље Трансакције</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
@@ -1321,46 +3118,86 @@
</message>
<message>
<source>Confirmed</source>
- <translation>Potvrdjen</translation>
+ <translation>Потврђено</translation>
+ </message>
+ <message>
+ <source>Watch-only</source>
+ <translation>Само-гледање</translation>
</message>
<message>
<source>Date</source>
- <translation>datum</translation>
+ <translation>Датум</translation>
</message>
<message>
<source>Type</source>
- <translation>tip</translation>
+ <translation>Тип</translation>
</message>
<message>
<source>Label</source>
- <translation>Налепница</translation>
+ <translation>Ознака</translation>
</message>
<message>
<source>Address</source>
- <translation>Adresa</translation>
+ <translation>Адреса</translation>
+ </message>
+ <message>
+ <source>ID</source>
+ <translation>ID</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Извоз Неуспешан</translation>
</message>
<message>
+ <source>There was an error trying to save the transaction history to %1.</source>
+ <translation>Десила се грешка приликом покушаја да се сними историја трансакција на %1.</translation>
+ </message>
+ <message>
+ <source>Exporting Successful</source>
+ <translation>Извоз Успешан</translation>
+ </message>
+ <message>
+ <source>The transaction history was successfully saved to %1.</source>
+ <translation>Историја трансакција је успешно снимљена на %1.</translation>
+ </message>
+ <message>
<source>Range:</source>
- <translation>Opseg:</translation>
+ <translation>Опсег:</translation>
</message>
<message>
<source>to</source>
- <translation>do</translation>
+ <translation>до</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
- </context>
+ <message>
+ <source>Unit to show amounts in. Click to select another unit.</source>
+ <translation>Јединица у којој се приказују износи. Притисни да се прикаже друга јединица.</translation>
+ </message>
+</context>
<context>
<name>WalletController</name>
- </context>
+ <message>
+ <source>Close wallet</source>
+ <translation>Затвори новчаник</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Да ли сте сигурни да желите да затворите новчаник &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
+ <source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
+ <translation>Услед затварања новчаника на дугачки период времена може се десити да је потребна поновна синхронизација комплетног ланца, уколико је дозвољено резање.</translation>
+ </message>
+</context>
<context>
<name>WalletFrame</name>
- </context>
+ <message>
+ <source>No wallet has been loaded.</source>
+ <translation>Ниједан новчаник није учитан.</translation>
+ </message>
+</context>
<context>
<name>WalletModel</name>
<message>
@@ -1368,6 +3205,54 @@
<translation>Слање новца</translation>
</message>
<message>
+ <source>Fee bump error</source>
+ <translation>Изненадна грешка у накнади</translation>
+ </message>
+ <message>
+ <source>Increasing transaction fee failed</source>
+ <translation>Повећавање провизије за трансакцију није успело</translation>
+ </message>
+ <message>
+ <source>Do you want to increase the fee?</source>
+ <translation>Да ли желиш да увећаш накнаду?</translation>
+ </message>
+ <message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Да ли желите да саставите трансакцију са повећаном провизијом?</translation>
+ </message>
+ <message>
+ <source>Current fee:</source>
+ <translation>Тренутна накнада:</translation>
+ </message>
+ <message>
+ <source>Increase:</source>
+ <translation>Увећај:</translation>
+ </message>
+ <message>
+ <source>New fee:</source>
+ <translation>Нова накнада:</translation>
+ </message>
+ <message>
+ <source>Confirm fee bump</source>
+ <translation>Потврдите ударну провизију</translation>
+ </message>
+ <message>
+ <source>Can't draft transaction.</source>
+ <translation>Није могуће саставити трансакцију.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT је копиран</translation>
+ </message>
+ <message>
+ <source>Can't sign transaction.</source>
+ <translation>Није могуће потписати трансакцију.</translation>
+ </message>
+ <message>
+ <source>Could not commit transaction</source>
+ <translation>Трансакција није могућа</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>подразумевани новчаник</translation>
</message>
@@ -1376,7 +3261,7 @@
<name>WalletView</name>
<message>
<source>&amp;Export</source>
- <translation>&amp;Izvedi</translation>
+ <translation>&amp;Извези</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
@@ -1384,24 +3269,526 @@
</message>
<message>
<source>Backup Wallet</source>
- <translation>Backup новчаника</translation>
+ <translation>Резервна копија новчаника</translation>
+ </message>
+ <message>
+ <source>Wallet Data (*.dat)</source>
+ <translation>Датотека новчаника (*.dat)</translation>
</message>
- </context>
+ <message>
+ <source>Backup Failed</source>
+ <translation>Резервна копија није успела</translation>
+ </message>
+ <message>
+ <source>There was an error trying to save the wallet data to %1.</source>
+ <translation>Десила се грешка приликом покушаја да се сними датотека новчаника на %1.</translation>
+ </message>
+ <message>
+ <source>Backup Successful</source>
+ <translation>Резервна копија је успела</translation>
+ </message>
+ <message>
+ <source>The wallet data was successfully saved to %1.</source>
+ <translation>Датотека новчаника је успешно снимљена на %1.</translation>
+ </message>
+ <message>
+ <source>Cancel</source>
+ <translation>Откажи</translation>
+ </message>
+</context>
<context>
<name>bitcoin-core</name>
<message>
+ <source>Distributed under the MIT software license, see the accompanying file %s or %s</source>
+ <translation>Дистрибуирано под MIT софтверском лиценцом, погледајте придружени документ %s или %s</translation>
+ </message>
+ <message>
+ <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source>
+ <translation>Скраћивање је конфигурисано испод минимума од %d MiB. Молимо користите већи број.</translation>
+ </message>
+ <message>
+ <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>Скраћивање: последња синхронизација иде преко одрезаних података. Потребно је урадити ре-индексирање (преузети комплетан ланац блокова поново у случају одсеченог чвора)</translation>
+ </message>
+ <message>
+ <source>Error: A fatal internal error occurred, see debug.log for details</source>
+ <translation>Грешка: Десила се фатална интерна грешка, погледати debug.log за детаље</translation>
+ </message>
+ <message>
+ <source>Pruning blockstore...</source>
+ <translation>Скраћивање спремљених блокова...</translation>
+ </message>
+ <message>
+ <source>Unable to start HTTP server. See debug log for details.</source>
+ <translation>Стартовање HTTP сервера није могуће. Погледати дневник исправљених грешака за детаље.</translation>
+ </message>
+ <message>
+ <source>The %s developers</source>
+ <translation>%s девелопери</translation>
+ </message>
+ <message>
+ <source>Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.</source>
+ <translation>Кључ за промену адресе није могуће генерисати. У интерној групи кључева нема кључева и не може се генерисати нови кључ.</translation>
+ </message>
+ <message>
+ <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source>
+ <translation>Директоријум података се не може закључати %s. %s је вероватно већ покренут.</translation>
+ </message>
+ <message>
+ <source>Cannot provide specific connections and have addrman find outgoing connections at the same.</source>
+ <translation>Не може се обезбедити одређена конекција и да addrman нађе одлазне конекције у исто време.</translation>
+ </message>
+ <message>
+ <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source>
+ <translation>Грешка у читању %s! Сви кључеви су прочитани коректно, али подаци о трансакцији или уноси у адресар могу недостајати или бити нетачни.</translation>
+ </message>
+ <message>
+ <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source>
+ <translation>Молим проверите да су време и датум на вашем рачунару тачни. Уколико је сат нетачан, %s неће радити исправно.</translation>
+ </message>
+ <message>
+ <source>Please contribute if you find %s useful. Visit %s for further information about the software.</source>
+ <translation>Молим донирајте, уколико сматрате %s корисним. Посетите %s за више информација о софтверу.</translation>
+ </message>
+ <message>
+ <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source>
+ <translation>База података о блоковима садржи блок, за који се чини да је из будућности. Ово може бити услед тога што су време и датум на вашем рачунару нису подешени коректно. Покушајте обнову базе података о блоковима, само уколико сте сигурни да су време и датум на вашем рачунару исправни.</translation>
+ </message>
+ <message>
+ <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source>
+ <translation>Ово је тестна верзија пред издавање - користите на ваш ризик - не користити за рударење или трговачку примену</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may discard if change is smaller than dust at this level</source>
+ <translation>Ово је накнада за трансакцију коју можете одбацити уколико је мања од нивоа прашине</translation>
+ </message>
+ <message>
+ <source>Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source>
+ <translation>Блокове није могуће поново репродуковати. Ви ћете морати да обновите базу података користећи -reindex-chainstate.</translation>
+ </message>
+ <message>
+ <source>Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain</source>
+ <translation>Није могуће вратити базу података на стање пре форк-а. Ви ћете морати да урадите поновно преузимање ланца блокова.</translation>
+ </message>
+ <message>
+ <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source>
+ <translation>Упозорење: Изгледа да не постоји пуна сагласност на мрежи. Изгледа да одређени рудари имају проблеме.</translation>
+ </message>
+ <message>
+ <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>Упозорење: Изгледа да се ми у потпуности не слажемо са нашим чворовима! Можда постоји потреба да урадите надоградњу, или други чворови морају да ураде надоградњу.</translation>
+ </message>
+ <message>
+ <source>%d of last 100 blocks have unexpected version</source>
+ <translation>%d од последњих 100 блокова имају неочекивану верзију</translation>
+ </message>
+ <message>
+ <source>%s corrupt, salvage failed</source>
+ <translation>%s је оштећен, спас није успео</translation>
+ </message>
+ <message>
+ <source>-maxmempool must be at least %d MB</source>
+ <translation>-maxmempool мора бити минимално %d MB</translation>
+ </message>
+ <message>
+ <source>Cannot resolve -%s address: '%s'</source>
+ <translation>Не могу решити -%s адреса: '%s'</translation>
+ </message>
+ <message>
+ <source>Change index out of range</source>
+ <translation>Промењен индекс изван домета</translation>
+ </message>
+ <message>
+ <source>Config setting for %s only applied on %s network when in [%s] section.</source>
+ <translation>Подешавање конфигурације за %s је само примењено на %s мрежи када је у [%s] секцији.</translation>
+ </message>
+ <message>
+ <source>Copyright (C) %i-%i</source>
+ <translation>Ауторско право (C) %i-%i</translation>
+ </message>
+ <message>
+ <source>Corrupted block database detected</source>
+ <translation>Детектована је оштећена база података блокова</translation>
+ </message>
+ <message>
+ <source>Could not find asmap file %s</source>
+ <translation>Не могу пронаћи датотеку asmap %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Не могу рашчланити датотеку asmap %s</translation>
+ </message>
+ <message>
+ <source>Do you want to rebuild the block database now?</source>
+ <translation>Да ли желите да сада обновите базу података блокова?</translation>
+ </message>
+ <message>
+ <source>Error initializing block database</source>
+ <translation>Грешка у иницијализацији базе података блокова</translation>
+ </message>
+ <message>
+ <source>Error initializing wallet database environment %s!</source>
+ <translation>Грешка код иницијализације окружења базе података новчаника %s!</translation>
+ </message>
+ <message>
+ <source>Error loading %s</source>
+ <translation>Грешка током учитавања %s</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Private keys can only be disabled during creation</source>
+ <translation>Грешка током учитавања %s: Приватни кључеви могу бити онемогућени само приликом креирања</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet corrupted</source>
+ <translation>Грешка током учитавања %s: Новчаник је оштећен</translation>
+ </message>
+ <message>
+ <source>Error loading %s: Wallet requires newer version of %s</source>
+ <translation>Грешка током учитавања %s: Новчаник захтева новију верзију %s</translation>
+ </message>
+ <message>
+ <source>Error loading block database</source>
+ <translation>Грешка у учитавању базе података блокова</translation>
+ </message>
+ <message>
+ <source>Error opening block database</source>
+ <translation>Грешка приликом отварања базе података блокова</translation>
+ </message>
+ <message>
+ <source>Failed to listen on any port. Use -listen=0 if you want this.</source>
+ <translation>Преслушавање није успело ни на једном порту. Користите -listen=0 уколико желите то.</translation>
+ </message>
+ <message>
+ <source>Failed to rescan the wallet during initialization</source>
+ <translation>Није успело поновно скенирање новчаника приликом иницијализације.</translation>
+ </message>
+ <message>
+ <source>Importing...</source>
+ <translation>Увоз у току...</translation>
+ </message>
+ <message>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <translation>Почетни блок је погрешан или се не може пронаћи. Погрешан datadir за мрежу?</translation>
+ </message>
+ <message>
+ <source>Initialization sanity check failed. %s is shutting down.</source>
+ <translation>Провера исправности иницијализације није успела. %s се искључује.</translation>
+ </message>
+ <message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Неважећа P2P дозвола: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
+ <translation>Неважећи износ за %s=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -discardfee=&lt;amount&gt;: '%s'</source>
+ <translation>Неважећи износ за -discardfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -fallbackfee=&lt;amount&gt;: '%s'</source>
+ <translation>Неважећи износ за -fallbackfee=&lt;amount&gt;: '%s'</translation>
+ </message>
+ <message>
+ <source>Specified blocks directory "%s" does not exist.</source>
+ <translation>Наведени директоријум блокова "%s" не постоји.</translation>
+ </message>
+ <message>
+ <source>Unknown address type '%s'</source>
+ <translation>Непознати тип адресе '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Непознати тип промене '%s'</translation>
+ </message>
+ <message>
+ <source>Upgrading txindex database</source>
+ <translation>Надоградња txindex базе података</translation>
+ </message>
+ <message>
+ <source>Loading P2P addresses...</source>
+ <translation>Учитавање P2P адреса...</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is too low!</source>
+ <translation>Грешка: Простор на диску је сувише мали!</translation>
+ </message>
+ <message>
+ <source>Loading banlist...</source>
+ <translation>Учитавање листе забрана...</translation>
+ </message>
+ <message>
+ <source>Not enough file descriptors available.</source>
+ <translation>Нема довољно доступних дескриптора датотеке.</translation>
+ </message>
+ <message>
+ <source>Prune cannot be configured with a negative value.</source>
+ <translation>Скраћење се не може конфигурисати са негативном вредношћу.</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -txindex.</source>
+ <translation>Мод скраћивања није компатибилан са -txindex.</translation>
+ </message>
+ <message>
+ <source>Replaying blocks...</source>
+ <translation>Поновно репродуковање блокова...</translation>
+ </message>
+ <message>
+ <source>Rewinding blocks...</source>
+ <translation>Премотавање блокова...</translation>
+ </message>
+ <message>
+ <source>The source code is available from %s.</source>
+ <translation>Изворни код је доступан из %s.</translation>
+ </message>
+ <message>
+ <source>Transaction fee and change calculation failed</source>
+ <translation>Провизија за трансакцију и промена израчуна није успела</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer. %s is probably already running.</source>
+ <translation>Није могуће повезивање са %s на овом рачунару. %s је вероватно већ покренут.</translation>
+ </message>
+ <message>
+ <source>Unable to generate keys</source>
+ <translation>Није могуће генерисати кључеве</translation>
+ </message>
+ <message>
+ <source>Unsupported logging category %s=%s.</source>
+ <translation>Категорија записа није подржана %s=%s.</translation>
+ </message>
+ <message>
+ <source>Upgrading UTXO database</source>
+ <translation>Надоградња UTXO базе података</translation>
+ </message>
+ <message>
+ <source>User Agent comment (%s) contains unsafe characters.</source>
+ <translation>Коментар агента корисника (%s) садржи небезбедне знакове.</translation>
+ </message>
+ <message>
+ <source>Verifying blocks...</source>
+ <translation>Потврда блокова у току...</translation>
+ </message>
+ <message>
+ <source>Wallet needed to be rewritten: restart %s to complete</source>
+ <translation>Новчаник треба да буде преписан: поновно покрените %s да завршите</translation>
+ </message>
+ <message>
+ <source>Error: Listening for incoming connections failed (listen returned error %s)</source>
+ <translation>Грешка: Претрага за долазним конекцијама није успела (претрага враћа грешку %s)</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -maxtxfee=&lt;amount&gt;: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source>
+ <translation>Неважећи износ за -maxtxfee=&lt;amount&gt;: '%s' (мора бити minrelay провизија од %s да би се спречило да се трансакција заглави)</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to send after the fee has been deducted</source>
+ <translation>Износ трансакције је толико мали за слање након што се одузме провизија</translation>
+ </message>
+ <message>
+ <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source>
+ <translation>Обновите базу података користећи -reindex да би се вратили у нескраћени мод. Ово ће урадити поновно преузимање комплетног ланца података</translation>
+ </message>
+ <message>
+ <source>Error reading from database, shutting down.</source>
+ <translation>Грешка приликом читања из базе података, искључивање у току.</translation>
+ </message>
+ <message>
+ <source>Error upgrading chainstate database</source>
+ <translation>Грешка приликом надоградње базе података стања ланца</translation>
+ </message>
+ <message>
+ <source>Error: Disk space is low for %s</source>
+ <translation>Грешка: Простор на диску је мали за %s</translation>
+ </message>
+ <message>
+ <source>Invalid -onion address or hostname: '%s'</source>
+ <translation>Неважећа -onion адреса или име хоста: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid -proxy address or hostname: '%s'</source>
+ <translation>Неважећа -proxy адреса или име хоста: '%s'</translation>
+ </message>
+ <message>
+ <source>Invalid amount for -paytxfee=&lt;amount&gt;: '%s' (must be at least %s)</source>
+ <translation>Неважећи износ за -paytxfee=&lt;amount&gt;: '%s' (мора бити бар %s)</translation>
+ </message>
+ <message>
+ <source>Invalid netmask specified in -whitelist: '%s'</source>
+ <translation>Неважећа мрежна маска наведена у -whitelist: '%s'</translation>
+ </message>
+ <message>
+ <source>Need to specify a port with -whitebind: '%s'</source>
+ <translation>Ви морате одредити порт са -whitebind: '%s'</translation>
+ </message>
+ <message>
+ <source>Prune mode is incompatible with -blockfilterindex.</source>
+ <translation>Мод скраћења је некомпатибилна са -blockfilterindex.</translation>
+ </message>
+ <message>
+ <source>Reducing -maxconnections from %d to %d, because of system limitations.</source>
+ <translation>Смањивање -maxconnections са %d на %d, због ограничења система.</translation>
+ </message>
+ <message>
+ <source>Section [%s] is not recognized.</source>
+ <translation>Одељак [%s] није препознат.</translation>
+ </message>
+ <message>
+ <source>Signing transaction failed</source>
+ <translation>Потписивање трансакције није успело</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" does not exist</source>
+ <translation>Наведени -walletdir "%s" не постоји</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is a relative path</source>
+ <translation>Наведени -walletdir "%s" је релативна путања</translation>
+ </message>
+ <message>
+ <source>Specified -walletdir "%s" is not a directory</source>
+ <translation>Наведени -walletdir "%s" није директоријум</translation>
+ </message>
+ <message>
+ <source>The specified config file %s does not exist
+</source>
+ <translation>Наведени конфигурациони документ %s не постоји
+</translation>
+ </message>
+ <message>
+ <source>The transaction amount is too small to pay the fee</source>
+ <translation>Износ трансакције је сувише мали да се плати трансакција</translation>
+ </message>
+ <message>
+ <source>This is experimental software.</source>
+ <translation>Ово је експерименталн софтвер.</translation>
+ </message>
+ <message>
+ <source>Transaction amount too small</source>
+ <translation>Износ трансакције премали.</translation>
+ </message>
+ <message>
+ <source>Transaction too large</source>
+ <translation>Трансакција превелика.</translation>
+ </message>
+ <message>
+ <source>Unable to bind to %s on this computer (bind returned error %s)</source>
+ <translation>Није могуће повезати %s на овом рачунару (веза враћа грешку %s)</translation>
+ </message>
+ <message>
+ <source>Unable to create the PID file '%s': %s</source>
+ <translation>Стварање PID документа '%s': %s није могуће</translation>
+ </message>
+ <message>
+ <source>Unable to generate initial keys</source>
+ <translation>Генерисање кључева за иницијализацију није могуће</translation>
+ </message>
+ <message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Непозната вредност -blockfilterindex %s.</translation>
+ </message>
+ <message>
+ <source>Verifying wallet(s)...</source>
+ <translation>Потврђивање новчаника(а)...</translation>
+ </message>
+ <message>
+ <source>Warning: unknown new rules activated (versionbit %i)</source>
+ <translation>Упозорење: активирано је ново непознато правило (versionbit %i)</translation>
+ </message>
+ <message>
+ <source>Zapping all transactions from wallet...</source>
+ <translation>Затварање свих трансакција из новчаника...</translation>
+ </message>
+ <message>
+ <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source>
+ <translation>-maxtxfee је постављен сувише високо! Овако велике провизије могу бити наплаћене на само једној трансакцији.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you may pay when fee estimates are not available.</source>
+ <translation>Ово је провизија за трансакцију коју можете платити када процена провизије није доступна.</translation>
+ </message>
+ <message>
+ <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source>
+ <translation>Укупна дужина мрежне верзије низа (%i) је већа од максималне дужине (%i). Смањити број или величину корисничких коментара.</translation>
+ </message>
+ <message>
+ <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source>
+ <translation>Упозорење: Датотека новчаника је оштећена, подаци су спасени! Оргинални %s је снимљен као %s у %s; уколико ваш салдо или трансакције нису исправни, потребно је вратити податке из резервне копије. </translation>
+ </message>
+ <message>
+ <source>%s is set very high!</source>
+ <translation>%s је постављен врло високо!</translation>
+ </message>
+ <message>
+ <source>Error loading wallet %s. Duplicate -wallet filename specified.</source>
+ <translation>Грешка приликом учитавања новчаника %s. Наведено је дуплирано име датотеке -wallet.</translation>
+ </message>
+ <message>
+ <source>Starting network threads...</source>
+ <translation>Покретање мрежних тема...</translation>
+ </message>
+ <message>
+ <source>The wallet will avoid paying less than the minimum relay fee.</source>
+ <translation>Новчаник ће избећи плаћање износа мањег него што је минимална повезана провизија.</translation>
+ </message>
+ <message>
+ <source>This is the minimum transaction fee you pay on every transaction.</source>
+ <translation>Ово је минимални износ провизије за трансакцију коју ћете платити на свакој трансакцији.</translation>
+ </message>
+ <message>
+ <source>This is the transaction fee you will pay if you send a transaction.</source>
+ <translation>Ово је износ провизије за трансакцију коју ћете платити уколико шаљете трансакцију.</translation>
+ </message>
+ <message>
+ <source>Transaction amounts must not be negative</source>
+ <translation>Износ трансакције не може бити негативан</translation>
+ </message>
+ <message>
+ <source>Transaction has too long of a mempool chain</source>
+ <translation>Трансакција има предугачак ланац у удруженој меморији</translation>
+ </message>
+ <message>
+ <source>Transaction must have at least one recipient</source>
+ <translation>Трансакција мора имати бар једног примаоца</translation>
+ </message>
+ <message>
+ <source>Unknown network specified in -onlynet: '%s'</source>
+ <translation>Непозната мрежа је наведена у -onlynet: '%s'</translation>
+ </message>
+ <message>
<source>Insufficient funds</source>
- <translation>Nedovoljno sredstava</translation>
+ <translation>Недовољно средстава</translation>
+ </message>
+ <message>
+ <source>Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.</source>
+ <translation>HD подељени новчаник се не може надоградити без надоградње групе кључева пре дељења. Молим користите -upgradewallet=169900 или -upgradewallet без наведене верзије.</translation>
+ </message>
+ <message>
+ <source>Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source>
+ <translation>Процена провизије није успела. Промена провизије током трансакције је онемогућена. Сачекајте неколико блокова или омогућите -fallbackfee.</translation>
+ </message>
+ <message>
+ <source>Warning: Private keys detected in wallet {%s} with disabled private keys</source>
+ <translation>Упозорење: Приватни кључеви су пронађени у новчанику {%s} са онемогућеним приватним кључевима.</translation>
+ </message>
+ <message>
+ <source>Cannot write to data directory '%s'; check permissions.</source>
+ <translation>Није могуће извршити упис у директоријум података '%s'; проверите дозволе за упис.</translation>
</message>
<message>
<source>Loading block index...</source>
- <translation>Učitavam blok indeksa...</translation>
+ <translation>Учитавање индекса блокова</translation>
</message>
<message>
<source>Loading wallet...</source>
<translation>Новчаник се учитава...</translation>
</message>
<message>
+ <source>Cannot downgrade wallet</source>
+ <translation>Новчаник се не може уназадити</translation>
+ </message>
+ <message>
<source>Rescanning...</source>
<translation>Ponovo skeniram...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_sr@latin.ts b/src/qt/locale/bitcoin_sr@latin.ts
index a80c5a0891..02f0fb72b5 100644
--- a/src/qt/locale/bitcoin_sr@latin.ts
+++ b/src/qt/locale/bitcoin_sr@latin.ts
@@ -30,6 +30,10 @@
<translation>Briše trenutno izabranu adresu sa liste</translation>
</message>
<message>
+ <source>Enter address or label to search</source>
+ <translation>Unesite adresu ili oznaku za pretragu</translation>
+ </message>
+ <message>
<source>Export the data in the current tab to a file</source>
<translation>Izvoz podataka iz trenutne kartice u datoteku</translation>
</message>
@@ -66,6 +70,10 @@
<translation>Ovo su Vaše Bitcoin adrese na koju se vrše uplate. Uvek proverite iznos i prijemnu adresu pre slanja novčića.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Ovo su vaše Bitcoin adrese za primanje isplete. Upotrebite dugme 'Kreiraj novu adresu prijema' na kartici za prijem da biste kreirali nove adrese.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopiraj Adresu</translation>
</message>
@@ -128,6 +136,10 @@
<translation>Ponovo unesite pristupnu frazu</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Prikaži lozinku</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Šifrujte novčanik</translation>
</message>
@@ -168,6 +180,22 @@
<translation>Novčanik je šifrovan</translation>
</message>
<message>
+ <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>Unesite lozinku u novčanik. &lt;br/&gt;Molimo, koristite lozinku koja ima &lt;b&gt; deset ili više nasumičnih znakova&lt;/b&gt;, ili &lt;b&gt;osam ili više reči&lt;/b&gt;.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Unesite u novčanik staru lozinku i novu lozinku.</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Novčanik će vam biti šifriran.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Vaš novčanik je sada šifrovan.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>VAŽNO: Ranije rezervne kopije wallet datoteke trebate zameniti sa novo-kreiranom, enkriptovanom wallet datotekom. Iz sigurnosnih razloga, ranije ne-enkriptovane wallet datoteke će postati neupotrebljive čim počnete koristiti novi, enkriptovani novčanik.</translation>
</message>
@@ -290,6 +318,10 @@
<translation>Otvori &amp;URI...</translation>
</message>
<message>
+ <source>Wallet:</source>
+ <translation>Novčanik:</translation>
+ </message>
+ <message>
<source>Click to disable network activity.</source>
<translation>Odaberite za prekid aktivnosti na mreži.</translation>
</message>
@@ -386,6 +418,10 @@
<translation>Informacije</translation>
</message>
<message>
+ <source>Open Wallet</source>
+ <translation>Otvori novčanik</translation>
+ </message>
+ <message>
<source>%1 client</source>
<translation>%1 klijent</translation>
</message>
@@ -456,6 +492,10 @@
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <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>Onemogućite privatne ključeve za ovaj novčanik. Novčanici sa isključenim privatnim ključevima neće imati privatne ključeve i ne mogu imati HD seme ili uvezene privatne ključeve. Ovo je idealno za novčanike samo za gledanje.</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts
index e626b1a930..28bb524ef0 100644
--- a/src/qt/locale/bitcoin_sv.ts
+++ b/src/qt/locale/bitcoin_sv.ts
@@ -483,6 +483,14 @@ Försök igen.</translation>
<translation>Uppdaterad</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Nod-fönster</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Öppna nodens konsol för felsökning och diagnostik</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>Av&amp;sändaradresser</translation>
</message>
@@ -491,6 +499,10 @@ Försök igen.</translation>
<translation>Mottaga&amp;radresser</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Öppna en bitcoin:-URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Öppna plånbok</translation>
</message>
@@ -784,7 +796,11 @@ Försök igen.</translation>
<source>Create wallet failed</source>
<translation>Plånboken kunde inte skapas</translation>
</message>
- </context>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Skapa plånboksvarning</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
<message>
@@ -804,6 +820,22 @@ Försök igen.</translation>
<translation>Kryptera plånbok</translation>
</message>
<message>
+ <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>Stäng av privata nycklar för denna plånbok. Plånböcker med privata nycklar avstängda kommer inte innehålla några privata nycklar alls, och kan inte innehålla vare sig en HD-seed eller importerade privata nycklar. Detta är idealt för plånböcker som endast ska granskas.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Stäng av privata nycklar</translation>
+ </message>
+ <message>
+ <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>Skapa en tom plånbok. Tomma plånböcker har från början inga privata nycklar eller skript. Privata nycklar och adresser kan importeras, eller en HD-seed kan väljas, vid ett senare tillfälle.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Skapa tom plånbok</translation>
+ </message>
+ <message>
<source>Create</source>
<translation>Skapa</translation>
</message>
@@ -920,6 +952,10 @@ Försök igen.</translation>
<translation>När du trycker OK kommer %1 att börja ladda ner och bearbeta den fullständiga %4-blockkedjan (%2 GB), med början vid de första transaktionerna %3 när %4 först lanserades.</translation>
</message>
<message>
+ <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>Att återställa detta alternativ påbörjar en omstart av nedladdningen av hela blockkedjan. Det går snabbare att ladda ner hela kedjan först, och gallra den senare. Detta alternativ stänger av vissa avancerade funktioner.</translation>
+ </message>
+ <message>
<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>Denna första synkronisering är väldigt krävande, och kan påvisa hårdvaruproblem hos din dator som tidigare inte visat sig. Varje gång du kör %1, kommer nerladdningen att fortsätta där den avslutades.</translation>
</message>
@@ -940,6 +976,10 @@ Försök igen.</translation>
<translation>Bitcoin</translation>
</message>
<message>
+ <source>Discard blocks after verification, except most recent %1 GB (prune)</source>
+ <translation>Släng block efter verifiering, förutom de senaste %1 GB (gallra).</translation>
+ </message>
+ <message>
<source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source>
<translation>Minst %1 GB data kommer att sparas i den här katalogen, och de växer över tiden.</translation>
</message>
@@ -1023,6 +1063,10 @@ Försök igen.</translation>
<translation>Dölj</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Okänd. Synkar huvuden (%1, %2%)...</translation>
</message>
@@ -1030,6 +1074,10 @@ Försök igen.</translation>
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Öppna bitcoin-URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -1041,6 +1089,10 @@ Försök igen.</translation>
<translation>Det gick inte att öppna plånboken</translation>
</message>
<message>
+ <source>Open wallet warning</source>
+ <translation>Öppna plånboksvarning.</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>Standardplånbok</translation>
</message>
@@ -1360,7 +1412,7 @@ Försök igen.</translation>
</message>
<message>
<source>Mined balance that has not yet matured</source>
- <translation>Genererat saldo som ännu inte har mognat</translation>
+ <translation>Grävt saldo som ännu inte har mognat</translation>
</message>
<message>
<source>Balances</source>
@@ -1392,7 +1444,7 @@ Försök igen.</translation>
</message>
<message>
<source>Mined balance in watch-only addresses that has not yet matured</source>
- <translation>Genererat saldo i granska-bara adresser som ännu inte har mognat</translation>
+ <translation>Grävt saldo i granska-bara adresser som ännu inte har mognat</translation>
</message>
<message>
<source>Current total balance in watch-only addresses</source>
@@ -1418,6 +1470,14 @@ Försök igen.</translation>
<translation>'bitcoin://' är inte en accepterad URI. Använd 'bitcoin:' istället.</translation>
</message>
<message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Som följd av utbredda säkerhetshål i BIP70, rekommenderas det starkt att en säljares instruktion för dig att byta plånbok ignoreras.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Om du får detta fel borde du be säljaren förse dig med en BIP21-kompatibel URI.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Ogiltig betalningsadress %1</translation>
</message>
@@ -1579,6 +1639,10 @@ Försök igen.</translation>
<translation>Fel vid skapande av QR-kod från URI.</translation>
</message>
<message>
+ <source>QR code support not available.</source>
+ <translation>Stöd för QR-kod är inte längre tillgängligt.</translation>
+ </message>
+ <message>
<source>Save QR Code</source>
<translation>Spara QR-kod</translation>
</message>
@@ -1722,6 +1786,10 @@ Försök igen.</translation>
<translation>Användaragent</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Nod-fönster</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Öppna felsökningsloggen %1 från aktuell datakatalog. Detta kan ta några sekunder för stora loggfiler.</translation>
</message>
@@ -1933,6 +2001,10 @@ Försök igen.</translation>
<translation>Ett valfritt belopp att begära. Lämna tomt eller ange noll för att inte begära ett specifikt belopp.</translation>
</message>
<message>
+ <source>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>
+ <translation>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.</translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
<translation>S&amp;kapa ny mottagaradress</translation>
</message>
@@ -2186,6 +2258,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Damm:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Dölj alternativ för transaktionsavgift</translation>
+ </message>
+ <message>
<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>När transaktionsvolymen är mindre än utrymmet i blocken kan både brytardatorer och relänoder kräva en minimiavgift. Det är okej att bara betala denna minimiavgift, men du ska vara medveten om att det kan leda till att en transaktion aldrig bekräftas så fort efterfrågan på bitcointransaktioner är större än vad nätverket kan hantera.</translation>
</message>
@@ -2254,18 +2330,34 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>%1 (%2 block)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Sk&amp;apa Osignerad</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation>från plånbok: '%1'</translation>
</message>
<message>
+ <source>%1 to '%2'</source>
+ <translation>%1 till '%2'</translation>
+ </message>
+ <message>
<source>%1 to %2</source>
<translation>%1 till %2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Vill du skissa denna transaktion?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Är du säker på att du vill skicka?</translation>
</message>
<message>
+ <source>Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Pleasetransaktion, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</translation>
+ </message>
+ <message>
<source>or</source>
<translation>eller</translation>
</message>
@@ -2290,10 +2382,30 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Totalt belopp</translation>
</message>
<message>
+ <source>To review recipient list click "Show Details..."</source>
+ <translation>För att gå igenom mottagarlistan, tryck "Visa Detaljer..."</translation>
+ </message>
+ <message>
<source>Confirm send coins</source>
<translation>Bekräfta att pengar ska skickas</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Bekräfta transaktionsförslag</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Kopiera PSBT till urklipp</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Skicka</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopierad</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Mottagarens adress är ogiltig. Kontrollera igen.</translation>
</message>
@@ -2389,6 +2501,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Ta bort denna post</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Beloppett att skicka i vald enhet</translation>
+ </message>
+ <message>
<source>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>
<translation>Avgiften dras från beloppet som skickas. Mottagaren kommer att ta emot mindre bitcoin än du angivit i beloppsfältet. Om flera mottagare väljs kommer avgiften att fördelas jämt.</translation>
</message>
@@ -2515,6 +2631,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Bitcoin-adress som meddelandet signerades med</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Signerat meddelande som ska verifieras</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verifiera meddelandet för att vara säker på att det signerades med angiven Bitcoin-adress</translation>
</message>
@@ -2547,6 +2667,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Upplåsningen av plånboken avbröts.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Inget fel</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Den privata nyckeln för den angivna adressen är inte tillgänglig.</translation>
</message>
@@ -2721,6 +2845,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Utmatningsindex</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Certifikatet verifierades inte)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Handlare</translation>
</message>
@@ -2832,7 +2960,7 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
</message>
<message>
<source>Mined</source>
- <translation>Genererade</translation>
+ <translation>Grävda</translation>
</message>
<message>
<source>watch-only</source>
@@ -2915,7 +3043,7 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
</message>
<message>
<source>Mined</source>
- <translation>Genererade</translation>
+ <translation>Grävda</translation>
</message>
<message>
<source>Other</source>
@@ -2959,7 +3087,7 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
</message>
<message>
<source>Copy full transaction details</source>
- <translation>Kopiera alla transaktionsdetaljerna</translation>
+ <translation>Kopiera alla transaktionsdetaljer</translation>
</message>
<message>
<source>Edit label</source>
@@ -3044,8 +3172,12 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Stäng plånboken</translation>
</message>
<message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Är du säker att du vill stänga plånboken &lt;i&gt;%1&lt;/i&gt;?</translation>
+ </message>
+ <message>
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
- <translation>Om plånboken är stängd under för lång tid kan hela kedjan behöva synkroniseras om, om gallring är aktiverad.</translation>
+ <translation>Om plånboken är stängd under för lång tid och gallring är aktiverad kan hela kedjan behöva synkroniseras på nytt.</translation>
</message>
</context>
<context>
@@ -3074,6 +3206,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Vill du öka avgiften?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Vill du skapa en transaktion med en avgiftsökning?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Aktuell avgift:</translation>
</message>
@@ -3090,6 +3226,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Bekräfta avgiftshöjning</translation>
</message>
<message>
+ <source>PSBT copied</source>
+ <translation>PSBT kopierad</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Kan ej signera transaktion.</translation>
</message>
@@ -3312,6 +3452,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Initieringschecken fallerade. %s stängs av.</translation>
</message>
<message>
+ <source>Invalid P2P permission: '%s'</source>
+ <translation>Ogiltigt P2P-tillstånd: '%s'</translation>
+ </message>
+ <message>
<source>Invalid amount for -%s=&lt;amount&gt;: '%s'</source>
<translation>Ogiltigt belopp för -%s=&lt;amount&gt;:'%s'</translation>
</message>
@@ -3328,6 +3472,14 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Den specificerade mappen för block "%s" existerar inte.</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Okänd adress-typ '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Okänd växel-typ '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Uppgraderar txindex-databasen</translation>
</message>
@@ -3377,7 +3529,7 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
</message>
<message>
<source>Unable to generate keys</source>
- <translation>Lyckas inte generera nycklar</translation>
+ <translation>Det gick inte att skapa nycklar</translation>
</message>
<message>
<source>Unsupported logging category %s=%s.</source>
@@ -3510,6 +3662,10 @@ Notera: Då avgiften beräknas per byte kommer en avgift på 50 satoshi tas ut f
<translation>Det gick inte att skapa ursprungliga nycklar</translation>
</message>
<message>
+ <source>Unknown -blockfilterindex value %s.</source>
+ <translation>Okänt värde för -blockfilterindex '%s'.</translation>
+ </message>
+ <message>
<source>Verifying wallet(s)...</source>
<translation>Verifierar plånbok(er)...</translation>
</message>
diff --git a/src/qt/locale/bitcoin_te.ts b/src/qt/locale/bitcoin_te.ts
index 6080d2e4dc..1e057d15bc 100644
--- a/src/qt/locale/bitcoin_te.ts
+++ b/src/qt/locale/bitcoin_te.ts
@@ -70,6 +70,10 @@
<translation>ఇవి మీరు పంపే చెల్లింపుల బిట్‌కాయిన్ చిరునామాలు. నాణేలు పంపే ముందు ప్రతిసారి అందుకునే చిరునామా మరియు చెల్లింపు మొత్తం సరిచూసుకోండి.</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>చెల్లింపులు స్వీకరించడానికి ఇవి మీ బిట్‌కాయిన్ చిరునామాలు. క్రొత్త చిరునామాలను సృష్టించడానికి స్వీకరించు ట్యాబ్‌లోని 'క్రొత్త స్వీకరించే చిరునామాను సృష్టించండి' బటన్‌ను ఉపయోగించండి.</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>చిరునామాను కాపీ చెయ్యండి</translation>
</message>
@@ -132,10 +136,38 @@
<translation>క్రొత్త సంకేతపదము మరలా ఇవ్వండి</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>సంకేతపదమును చూపించు</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>వాలెట్‌ను గుప్తీకరించండి</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>ఈ ఆపరేషన్‌కు వాలెట్‌ను అన్‌లాక్ చేయడానికి మీ వాలెట్ పాస్‌ఫ్రేజ్ అవసరం.</translation>
+ </message>
+ <message>
<source>Unlock wallet</source>
<translation>వాలెట్ అన్లాక్</translation>
</message>
<message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>ఈ ఆపరేషన్‌కు వాలెట్‌ను డీక్రిప్ట్ చేయడానికి మీ వాలెట్ పాస్‌ఫ్రేజ్ అవసరం.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>డీక్రిప్ట్ వాలెట్</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>పాస్‌ఫ్రేజ్‌ని మార్చండి</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>వాలెట్ గుప్తీకరణను నిర్ధారించండి</translation>
+ </message>
+ <message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
<translation>హెచ్చరిక: మీ జోలెని సంకేతపరిచి మీ సంకేతపదము కోల్పోతే, &lt;b&gt;మీ బిట్‌కాయిన్లు అన్నీ కోల్పోతారు&lt;/b&gt;</translation>
</message>
@@ -148,6 +180,26 @@
<translation>జోలె సంకేతపరబడింది</translation>
</message>
<message>
+ <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>వాలెట్ కోసం క్రొత్త పాస్‌ఫ్రేజ్‌ని నమోదు చేయండి.&lt;br/&gt; దయచేసి &lt;b&gt;పది లేదా అంతకంటే ఎక్కువ యాదృచ్ఛిక అక్షరాల&lt;/b&gt; పాస్‌ఫ్రేజ్‌ని లేదా &lt;b&gt;ఎనిమిది లేదా అంతకంటే ఎక్కువ పదాలను ఉపయోగించండి.&lt;/b&gt;</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Enter the old passphrase and new passphrase for the wallet.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>మీ వాలెట్‌ను గుప్తీకరించడం వల్ల మీ కంప్యూటర్‌కు హాని కలిగించే మాల్వేర్ దొంగిలించకుండా మీ బిట్‌కాయిన్‌లను పూర్తిగా రక్షించలేమని గుర్తుంచుకోండి.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>ఎన్క్రిప్ట్ చేయవలసిన వాలెట్</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>మీ వాలెట్ గుప్తీకరించబోతోంది.</translation>
+ </message>
+ <message>
<source>Wallet encryption failed</source>
<translation>జోలె సంకేతపరచడం విఫలమయ్యింది</translation>
</message>
diff --git a/src/qt/locale/bitcoin_th.ts b/src/qt/locale/bitcoin_th.ts
index e28c987893..4d82f7949b 100644
--- a/src/qt/locale/bitcoin_th.ts
+++ b/src/qt/locale/bitcoin_th.ts
@@ -70,6 +70,10 @@
<translation>ที่อยู่ Bitcoin ของคุณสำหรับการส่งการชำระเงิน โปรดตรวจสอบจำนวนเงินและที่อยู่รับก่อนที่จะส่งเหรียญ</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>นี่คือที่อยู่สำหรับการรับ Bitcoin ของคุณ กดปุ่ม ‘สร้างที่อยู่การรับใหม่’ ในแถบการรับ เพื่อสร้างที่อยู่การรับใหม่</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>คัดลอกที่อยู่</translation>
</message>
diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts
index ca8839fee3..722daadafe 100644
--- a/src/qt/locale/bitcoin_uk.ts
+++ b/src/qt/locale/bitcoin_uk.ts
@@ -1006,7 +1006,11 @@
<source>(of %n GB needed)</source>
<translation><numerusform>(в той час, як необхідно %n ГБ)</numerusform><numerusform>(в той час, як необхідно %n ГБ)</numerusform><numerusform>(в той час, як необхідно %n ГБ)</numerusform><numerusform>(в той час, як необхідно %n ГБ)</numerusform></translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n ГБ, необхідний для повного ланцюга)</numerusform><numerusform>(%n ГБ, необхідних для повного ланцюга)</numerusform><numerusform>(%n ГБ, необхідних для повного ланцюга)</numerusform><numerusform>(%n ГБ, необхідних для повного ланцюга)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -1781,6 +1785,14 @@
<translation>Синхронізовані Блоки</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Картована автономна система, що використовується для диверсифікації вибору вузлів.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>Картована Автономна Система</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>Клієнт користувача</translation>
</message>
diff --git a/src/qt/locale/bitcoin_ur.ts b/src/qt/locale/bitcoin_ur.ts
index b76551fc6f..d16c65ede4 100644
--- a/src/qt/locale/bitcoin_ur.ts
+++ b/src/qt/locale/bitcoin_ur.ts
@@ -297,6 +297,10 @@
<context>
<name>TransactionView</name>
<message>
+ <source>Other</source>
+ <translation>Other</translation>
+ </message>
+ <message>
<source>Comma separated file (*.csv)</source>
<translation>کاما سے جدا فائلیں (*.csv)</translation>
</message>
diff --git a/src/qt/locale/bitcoin_vi.ts b/src/qt/locale/bitcoin_vi.ts
index 1d5491137b..33a4da9561 100644
--- a/src/qt/locale/bitcoin_vi.ts
+++ b/src/qt/locale/bitcoin_vi.ts
@@ -3,7 +3,7 @@
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
- <translation>Phải chuột để sửa địa chỉ hoặc nhãn</translation>
+ <translation>Nhấn chuột phải để sửa địa chỉ hoặc nhãn</translation>
</message>
<message>
<source>Create a new address</source>
@@ -136,6 +136,10 @@
<translation>Lặp lại cụm mật khẩu mới</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>Hiện cụm từ mật khẩu</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>Ví mã hóa</translation>
</message>
@@ -176,6 +180,30 @@
<translation>Ví đã được mã hóa</translation>
</message>
<message>
+ <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>Nhập cụm từ mật khẩu mới cho ví điện tử. Hãy sử dụng cụm mật khẩu với mười hoặc nhiều hơn các ký tự ngẫu nhiên, hoặc nhiều hơn tám từ.</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>Nhập cụm mật khẩu cũ và mật khẩu mới cho ví.</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>Xin lưu ý rằng mật mã hóa ví của bạn không thể bảo vệ hoàn toàn bitcoin của bạn khỏi đánh cắp bởi các phẩn mềm gián điệp nhiễm vào máy tính của bạn.</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>Ví sẽ được mã hóa</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>Ví của bạn sẽ được mã hóa.</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>Ví của bạn đã được mã hóa.</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>QUAN TRỌNG: Bất cứ backup nào bạn từng làm trước đây từ ví của bạn nên được thay thế tạo mới, file mã hóa ví. Vì lý do bảo mật, các backup trước đây của các ví chưa mã hóa sẽ bị vô tác dụng ngay khi bạn bắt đầu sử dụng mới, ví đã được mã hóa.</translation>
</message>
@@ -298,6 +326,14 @@
<translation>Mở &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>Tạo ví...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>Tạo một ví mới</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>Ví tiền</translation>
</message>
@@ -446,6 +482,14 @@
<translation>Đã cập nhật</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Cửa sổ node</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>Mở dòng lệnh tìm và gỡ lỗi cho node</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;Các địa chỉ đang gửi</translation>
</message>
@@ -454,6 +498,10 @@
<translation>&amp;Các địa chỉ đang nhận</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>Mở một bitcoin: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>Mớ ví</translation>
</message>
@@ -739,10 +787,58 @@
</context>
<context>
<name>CreateWalletActivity</name>
- </context>
+ <message>
+ <source>Creating Wallet &lt;b&gt;%1&lt;/b&gt;...</source>
+ <translation>Đang tạo ví %1 ...</translation>
+ </message>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>Tạo ví thất bại</translation>
+ </message>
+ <message>
+ <source>Create wallet warning</source>
+ <translation>Cảnh báo khi tạo ví</translation>
+ </message>
+</context>
<context>
<name>CreateWalletDialog</name>
- </context>
+ <message>
+ <source>Create Wallet</source>
+ <translation>Tạo Ví</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>Tên Ví</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>Mật mã hóa ví. Ví sẽ được mật mã hóa với cụm mật khẩu của bạn.</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>Mật mã hóa ví</translation>
+ </message>
+ <message>
+ <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>Tắt các khóa cá nhân cho ví này. Các ví với khóa cá nhân tắt sẽ không có các khóa cá nhân và không thể có nhân HD hoặc nhập thêm khóa cá nhân. Việc này tốt cho các ví chỉ dùng để xem.</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>Vô hiệu hóa khóa cá nhân</translation>
+ </message>
+ <message>
+ <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>Tạo một ví trống. Ví trống không có các khóa cá nhân hay script ban đầu. Khóa cá nhân và địa chỉ có thể được nhập, hoặc một nhân HD có thể được thiết lập sau đó.</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>Tạo ví trống</translation>
+ </message>
+ <message>
+ <source>Create</source>
+ <translation>Tạo</translation>
+ </message>
+</context>
<context>
<name>EditAddressDialog</name>
<message>
@@ -855,6 +951,10 @@
<translation>Khi bạn click OK, %1 sẽ bắt đầu download và process the full %4 block chain (%2GB) starting with the earliest transactions in %3 when %4 initially launched.</translation>
</message>
<message>
+ <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>Đảo ngược lại thiết lập này yêu cầu download lại toàn bộ blockchain. Download toàn bộ blockchain trước và loại nó sau đó sẽ nhanh hơn. Vô hiệu hóa một số tính năng nâng cao.</translation>
+ </message>
+ <message>
<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>Đồng bộ hóa ban đầu này rất đòi hỏi, và có thể phơi bày các sự cố về phần cứng với máy tính của bạn trước đó đã không được chú ý. Mỗi khi bạn chạy %1, nó sẽ tiếp tục tải về nơi nó dừng lại.</translation>
</message>
@@ -906,7 +1006,11 @@
<source>(of %n GB needed)</source>
<translation><numerusform>(of %n GB cần thiết)</numerusform></translation>
</message>
- </context>
+ <message numerus="yes">
+ <source>(%n GB needed for full chain)</source>
+ <translation><numerusform>(%n GB cần cho toàn blockchain)</numerusform></translation>
+ </message>
+</context>
<context>
<name>ModalOverlay</name>
<message>
@@ -954,6 +1058,14 @@
<translation>Ẩn</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>Esc</translation>
+ </message>
+ <message>
+ <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>%1 đang được đồng bộ. Header và block sẽ được download từ các nốt lân cận và thẩm định tới khi đạt đỉnh của blockchain.</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>Không biết. Đang đồng bộ Headers (%1, %2%)...</translation>
</message>
@@ -961,6 +1073,10 @@
<context>
<name>OpenURIDialog</name>
<message>
+ <source>Open bitcoin URI</source>
+ <translation>Mở bitcoin URI</translation>
+ </message>
+ <message>
<source>URI:</source>
<translation>URI:</translation>
</message>
@@ -968,6 +1084,14 @@
<context>
<name>OpenWalletActivity</name>
<message>
+ <source>Open wallet failed</source>
+ <translation>Mở ví thất bại</translation>
+ </message>
+ <message>
+ <source>Open wallet warning</source>
+ <translation>Mở ví cảnh báo</translation>
+ </message>
+ <message>
<source>default wallet</source>
<translation>ví mặc định</translation>
</message>
@@ -1345,6 +1469,18 @@
<translation>'bitcoin://' không khả dụng URI. Dùng thay vì 'bitcoin:' .</translation>
</message>
<message>
+ <source>Cannot process payment request because BIP70 is not supported.</source>
+ <translation>Không thể tiến hần yêu cầu giao dịch vì BIP70 không được hỗ trợ.</translation>
+ </message>
+ <message>
+ <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source>
+ <translation>Do lỗ hổng bảo mật lan rộng của BIP70, bạn được khuyến cáo mạnh mẽ rằng bất kỳ hướng dẫn thương mại để chuyển ví đều bị bỏ qua.</translation>
+ </message>
+ <message>
+ <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source>
+ <translation>Nếu bạn nhận được lỗi này, bạn nên yêu cầu của hàng cung cấp một BIP21 tương thích URI.</translation>
+ </message>
+ <message>
<source>Invalid payment address %1</source>
<translation>Invalid payment address %1</translation>
</message>
@@ -1649,10 +1785,22 @@
<translation>Blocks đã được đồng bộ</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>Hệ thống tự động ánh xạ được sử dụng để đa dạng hóa lựa chọn ngang hàng.</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>AS đã được map</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>User đặc vụ</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>Cửa sổ node</translation>
+ </message>
+ <message>
<source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source>
<translation>Mở cái %1 debug log file từ danh mục dữ liệu hiện tại. Điều này cần vài giây cho large log files.</translation>
</message>
@@ -1864,6 +2012,14 @@
<translation>Một optional giá trị để request. Để lại đây khoảng trống hoặc zero để không request một giá trị xác định.</translation>
</message>
<message>
+ <source>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>
+ <translation>Một nhãn tùy chọn để liên kết với địa chỉ nhận mới (được bạn sử dụng để xác định hóa đơn). Nó cũng được đính kèm với yêu cầu thanh toán.</translation>
+ </message>
+ <message>
+ <source>An optional message that is attached to the payment request and may be displayed to the sender.</source>
+ <translation>Một thông báo tùy chọn được đính kèm với yêu cầu thanh toán và có thể được hiển thị cho người gửi.</translation>
+ </message>
+ <message>
<source>&amp;Create new receiving address</source>
<translation>&amp;Tạo địa chỉ nhận mới</translation>
</message>
@@ -1876,6 +2032,14 @@
<translation>Xóa</translation>
</message>
<message>
+ <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source>
+ <translation>Các địa chỉ segwit gốc (còn gọi là Bech32 hoặc BIP-173) sẽ giảm phí giao dịch của bạn sau này và bảo vệ tốt hơn trước các lỗi chính tả, nhưng ví cũ không hỗ trợ chúng. Khi không được chọn, một địa chỉ tương thích với ví cũ sẽ được tạo thay thế.</translation>
+ </message>
+ <message>
+ <source>Generate native segwit (Bech32) address</source>
+ <translation>Tạo địa chỉ segwit (Bech32) riêng</translation>
+ </message>
+ <message>
<source>Requested payments history</source>
<translation>Yêu cầu lịch sử giao dịch</translation>
</message>
@@ -2109,6 +2273,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Rác:</translation>
</message>
<message>
+ <source>Hide transaction fee settings</source>
+ <translation>Ẩn cài đặt phí giao dịch</translation>
+ </message>
+ <message>
<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>Khi có khối lượng giao dịch ít hơn chổ trống trong các khối, các nhà đào mỏ cũng như các nút chuyển tiếp có thể thực thi chỉ với một khoản phí tối thiểu. Chỉ trả khoản phí tối thiểu này là tốt, nhưng lưu ý rằng điều này có thể dẫn đến một giao dịch không bao giờ xác nhận một khi có nhu cầu giao dịch bitcoin nhiều hơn khả năng mạng có thể xử lý.</translation>
</message>
@@ -2177,6 +2345,14 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>%1 (%2 blocks)</translation>
</message>
<message>
+ <source>Cr&amp;eate Unsigned</source>
+ <translation>Cr&amp;eate không được ký</translation>
+ </message>
+ <message>
+ <source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
+ <translation>Tạo Giao dịch Bitcoin được ký một phần (PSBT) để sử dụng với các dạng như: ví ngoại tuyến %1 hoặc ví phần cứng tương thích PSBT.</translation>
+ </message>
+ <message>
<source> from wallet '%1'</source>
<translation>từ ví '%1'</translation>
</message>
@@ -2189,6 +2365,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>%1 đến%2</translation>
</message>
<message>
+ <source>Do you want to draft this transaction?</source>
+ <translation>Bạn có muốn tạo tạm thời dao dịch này?</translation>
+ </message>
+ <message>
<source>Are you sure you want to send?</source>
<translation>Bạn chắc chắn muốn gửi chứ?</translation>
</message>
@@ -2225,6 +2405,26 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Confirm gửi coins</translation>
</message>
<message>
+ <source>Confirm transaction proposal</source>
+ <translation>Xác nhận đề xuất giao dịch</translation>
+ </message>
+ <message>
+ <source>Copy PSBT to clipboard</source>
+ <translation>Sao chép PSBT vào khay nhớ tạp clipboard</translation>
+ </message>
+ <message>
+ <source>Send</source>
+ <translation>Gửi</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>Đã sao chép PSBT</translation>
+ </message>
+ <message>
+ <source>Watch-only balance:</source>
+ <translation>Số dư chỉ xem:</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>Địa chỉ người nhận address thì không valid. Kiểm tra lại đi.</translation>
</message>
@@ -2320,6 +2520,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Xóa bỏ entry này</translation>
</message>
<message>
+ <source>The amount to send in the selected unit</source>
+ <translation>Lượng tiền để gửi trong mỗi đơn vị đã chọn</translation>
+ </message>
+ <message>
<source>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>
<translation>The fee sẽ được khấu trừ từ số tiền đang gửi. Người nhận sẽ receive ít bitcoins hơn bạn gõ vào khoảng trống. Nếu nhiều người gửi được chọn, fee sẽ được chia đều.</translation>
</message>
@@ -2446,6 +2650,14 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>The Bitcoin address tin nhắn đã ký với</translation>
</message>
<message>
+ <source>The signed message to verify</source>
+ <translation>Tin nhắn đã được ký để xác nhận</translation>
+ </message>
+ <message>
+ <source>The signature given when the message was signed</source>
+ <translation>Chữ ký được cung cấp khi tin nhắn đã được ký</translation>
+ </message>
+ <message>
<source>Verify the message to ensure it was signed with the specified Bitcoin address</source>
<translation>Verify tin nhắn để chắc rằng nó đã được ký với xác định Bitcoin address</translation>
</message>
@@ -2478,6 +2690,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Wallet unlock đã được hủy.</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>Không lỗi</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>Private key cho address đã nhập thì không có sẵn.</translation>
</message>
@@ -2652,6 +2868,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Output index</translation>
</message>
<message>
+ <source> (Certificate was not verified)</source>
+ <translation>(Chứng chỉ chưa được thẩm định)</translation>
+ </message>
+ <message>
<source>Merchant</source>
<translation>Merchant</translation>
</message>
@@ -2975,6 +3195,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Đông ví</translation>
</message>
<message>
+ <source>Are you sure you wish to close the wallet &lt;i&gt;%1&lt;/i&gt;?</source>
+ <translation>Bạn có chắc bạn muốn đóng ví %1 ?</translation>
+ </message>
+ <message>
<source>Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.</source>
<translation>Đóng ví thời gian dài sẽ dẫn đến phải đồng bộ hóa lại cả chuỗi nếu cắt tỉa pruning được kích hoạt</translation>
</message>
@@ -3005,6 +3229,10 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Do you want to increase the fee?</translation>
</message>
<message>
+ <source>Do you want to draft a transaction with fee increase?</source>
+ <translation>Bạn có muốn tạo tạm thời một giao dịch với phí tăng?</translation>
+ </message>
+ <message>
<source>Current fee:</source>
<translation>Current fee:</translation>
</message>
@@ -3021,6 +3249,14 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Confirm fee bump</translation>
</message>
<message>
+ <source>Can't draft transaction.</source>
+ <translation>Không thể tạo tạm giao dịch.</translation>
+ </message>
+ <message>
+ <source>PSBT copied</source>
+ <translation>Đã sao chép PSBT</translation>
+ </message>
+ <message>
<source>Can't sign transaction.</source>
<translation>Can't sign transaction.</translation>
</message>
@@ -3187,6 +3423,14 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Corrupted block database detected</translation>
</message>
<message>
+ <source>Could not find asmap file %s</source>
+ <translation>Không tìm thấy tệp asmap %s</translation>
+ </message>
+ <message>
+ <source>Could not parse asmap file %s</source>
+ <translation>Không đọc được tệp asmap %s</translation>
+ </message>
+ <message>
<source>Do you want to rebuild the block database now?</source>
<translation>Do you want to rebuild the block database now?</translation>
</message>
@@ -3263,6 +3507,14 @@ Lưu ý: Vì phí được tính trên cơ sở mỗi byte, nên phí "100 satos
<translation>Thư mục chứa các khối được chỉ ra "%s" không tồn tại</translation>
</message>
<message>
+ <source>Unknown address type '%s'</source>
+ <translation>Không biết địa chỉ kiểu '%s'</translation>
+ </message>
+ <message>
+ <source>Unknown change type '%s'</source>
+ <translation>Không biết thay đổi kiểu '%s'</translation>
+ </message>
+ <message>
<source>Upgrading txindex database</source>
<translation>Đang nâng cấp dữ liệu txindex</translation>
</message>
diff --git a/src/qt/locale/bitcoin_zh.ts b/src/qt/locale/bitcoin_zh.ts
index 409cb3a2bb..52b2f24927 100644
--- a/src/qt/locale/bitcoin_zh.ts
+++ b/src/qt/locale/bitcoin_zh.ts
@@ -70,6 +70,10 @@
<translation>这些是你的比特币支付地址。在发送之前,一定要核对金额和接收地址。</translation>
</message>
<message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>这些是您的比特币接收地址。建议每个交易使用一个新的接收地址。</translation>
+ </message>
+ <message>
<source>&amp;Copy Address</source>
<translation>&amp;复制地址</translation>
</message>
@@ -132,6 +136,10 @@
<translation>重复新密码</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>显示密码</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>加密钱包</translation>
</message>
@@ -172,6 +180,30 @@
<translation>加密钱包</translation>
</message>
<message>
+ <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>输入钱包的新密码。&lt;br/&gt;密码中请使用&lt;b&gt;10个或更多随机字符&lt;/b&gt;,或&lt;b&gt;8个或更多的单词&lt;/b&gt;。</translation>
+ </message>
+ <message>
+ <source>Enter the old passphrase and new passphrase for the wallet.</source>
+ <translation>输入钱包的旧密码和新密码。</translation>
+ </message>
+ <message>
+ <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source>
+ <translation>记住,加密您的钱包并不能完全保护您的比特币不被您电脑中的恶意软件窃取。</translation>
+ </message>
+ <message>
+ <source>Wallet to be encrypted</source>
+ <translation>钱包即将被加密编码。</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>你的钱包即将被加密编码。</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>你的钱包已被加密编码。</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>重要提示:您以前对钱包文件所做的任何备份都应该替换为新的加密钱包文件。出于安全原因,一旦您开始使用新的加密钱包,以前未加密钱包文件备份将变得无用。</translation>
</message>
@@ -290,6 +322,14 @@
<translation>打开 &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>创建钱包</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>创建一个新的钱包</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>钱包:</translation>
</message>
@@ -393,6 +433,10 @@
<source>&amp;Command-line options</source>
<translation>&amp;命令行选项</translation>
</message>
+ <message numerus="yes">
+ <source>%n active connection(s) to Bitcoin network</source>
+ <translation><numerusform>%n 活跃的链接到比特币网络</numerusform></translation>
+ </message>
<message>
<source>Indexing blocks on disk...</source>
<translation>索引磁盘上的区块...</translation>
@@ -401,6 +445,10 @@
<source>Processing blocks on disk...</source>
<translation>处理磁盘上的区块...</translation>
</message>
+ <message numerus="yes">
+ <source>Processed %n block(s) of transaction history.</source>
+ <translation><numerusform>已处理 %n 的历史交易区块</numerusform></translation>
+ </message>
<message>
<source>%1 behind</source>
<translation>%1 落后</translation>
@@ -430,6 +478,14 @@
<translation>最新的</translation>
</message>
<message>
+ <source>Node window</source>
+ <translation>结点窗口</translation>
+ </message>
+ <message>
+ <source>Open node debugging and diagnostic console</source>
+ <translation>打开结点的调试和诊断控制台</translation>
+ </message>
+ <message>
<source>&amp;Sending addresses</source>
<translation>&amp;发送地址</translation>
</message>
@@ -438,6 +494,10 @@
<translation>&amp;接受地址</translation>
</message>
<message>
+ <source>Open a bitcoin: URI</source>
+ <translation>打开比特币: URI</translation>
+ </message>
+ <message>
<source>Open Wallet</source>
<translation>打开钱包</translation>
</message>
@@ -490,6 +550,10 @@
<translation>连接到节点...</translation>
</message>
<message>
+ <source>Catching up...</source>
+ <translation>跟进中</translation>
+ </message>
+ <message>
<source>Date: %1
</source>
<translation>日期:%1
@@ -609,6 +673,14 @@
<translation>总计</translation>
</message>
<message>
+ <source>Received with label</source>
+ <translation>收到,夹带标签</translation>
+ </message>
+ <message>
+ <source>Received with address</source>
+ <translation>收到,夹带地址</translation>
+ </message>
+ <message>
<source>Date</source>
<translation>日期</translation>
</message>
@@ -694,6 +766,38 @@
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <source>Create Wallet</source>
+ <translation>创建钱包</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>钱包名称</translation>
+ </message>
+ <message>
+ <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source>
+ <translation>编码钱包。钱包将会根据你选择的密码进行加密编码。</translation>
+ </message>
+ <message>
+ <source>Encrypt Wallet</source>
+ <translation>加密钱包</translation>
+ </message>
+ <message>
+ <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>禁用这个钱包的私钥。禁用私钥的钱包将没有私钥,也不能使用HD种子或者导入的私钥。对于仅供查看的钱包这是理想的设置。</translation>
+ </message>
+ <message>
+ <source>Disable Private Keys</source>
+ <translation>禁用私钥</translation>
+ </message>
+ <message>
+ <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>创建一个空白钱包。空白钱包没有起始的私钥和脚本。稍后可以倒入私钥和地址、设置HD种子。</translation>
+ </message>
+ <message>
+ <source>Make Blank Wallet</source>
+ <translation>创建空白钱包</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -961,6 +1065,10 @@
<context>
<name>RPCConsole</name>
<message>
+ <source>Node window</source>
+ <translation>结点窗口</translation>
+ </message>
+ <message>
<source>Last block time</source>
<translation>最后的区块时间</translation>
</message>
diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts
index 2c534d9162..8540c05b8d 100644
--- a/src/qt/locale/bitcoin_zh_CN.ts
+++ b/src/qt/locale/bitcoin_zh_CN.ts
@@ -1120,7 +1120,7 @@
</message>
<message>
<source>&amp;Start %1 on system login</source>
- <translation>系统登入时启动 %1 (%S)</translation>
+ <translation>系统登入时启动 %1 (&amp;S)</translation>
</message>
<message>
<source>Size of &amp;database cache</source>
@@ -1789,6 +1789,14 @@
<translation>已同步区块</translation>
</message>
<message>
+ <source>The mapped Autonomous System used for diversifying peer selection.</source>
+ <translation>映射到的自治系统,被用来多样化选择节点</translation>
+ </message>
+ <message>
+ <source>Mapped AS</source>
+ <translation>映射到的AS</translation>
+ </message>
+ <message>
<source>User Agent</source>
<translation>用户代理</translation>
</message>
@@ -2346,7 +2354,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source>
- <translation>创建一个“部分签名比特币交易”(PSBT),以用于像是离线%1钱包,或是兼容PSBT的硬件钱包这种用途。</translation>
+ <translation>创建一个“部分签名比特币交易”(PSBT),以用于诸如离线%1钱包,或是兼容PSBT的硬件钱包这类用途。</translation>
</message>
<message>
<source> from wallet '%1'</source>
diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts
index 27c2b0c71f..da1f92a461 100644
--- a/src/qt/locale/bitcoin_zh_TW.ts
+++ b/src/qt/locale/bitcoin_zh_TW.ts
@@ -136,6 +136,10 @@
<translation>重複新密碼</translation>
</message>
<message>
+ <source>Show passphrase</source>
+ <translation>顯示密碼</translation>
+ </message>
+ <message>
<source>Encrypt wallet</source>
<translation>加密錢包</translation>
</message>
@@ -180,6 +184,18 @@
<translation>請記得, 即使將錢包加密, 也不能完全防止因惡意軟體入侵, 而導致位元幣被偷.</translation>
</message>
<message>
+ <source>Wallet to be encrypted</source>
+ <translation>加密錢包</translation>
+ </message>
+ <message>
+ <source>Your wallet is about to be encrypted. </source>
+ <translation>你的錢包將被加密</translation>
+ </message>
+ <message>
+ <source>Your wallet is now encrypted. </source>
+ <translation>你的錢包現已被加密</translation>
+ </message>
+ <message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>重要須知: 請改用新造出來、有加密的錢包檔,來取代舊錢包檔的備份。為了安全起見,當你開始使用新的有加密的錢包後,舊錢包檔的備份就沒有用了。</translation>
</message>
@@ -302,6 +318,14 @@
<translation>開啓 &amp;URI...</translation>
</message>
<message>
+ <source>Create Wallet...</source>
+ <translation>新增錢包...</translation>
+ </message>
+ <message>
+ <source>Create a new wallet</source>
+ <translation>創建一個新錢包</translation>
+ </message>
+ <message>
<source>Wallet:</source>
<translation>錢包:</translation>
</message>
@@ -363,7 +387,7 @@
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
- <translation>把錢包中的密鑰加密</translation>
+ <translation>將錢包中之密鑰加密</translation>
</message>
<message>
<source>Sign messages with your Bitcoin addresses to prove you own them</source>
@@ -743,9 +767,21 @@
</context>
<context>
<name>CreateWalletActivity</name>
+ <message>
+ <source>Create wallet failed</source>
+ <translation>創建錢包失敗&lt;br&gt;</translation>
+ </message>
</context>
<context>
<name>CreateWalletDialog</name>
+ <message>
+ <source>Create Wallet</source>
+ <translation>新增錢包</translation>
+ </message>
+ <message>
+ <source>Wallet Name</source>
+ <translation>錢包名稱</translation>
+ </message>
</context>
<context>
<name>EditAddressDialog</name>
@@ -958,6 +994,10 @@
<translation>隱藏</translation>
</message>
<message>
+ <source>Esc</source>
+ <translation>離開鍵</translation>
+ </message>
+ <message>
<source>Unknown. Syncing Headers (%1, %2%)...</source>
<translation>不明。正在同步前導資料中(%1, %2%)...</translation>
</message>
@@ -2237,6 +2277,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>確認付款金額</translation>
</message>
<message>
+ <source>Send</source>
+ <translation>發</translation>
+ </message>
+ <message>
<source>The recipient address is not valid. Please recheck.</source>
<translation>收款位址無效。請再檢查看看。</translation>
</message>
@@ -2490,6 +2534,10 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<translation>錢包解鎖已取消。</translation>
</message>
<message>
+ <source>No error</source>
+ <translation>沒有錯誤</translation>
+ </message>
+ <message>
<source>Private key for the entered address is not available.</source>
<translation>沒有對應輸入位址的密鑰。</translation>
</message>
@@ -3458,7 +3506,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Unable to create the PID file '%s': %s</source>
- <translation>无法创建PID文件'%s': %s</translation>
+ <translation>無法創建PID文件'%s': %s</translation>
</message>
<message>
<source>Unable to generate initial keys</source>
@@ -3530,7 +3578,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
</message>
<message>
<source>Transaction must have at least one recipient</source>
- <translation>交易必須至少要有一個收款人</translation>
+ <translation>交易必須至少有一個收款人</translation>
</message>
<message>
<source>Unknown network specified in -onlynet: '%s'</source>
diff --git a/src/qt/locale/bitcoin_zu.ts b/src/qt/locale/bitcoin_zu.ts
new file mode 100644
index 0000000000..30e7bec4b9
--- /dev/null
+++ b/src/qt/locale/bitcoin_zu.ts
@@ -0,0 +1,291 @@
+<TS language="zu" version="2.1">
+<context>
+ <name>AddressBookPage</name>
+ <message>
+ <source>Right-click to edit address or label</source>
+ <translation>Qhafaza kwesokudla ukuze uhlele ikheli noma ilebula</translation>
+ </message>
+ <message>
+ <source>Create a new address</source>
+ <translation>Dala ikheli elisha</translation>
+ </message>
+ <message>
+ <source>Copy the currently selected address to the system clipboard</source>
+ <translation>Kopisha ikheli elikhethwe njengamanje ebhodini lokunameka lesistimu</translation>
+ </message>
+ <message>
+ <source>Delete the currently selected address from the list</source>
+ <translation>Susa ikheli elikhethwe njengamanje ohlwini</translation>
+ </message>
+ <message>
+ <source>Enter address or label to search</source>
+ <translation>Faka ikheli noma ilebula ukusesha</translation>
+ </message>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Khiphela idatha kuthebhu yamanje kufayela</translation>
+ </message>
+ <message>
+ <source>Choose the address to send coins to</source>
+ <translation>Khetha ikheli ozothumela kulo izinhlamvu zemali</translation>
+ </message>
+ <message>
+ <source>Choose the address to receive coins with</source>
+ <translation>Khetha ikheli ukuthola izinhlamvu zemali nge</translation>
+ </message>
+ <message>
+ <source>Sending addresses</source>
+ <translation>Kuthunyelwa amakheli</translation>
+ </message>
+ <message>
+ <source>Receiving addresses</source>
+ <translation>Ukuthola amakheli</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
+ <translation>Lawa amakheli akho e-Bitcoin okuthumela izinkokhelo. Njalo hlola inani nekheli elitholwayo ngaphambi kokuthumela izinhlamvu zemali.</translation>
+ </message>
+ <message>
+ <source>These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.</source>
+ <translation>Lawa amakheli akho e-Bitcoin athola izinkokhelo. Sebenzisa inkinobho ethi 'Dala ikheli elisha lokuthola' kuthebhu yokwamukela ukudala amakheli amasha.</translation>
+ </message>
+ <message>
+ <source>Export Address List</source>
+ <translation>Thumela Ikheli Langaphandle</translation>
+ </message>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Ifayela elihlukaniswe ngokhefana (* .csv)</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Ukuthekelisa kwehlulekile</translation>
+ </message>
+ </context>
+<context>
+ <name>AddressTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Ilebuli</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Ikheli</translation>
+ </message>
+ <message>
+ <source>(no label)</source>
+ <translation>(akukho ilebula)</translation>
+ </message>
+</context>
+<context>
+ <name>AskPassphraseDialog</name>
+ <message>
+ <source>Passphrase Dialog</source>
+ <translation>I-Passphrase Dialog</translation>
+ </message>
+ <message>
+ <source>Enter passphrase</source>
+ <translation>Faka umushwana wokungena</translation>
+ </message>
+ <message>
+ <source>New passphrase</source>
+ <translation>Umushwana omusha wokungena</translation>
+ </message>
+ <message>
+ <source>Repeat new passphrase</source>
+ <translation>Phinda umushwana omusha wokungena</translation>
+ </message>
+ <message>
+ <source>Show passphrase</source>
+ <translation>Khombisa umushwana wokungena</translation>
+ </message>
+ <message>
+ <source>Encrypt wallet</source>
+ <translation>Bethela isikhwama</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to unlock the wallet.</source>
+ <translation>Lokhu kusebenza kudinga umushwana wakho wokungena wesikhwama ukuvula isikhwama.</translation>
+ </message>
+ <message>
+ <source>Unlock wallet</source>
+ <translation>Vula isikhwama semali</translation>
+ </message>
+ <message>
+ <source>This operation needs your wallet passphrase to decrypt the wallet.</source>
+ <translation>Lo msebenzi udinga umushwana wakho wokungena wesikhwama ukukhipha isikhwama esikhwameni.</translation>
+ </message>
+ <message>
+ <source>Decrypt wallet</source>
+ <translation>Ukhiphe isikhwama semali</translation>
+ </message>
+ <message>
+ <source>Change passphrase</source>
+ <translation>Shintsha umushwana wokungena</translation>
+ </message>
+ <message>
+ <source>Confirm wallet encryption</source>
+ <translation>Qinisekisa ukubethelwa kwe-wallet</translation>
+ </message>
+ <message>
+ <source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR BITCOINS&lt;/b&gt;!</source>
+ <translation>Isexwayiso: Uma ubhala ngemfihlo isikhwama sakho futhi ulahlekelwe umushwana wakho wokungena, uzokwazi
+Lahla YONKE IBITCOIN YAKHO!</translation>
+ </message>
+ <message>
+ <source>Are you sure you wish to encrypt your wallet?</source>
+ <translation>Uqinisekile ukuthi ufisa ukubhala ngemfihlo isikhwama sakho?</translation>
+ </message>
+ <message>
+ <source>Wallet encrypted</source>
+ <translation>Kufakwe i-Wallet</translation>
+ </message>
+ </context>
+<context>
+ <name>BanTableModel</name>
+ </context>
+<context>
+ <name>BitcoinGUI</name>
+ </context>
+<context>
+ <name>CoinControlDialog</name>
+ </context>
+<context>
+ <name>CreateWalletActivity</name>
+ </context>
+<context>
+ <name>CreateWalletDialog</name>
+ </context>
+<context>
+ <name>EditAddressDialog</name>
+ </context>
+<context>
+ <name>FreespaceChecker</name>
+ </context>
+<context>
+ <name>HelpMessageDialog</name>
+ </context>
+<context>
+ <name>Intro</name>
+ </context>
+<context>
+ <name>ModalOverlay</name>
+ </context>
+<context>
+ <name>OpenURIDialog</name>
+ </context>
+<context>
+ <name>OpenWalletActivity</name>
+ </context>
+<context>
+ <name>OptionsDialog</name>
+ </context>
+<context>
+ <name>OverviewPage</name>
+ </context>
+<context>
+ <name>PaymentServer</name>
+ </context>
+<context>
+ <name>PeerTableModel</name>
+ </context>
+<context>
+ <name>QObject</name>
+ </context>
+<context>
+ <name>QRImageWidget</name>
+ </context>
+<context>
+ <name>RPCConsole</name>
+ </context>
+<context>
+ <name>ReceiveCoinsDialog</name>
+ </context>
+<context>
+ <name>ReceiveRequestDialog</name>
+ <message>
+ <source>Address</source>
+ <translation>Ikheli</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Ilebuli</translation>
+ </message>
+ </context>
+<context>
+ <name>RecentRequestsTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Ilebuli</translation>
+ </message>
+ </context>
+<context>
+ <name>SendCoinsDialog</name>
+ </context>
+<context>
+ <name>SendCoinsEntry</name>
+ </context>
+<context>
+ <name>ShutdownWindow</name>
+ </context>
+<context>
+ <name>SignVerifyMessageDialog</name>
+ </context>
+<context>
+ <name>TrafficGraphWidget</name>
+ </context>
+<context>
+ <name>TransactionDesc</name>
+ </context>
+<context>
+ <name>TransactionDescDialog</name>
+ </context>
+<context>
+ <name>TransactionTableModel</name>
+ <message>
+ <source>Label</source>
+ <translation>Ilebuli</translation>
+ </message>
+ </context>
+<context>
+ <name>TransactionView</name>
+ <message>
+ <source>Comma separated file (*.csv)</source>
+ <translation>Ifayela elihlukaniswe ngokhefana (* .csv)</translation>
+ </message>
+ <message>
+ <source>Label</source>
+ <translation>Ilebuli</translation>
+ </message>
+ <message>
+ <source>Address</source>
+ <translation>Ikheli</translation>
+ </message>
+ <message>
+ <source>Exporting Failed</source>
+ <translation>Ukuthekelisa kwehlulekile</translation>
+ </message>
+ </context>
+<context>
+ <name>UnitDisplayStatusBarControl</name>
+ </context>
+<context>
+ <name>WalletController</name>
+ </context>
+<context>
+ <name>WalletFrame</name>
+ </context>
+<context>
+ <name>WalletModel</name>
+ </context>
+<context>
+ <name>WalletView</name>
+ <message>
+ <source>Export the data in the current tab to a file</source>
+ <translation>Khipha idatha kuthebhu yamanje kufayela</translation>
+ </message>
+ </context>
+<context>
+ <name>bitcoin-core</name>
+ </context>
+</TS> \ No newline at end of file
diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp
index 3a251e0573..b1081f6aee 100644
--- a/src/qt/networkstyle.cpp
+++ b/src/qt/networkstyle.cpp
@@ -19,7 +19,8 @@ static const struct {
} network_styles[] = {
{"main", QAPP_APP_NAME_DEFAULT, 0, 0},
{"test", QAPP_APP_NAME_TESTNET, 70, 30},
- {"regtest", QAPP_APP_NAME_REGTEST, 160, 30}
+ {"signet", QAPP_APP_NAME_SIGNET, 35, 15},
+ {"regtest", QAPP_APP_NAME_REGTEST, 160, 30},
};
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 58a7591c95..7e089b4f95 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -27,8 +27,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
- QAbstractListModel(parent), m_node(node)
+OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent)
{
Init(resetSettings);
}
@@ -97,12 +97,12 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
- if (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
addOverriddenOption("-dbcache");
if (!settings.contains("nThreadsScriptVerif"))
settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
- if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
@@ -112,19 +112,19 @@ void OptionsModel::Init(bool resetSettings)
#ifdef ENABLE_WALLET
if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true);
- if (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
+ if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
#endif
// Network
if (!settings.contains("fUseUPnP"))
settings.setValue("fUseUPnP", DEFAULT_UPNP);
- if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
+ if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN);
- if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
+ if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
if (!settings.contains("fUseProxy"))
@@ -132,7 +132,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrProxy"))
settings.setValue("addrProxy", GetDefaultProxyAddress());
// Only try to set -proxy, if user has enabled fUseProxy
- if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
+ if ((settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())))
addOverriddenOption("-proxy");
else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
addOverriddenOption("-proxy");
@@ -142,7 +142,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrSeparateProxyTor"))
settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
// Only try to set -onion, if user has enabled fUseSeparateProxyTor
- if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
+ if ((settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())))
addOverriddenOption("-onion");
else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
addOverriddenOption("-onion");
@@ -150,7 +150,7 @@ void OptionsModel::Init(bool resetSettings)
// Display
if (!settings.contains("language"))
settings.setValue("language", "");
- if (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
addOverriddenOption("-lang");
language = settings.value("language").toString();
@@ -244,10 +244,10 @@ void OptionsModel::SetPruneEnabled(bool prune, bool force)
const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
std::string prune_val = prune ? ToString(prune_target_mib) : "0";
if (force) {
- m_node.forceSetArg("-prune", prune_val);
+ gArgs.ForceSetArg("-prune", prune_val);
return;
}
- if (!m_node.softSetArg("-prune", prune_val)) {
+ if (!gArgs.SoftSetArg("-prune", prune_val)) {
addOverriddenOption("-prune");
}
}
@@ -353,7 +353,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- m_node.mapPort(value.toBool());
+ node().mapPort(value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 14fdf9046e..3d9e7bbb80 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -11,6 +11,8 @@
#include <QAbstractListModel>
+#include <assert.h>
+
namespace interfaces {
class Node;
}
@@ -39,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -92,10 +94,11 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { return m_node; }
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
private:
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
/* Qt-only settings */
bool fHideTrayIcon;
bool fMinimizeToTray;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index a1da85bda7..6c2db52f63 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -74,7 +74,7 @@ static QSet<QString> savedPaymentRequests;
// Warning: ipcSendCommandLine() is called early in init,
// so don't use "Q_EMIT message()", but "QMessageBox::"!
//
-void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char* argv[])
+void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
@@ -94,14 +94,14 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{
- auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
+ auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::MAIN);
+ SelectParams(CBaseChainParams::MAIN);
} else {
- tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
+ tempChainParams = CreateChainParams(gArgs, CBaseChainParams::TESTNET);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::TESTNET);
+ SelectParams(CBaseChainParams::TESTNET);
}
}
}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 154f4a7ea6..eaf2bafe59 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -61,7 +61,7 @@ class PaymentServer : public QObject
public:
// Parse URIs on command line
// Returns false on error
- static void ipcParseCommandLine(interfaces::Node& node, int argc, char *argv[]);
+ static void ipcParseCommandLine(int argc, char *argv[]);
// Returns true if there were URIs on the command line
// which were successfully sent to an already-running
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index c816e1f8ed..52f1e60957 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -9,6 +9,7 @@
#include <QApplication>
#include <QClipboard>
#include <QDrag>
+#include <QFontDatabase>
#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
@@ -64,26 +65,28 @@ bool QRImageWidget::setQR(const QString& data, const QString& text)
}
QRcode_free(code);
- QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 20), QImage::Format_RGB32);
+ const int qr_image_size = QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 2 * QR_IMAGE_MARGIN);
+ QImage qrAddrImage(qr_image_size, qr_image_size, QImage::Format_RGB32);
qrAddrImage.fill(0xffffff);
- QPainter painter(&qrAddrImage);
- painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
+ {
+ QPainter painter(&qrAddrImage);
+ painter.drawImage(QR_IMAGE_MARGIN, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
- if (!text.isEmpty()) {
- QFont font = GUIUtil::fixedPitchFont();
- font.setStyleStrategy(QFont::NoAntialias);
- QRect paddedRect = qrAddrImage.rect();
+ if (!text.isEmpty()) {
+ QRect paddedRect = qrAddrImage.rect();
+ paddedRect.setHeight(QR_IMAGE_SIZE + QR_IMAGE_TEXT_MARGIN);
- // calculate ideal font size
- qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 20, text, font);
- font.setPointSizeF(font_size);
+ QFont font = GUIUtil::fixedPitchFont();
+ font.setStretch(QFont::SemiCondensed);
+ font.setLetterSpacing(QFont::AbsoluteSpacing, 1);
+ const qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 2 * QR_IMAGE_TEXT_MARGIN, text, font);
+ font.setPointSizeF(font_size);
- painter.setFont(font);
- paddedRect.setHeight(QR_IMAGE_SIZE+12);
- painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, text);
+ painter.setFont(font);
+ painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text);
+ }
}
- painter.end();
setPixmap(QPixmap::fromImage(qrAddrImage));
return true;
diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h
index cca598c2ce..a031bd7632 100644
--- a/src/qt/qrimagewidget.h
+++ b/src/qt/qrimagewidget.h
@@ -12,7 +12,9 @@
static const int MAX_URI_LENGTH = 255;
/* Size of exported QR Code image */
-static const int QR_IMAGE_SIZE = 300;
+static constexpr int QR_IMAGE_SIZE = 300;
+static constexpr int QR_IMAGE_TEXT_MARGIN = 10;
+static constexpr int QR_IMAGE_MARGIN = 2 * QR_IMAGE_TEXT_MARGIN;
QT_BEGIN_NAMESPACE
class QMenu;
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 5debded4ea..d374d610ee 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -242,22 +242,6 @@ void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
}
-void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
-{
- if (event->key() == Qt::Key_Return)
- {
- // press return -> submit form
- if (ui->reqLabel->hasFocus() || ui->reqAmount->hasFocus() || ui->reqMessage->hasFocus())
- {
- event->ignore();
- on_receiveButton_clicked();
- return;
- }
- }
-
- this->QDialog::keyPressEvent(event);
-}
-
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 2f48cd58f0..27455e2906 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -49,9 +49,6 @@ public Q_SLOTS:
void reject() override;
void accept() override;
-protected:
- virtual void keyPressEvent(QKeyEvent *event) override;
-
private:
Ui::ReceiveCoinsDialog *ui;
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 821a337a62..4c5601242e 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -20,6 +20,7 @@
#include <rpc/client.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/threadnames.h>
#include <univalue.h>
@@ -556,7 +557,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
return QWidget::eventFilter(obj, event);
}
-void RPCConsole::setClientModel(ClientModel *model)
+void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_t bestblock_date, double verification_progress)
{
clientModel = model;
@@ -576,13 +577,13 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
connect(model, &ClientModel::numConnectionsChanged, this, &RPCConsole::setNumConnections);
- interfaces::Node& node = clientModel->node();
- setNumBlocks(node.getNumBlocks(), QDateTime::fromTime_t(node.getLastBlockTime()), node.getVerificationProgress(), false);
+ setNumBlocks(bestblock_height, QDateTime::fromTime_t(bestblock_date), verification_progress, false);
connect(model, &ClientModel::numBlocksChanged, this, &RPCConsole::setNumBlocks);
updateNetworkState();
connect(model, &ClientModel::networkActiveChanged, this, &RPCConsole::setNetworkActive);
+ interfaces::Node& node = clientModel->node();
updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
connect(model, &ClientModel::bytesChanged, this, &RPCConsole::updateTrafficStats);
@@ -978,6 +979,9 @@ void RPCConsole::startExecutor()
// Default implementation of QThread::run() simply spins up an event loop in the thread,
// which is what we want.
thread.start();
+ QTimer::singleShot(0, executor, []() {
+ util::ThreadRename("qt-rpcconsole");
+ });
}
void RPCConsole::on_tabWidget_currentChanged(int index)
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index de8e37cca2..280c5bd71a 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -46,7 +46,7 @@ public:
return RPCParseCommandLine(&node, strResult, strCommand, true, pstrFilteredOut, wallet_model);
}
- void setClientModel(ClientModel *model);
+ void setClientModel(ClientModel *model = nullptr, int bestblock_height = 0, int64_t bestblock_date = 0, double verification_progress = 0.0);
void addWallet(WalletModel * const walletModel);
void removeWallet(WalletModel* const walletModel);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 97fb88d71c..50a1ea6936 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -28,6 +28,8 @@
#include <wallet/fees.h>
#include <wallet/wallet.h>
+#include <validation.h>
+
#include <QFontMetrics>
#include <QScrollBar>
#include <QSettings>
@@ -134,7 +136,7 @@ void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
this->clientModel = _clientModel;
if (_clientModel) {
- connect(_clientModel, &ClientModel::numBlocksChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
+ connect(_clientModel, &ClientModel::numBlocksChanged, this, &SendCoinsDialog::updateNumberOfBlocks);
}
}
@@ -744,6 +746,12 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
ctrl.fAllowWatchOnly = model->wallet().privateKeysDisabled();
}
+void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
+ if (sync_state == SynchronizationState::POST_INIT) {
+ updateSmartFeeLabel();
+ }
+}
+
void SendCoinsDialog::updateSmartFeeLabel()
{
if(!model || !model->getOptionsModel())
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 6961aa7821..8519f1f65b 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -17,6 +17,7 @@ class ClientModel;
class PlatformStyle;
class SendCoinsEntry;
class SendCoinsRecipient;
+enum class SynchronizationState;
namespace Ui {
class SendCoinsDialog;
@@ -98,6 +99,7 @@ private Q_SLOTS:
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
void updateFeeSectionControls();
+ void updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 6e6b2b8466..f00f086d1e 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -14,6 +14,7 @@
#include <interfaces/wallet.h>
#include <qt/guiutil.h>
#include <qt/networkstyle.h>
+#include <qt/walletmodel.h>
#include <util/system.h>
#include <util/translation.h>
@@ -24,8 +25,8 @@
#include <QScreen>
-SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
- QWidget(nullptr, f), curAlignment(0), m_node(node)
+SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) :
+ QWidget(nullptr, f), curAlignment(0)
{
// set reference point, paddings
int paddingRight = 50;
@@ -124,7 +125,6 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
setFixedSize(r.size());
move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
- subscribeToCoreSignals();
installEventFilter(this);
GUIUtil::handleCloseWindowShortcut(this);
@@ -132,14 +132,28 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
SplashScreen::~SplashScreen()
{
- unsubscribeFromCoreSignals();
+ if (m_node) unsubscribeFromCoreSignals();
+}
+
+void SplashScreen::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ subscribeToCoreSignals();
+ if (m_shutdown) m_node->startShutdown();
+}
+
+void SplashScreen::shutdown()
+{
+ m_shutdown = true;
+ if (m_node) m_node->startShutdown();
}
bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
if (ev->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (keyEvent->key() == Qt::Key_Q) {
- m_node.startShutdown();
+ shutdown();
}
}
return QObject::eventFilter(obj, ev);
@@ -172,21 +186,22 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
: _("press q to shutdown").translated) +
strprintf("\n%d", nProgress) + "%");
}
-#ifdef ENABLE_WALLET
-void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
-{
- m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
- m_connected_wallets.emplace_back(std::move(wallet));
-}
-#endif
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
- m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_handler_init_message = m_node->handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
+ m_handler_show_progress = m_node->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+}
+
+void SplashScreen::handleLoadWallet()
+{
#ifdef ENABLE_WALLET
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
+ if (!WalletModel::isWalletEnabled()) return;
+ m_handler_load_wallet = m_node->walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
+ m_connected_wallets.emplace_back(std::move(wallet));
+ });
#endif
}
@@ -221,6 +236,6 @@ void SplashScreen::paintEvent(QPaintEvent *event)
void SplashScreen::closeEvent(QCloseEvent *event)
{
- m_node.startShutdown(); // allows an "emergency" shutdown during startup
+ shutdown(); // allows an "emergency" shutdown during startup
event->ignore();
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 3158524117..a0cd677d3d 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -28,8 +28,9 @@ class SplashScreen : public QWidget
Q_OBJECT
public:
- explicit SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle);
+ explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle);
~SplashScreen();
+ void setNode(interfaces::Node& node);
protected:
void paintEvent(QPaintEvent *event) override;
@@ -42,6 +43,9 @@ public Q_SLOTS:
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
+ /** Handle wallet load notifications. */
+ void handleLoadWallet();
+
protected:
bool eventFilter(QObject * obj, QEvent * ev) override;
@@ -50,15 +54,16 @@ private:
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
- /** Connect wallet signals to splash screen */
- void ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet);
+ /** Initiate shutdown */
+ void shutdown();
QPixmap pixmap;
QString curMessage;
QColor curColor;
int curAlignment;
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
+ bool m_shutdown = false;
std::unique_ptr<interfaces::Handler> m_handler_init_message;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 035c8196bc..35fcb2b0ca 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -61,7 +61,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
bool firstRun;
wallet->LoadWallet(firstRun);
@@ -108,11 +108,11 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index b880a99baf..8dffd2f59f 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -67,6 +67,7 @@ void AppTests::appTests()
return GetDataDir() / "blocks";
}());
+ qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().NetworkIDString()));
@@ -83,8 +84,11 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- UnloadBlockIndex(/* mempool */ nullptr);
- WITH_LOCK(::cs_main, g_chainman.Reset());
+ {
+ LOCK(cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
+ g_chainman.Reset();
+ }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 031913bd02..86356b43c8 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -68,11 +68,11 @@ int main(int argc, char* argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
- BitcoinApplication app(*node);
+ BitcoinApplication app;
+ app.setNode(*node);
app.setApplicationName("Bitcoin-Qt-test");
- node->setupServerArgs(); // Make gArgs available in the NodeContext
- node->context()->args->ClearArgs(); // Clear added args again
+ app.node().context()->args = &gArgs; // Make gArgs available in the NodeContext
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
@@ -81,7 +81,7 @@ int main(int argc, char* argv[])
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
}
- RPCNestedTests test3(*node);
+ RPCNestedTests test3(app.node());
if (QTest::qExec(&test3) != 0) {
fInvalid = true;
}
@@ -90,11 +90,11 @@ int main(int argc, char* argv[])
fInvalid = true;
}
#ifdef ENABLE_WALLET
- WalletTests test5(*node);
+ WalletTests test5(app.node());
if (QTest::qExec(&test5) != 0) {
fInvalid = true;
}
- AddressBookTests test6(*node);
+ AddressBookTests test6(app.node());
if (QTest::qExec(&test6) != 0) {
fInvalid = true;
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 475fd589af..d6d2d0e3df 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -139,7 +139,7 @@ void TestGUI(interfaces::Node& node)
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -163,11 +163,11 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 01922cf996..b7f85446f4 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -28,7 +28,7 @@
#include <QVBoxLayout>
/** "Help message" or "About" dialog box */
-HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about) :
+HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
QDialog(parent),
ui(new Ui::HelpMessageDialog)
{
diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h
index 425b468f40..d2a5d5f67f 100644
--- a/src/qt/utilitydialog.h
+++ b/src/qt/utilitydialog.h
@@ -12,10 +12,6 @@ QT_BEGIN_NAMESPACE
class QMainWindow;
QT_END_NAMESPACE
-namespace interfaces {
- class Node;
-}
-
namespace Ui {
class HelpMessageDialog;
}
@@ -26,7 +22,7 @@ class HelpMessageDialog : public QDialog
Q_OBJECT
public:
- explicit HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about);
+ explicit HelpMessageDialog(QWidget *parent, bool about);
~HelpMessageDialog();
void printToConsole();
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 3aed98e0e8..d9e0274d01 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -14,6 +14,7 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
+#include <util/threadnames.h>
#include <util/translation.h>
#include <wallet/wallet.h>
@@ -35,16 +36,19 @@ WalletController::WalletController(ClientModel& client_model, const PlatformStyl
, m_platform_style(platform_style)
, m_options_model(client_model.getOptionsModel())
{
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_handler_load_wallet = m_node.walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
getOrCreateWallet(std::move(wallet));
});
- for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.getWallets()) {
+ for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.walletClient().getWallets()) {
getOrCreateWallet(std::move(wallet));
}
m_activity_worker->moveToThread(m_activity_thread);
m_activity_thread->start();
+ QTimer::singleShot(0, m_activity_worker, []() {
+ util::ThreadRename("qt-walletctrl");
+ });
}
// Not using the default destructor because not all member types definitions are
@@ -66,7 +70,7 @@ std::map<std::string, bool> WalletController::listWalletDir() const
{
QMutexLocker locker(&m_mutex);
std::map<std::string, bool> wallets;
- for (const std::string& name : m_node.listWalletDir()) {
+ for (const std::string& name : m_node.walletClient().listWalletDir()) {
wallets[name] = false;
}
for (WalletModel* wallet_model : m_wallets) {
@@ -249,10 +253,9 @@ void CreateWalletActivity::createWallet()
}
QTimer::singleShot(500, worker(), [this, name, flags] {
- WalletCreationStatus status;
- std::unique_ptr<interfaces::Wallet> wallet = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, status);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
- if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
+ if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
QTimer::singleShot(500, this, &CreateWalletActivity::finish);
});
@@ -321,7 +324,7 @@ void OpenWalletActivity::open(const std::string& path)
showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
QTimer::singleShot(0, worker(), [this, path] {
- std::unique_ptr<interfaces::Wallet> wallet = node().loadWallet(path, m_error_message, m_warning_message);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index ec56f2755f..f16761d6b2 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <qt/createwalletdialog.h>
+#include <qt/walletcontroller.h>
#include <qt/walletframe.h>
#include <qt/walletmodel.h>
@@ -10,8 +12,11 @@
#include <cassert>
+#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
+#include <QPushButton>
+#include <QVBoxLayout>
WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) :
QFrame(_gui),
@@ -25,9 +30,25 @@ WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui)
walletFrameLayout->setContentsMargins(0,0,0,0);
walletFrameLayout->addWidget(walletStack);
- QLabel *noWallet = new QLabel(tr("No wallet has been loaded."));
+ // hbox for no wallet
+ QGroupBox* no_wallet_group = new QGroupBox(walletStack);
+ QVBoxLayout* no_wallet_layout = new QVBoxLayout(no_wallet_group);
+
+ QLabel *noWallet = new QLabel(tr("No wallet has been loaded.\nGo to File > Open Wallet to load a wallet.\n- OR -"));
noWallet->setAlignment(Qt::AlignCenter);
- walletStack->addWidget(noWallet);
+ no_wallet_layout->addWidget(noWallet, 0, Qt::AlignHCenter | Qt::AlignBottom);
+
+ // 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();
+ });
+ no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
+ no_wallet_group->setLayout(no_wallet_layout);
+
+ walletStack->addWidget(no_wallet_group);
}
WalletFrame::~WalletFrame()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e374dd191c..6a3f903206 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -413,7 +413,7 @@ void WalletModel::subscribeToCoreSignals()
m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1));
- m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(boost::bind(NotifyCanGetAddressesChanged, this));
+ m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(std::bind(NotifyCanGetAddressesChanged, this));
}
void WalletModel::unsubscribeFromCoreSignals()
@@ -581,7 +581,7 @@ QString WalletModel::getDisplayName() const
bool WalletModel::isMultiwallet()
{
- return m_node.getWallets().size() > 1;
+ return m_node.walletClient().getWallets().size() > 1;
}
void WalletModel::refresh(bool pk_hash_only)
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index 073d82b491..07122b7f6d 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -67,7 +67,8 @@ void RandAddSeedPerfmon(CSHA512& hasher)
#ifdef WIN32
// Seed with the entire set of perfmon data
- // This can take up to 2 seconds, so only do it every 10 minutes
+ // This can take up to 2 seconds, so only do it every 10 minutes.
+ // Initialize last_perfmon to 0 seconds, we don't skip the first call.
static std::atomic<std::chrono::seconds> last_perfmon{std::chrono::seconds{0}};
auto last_time = last_perfmon.load();
auto current_time = GetTime<std::chrono::seconds>();
@@ -83,7 +84,7 @@ void RandAddSeedPerfmon(CSHA512& hasher)
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
break;
- vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
+ vData.resize(std::min((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
}
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS) {
diff --git a/src/rest.cpp b/src/rest.cpp
index 7130625d5c..949cc9d84a 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -102,7 +102,7 @@ static CTxMemPool* GetMemPool(const util::Ref& context, HTTPRequest* req)
RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
return nullptr;
}
- return node->mempool;
+ return node->mempool.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
@@ -303,7 +303,7 @@ static bool rest_block_notxdetails(const util::Ref& context, HTTPRequest* req, c
}
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
-UniValue getblockchaininfo(const JSONRPCRequest& request);
+RPCHelpMan getblockchaininfo();
static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
@@ -316,7 +316,7 @@ static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std
case RetFormat::JSON: {
JSONRPCRequest jsonRequest(context);
jsonRequest.params = UniValue(UniValue::VARR);
- UniValue chainInfoObject = getblockchaininfo(jsonRequest);
+ UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
std::string strJSON = chainInfoObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
@@ -393,7 +393,7 @@ static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::strin
const NodeContext* const node = GetNodeContext(context, req);
if (!node) return false;
uint256 hashBlock = uint256();
- const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool, hash, Params().GetConsensus(), hashBlock);
+ const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool.get(), hash, Params().GetConsensus(), hashBlock);
if (!tx) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index f27373b57c..a162c1ee70 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -189,9 +189,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
return result;
}
-static UniValue getblockcount(const JSONRPCRequest& request)
+static RPCHelpMan getblockcount()
{
- RPCHelpMan{"getblockcount",
+ return RPCHelpMan{"getblockcount",
"\nReturns the height of the most-work fully-validated chain.\n"
"The genesis block has height 0.\n",
{},
@@ -201,15 +201,17 @@ static UniValue getblockcount(const JSONRPCRequest& request)
HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return ::ChainActive().Height();
+},
+ };
}
-static UniValue getbestblockhash(const JSONRPCRequest& request)
+static RPCHelpMan getbestblockhash()
{
- RPCHelpMan{"getbestblockhash",
+ return RPCHelpMan{"getbestblockhash",
"\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
RPCResult{
@@ -218,10 +220,12 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return ::ChainActive().Tip()->GetBlockHash().GetHex();
+},
+ };
}
void RPCNotifyBlockChange(const CBlockIndex* pindex)
@@ -234,9 +238,9 @@ void RPCNotifyBlockChange(const CBlockIndex* pindex)
cond_blockchange.notify_all();
}
-static UniValue waitfornewblock(const JSONRPCRequest& request)
+static RPCHelpMan waitfornewblock()
{
- RPCHelpMan{"waitfornewblock",
+ return RPCHelpMan{"waitfornewblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
@@ -252,7 +256,8 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
HelpExampleCli("waitfornewblock", "1000")
+ HelpExampleRpc("waitfornewblock", "1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
if (!request.params[0].isNull())
timeout = request.params[0].get_int();
@@ -271,11 +276,13 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue waitforblock(const JSONRPCRequest& request)
+static RPCHelpMan waitforblock()
{
- RPCHelpMan{"waitforblock",
+ return RPCHelpMan{"waitforblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
@@ -292,7 +299,8 @@ static UniValue waitforblock(const JSONRPCRequest& request)
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
+ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -314,11 +322,13 @@ static UniValue waitforblock(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue waitforblockheight(const JSONRPCRequest& request)
+static RPCHelpMan waitforblockheight()
{
- RPCHelpMan{"waitforblockheight",
+ return RPCHelpMan{"waitforblockheight",
"\nWaits for (at least) block height and returns the height and hash\n"
"of the current tip.\n"
"\nReturns the current block on timeout or exit.\n",
@@ -336,7 +346,8 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
HelpExampleCli("waitforblockheight", "100 1000")
+ HelpExampleRpc("waitforblockheight", "100, 1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
int height = request.params[0].get_int();
@@ -357,11 +368,13 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
+static RPCHelpMan syncwithvalidationinterfacequeue()
{
- RPCHelpMan{"syncwithvalidationinterfacequeue",
+ return RPCHelpMan{"syncwithvalidationinterfacequeue",
"\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -369,15 +382,17 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
HelpExampleCli("syncwithvalidationinterfacequeue","")
+ HelpExampleRpc("syncwithvalidationinterfacequeue","")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
SyncWithValidationInterfaceQueue();
return NullUniValue;
+},
+ };
}
-static UniValue getdifficulty(const JSONRPCRequest& request)
+static RPCHelpMan getdifficulty()
{
- RPCHelpMan{"getdifficulty",
+ return RPCHelpMan{"getdifficulty",
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
{},
RPCResult{
@@ -386,10 +401,12 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return GetDifficulty(::ChainActive().Tip());
+},
+ };
}
static std::vector<RPCResult> MempoolEntryDescription() { return {
@@ -463,9 +480,9 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
UniValue spent(UniValue::VARR);
const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
- const CTxMemPool::setEntries& setChildren = pool.GetMemPoolChildren(it);
- for (CTxMemPool::txiter childiter : setChildren) {
- spent.push_back(childiter->GetTx().GetHash().ToString());
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& child : children) {
+ spent.push_back(child.GetTx().GetHash().ToString());
}
info.pushKV("spentby", spent);
@@ -483,9 +500,12 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
}
-UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
{
if (verbose) {
+ if (include_mempool_sequence) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
+ }
LOCK(pool.cs);
UniValue o(UniValue::VOBJ);
for (const CTxMemPoolEntry& e : pool.mapTx) {
@@ -499,24 +519,36 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
}
return o;
} else {
+ uint64_t mempool_sequence;
std::vector<uint256> vtxid;
- pool.queryHashes(vtxid);
-
+ {
+ LOCK(pool.cs);
+ pool.queryHashes(vtxid);
+ mempool_sequence = pool.GetSequence();
+ }
UniValue a(UniValue::VARR);
for (const uint256& hash : vtxid)
a.push_back(hash.ToString());
- return a;
+ if (!include_mempool_sequence) {
+ return a;
+ } else {
+ UniValue o(UniValue::VOBJ);
+ o.pushKV("txids", a);
+ o.pushKV("mempool_sequence", mempool_sequence);
+ return o;
+ }
}
}
-static UniValue getrawmempool(const JSONRPCRequest& request)
+static RPCHelpMan getrawmempool()
{
- RPCHelpMan{"getrawmempool",
+ return 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."},
},
{
RPCResult{"for verbose = false",
@@ -529,23 +561,39 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
{
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
}},
+ RPCResult{"for verbose = false and mempool_sequence = true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "txids", "",
+ {
+ {RPCResult::Type::STR_HEX, "", "The transaction id"},
+ }},
+ {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
+ }},
},
RPCExamples{
HelpExampleCli("getrawmempool", "true")
+ HelpExampleRpc("getrawmempool", "true")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool();
- return MempoolToJSON(EnsureMemPool(request.context), fVerbose);
+ bool include_mempool_sequence = false;
+ if (!request.params[1].isNull()) {
+ include_mempool_sequence = request.params[1].get_bool();
+ }
+
+ return MempoolToJSON(EnsureMemPool(request.context), fVerbose, include_mempool_sequence);
+},
+ };
}
-static UniValue getmempoolancestors(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolancestors()
{
- RPCHelpMan{"getmempoolancestors",
+ return 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)"},
@@ -556,14 +604,17 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
RPCResult::Type::ARR, "", "",
{{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
RPCResult{"for verbose = true",
- RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
+ RPCResult::Type::OBJ_DYN, "", "",
+ {
+ {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
+ }},
},
RPCExamples{
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
+ HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[1].isNull())
fVerbose = request.params[1].get_bool();
@@ -588,7 +639,6 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
for (CTxMemPool::txiter ancestorIt : setAncestors) {
o.push_back(ancestorIt->GetTx().GetHash().ToString());
}
-
return o;
} else {
UniValue o(UniValue::VOBJ);
@@ -601,11 +651,13 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
}
return o;
}
+},
+ };
}
-static UniValue getmempooldescendants(const JSONRPCRequest& request)
+static RPCHelpMan getmempooldescendants()
{
- RPCHelpMan{"getmempooldescendants",
+ return 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)"},
@@ -625,8 +677,8 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
+ HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[1].isNull())
fVerbose = request.params[1].get_bool();
@@ -664,11 +716,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
}
return o;
}
+},
+ };
}
-static UniValue getmempoolentry(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolentry()
{
- RPCHelpMan{"getmempoolentry",
+ return RPCHelpMan{"getmempoolentry",
"\nReturns mempool data for given transaction\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
@@ -679,8 +733,8 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CTxMemPool& mempool = EnsureMemPool(request.context);
@@ -695,11 +749,13 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
UniValue info(UniValue::VOBJ);
entryToJSON(mempool, info, e);
return info;
+},
+ };
}
-static UniValue getblockhash(const JSONRPCRequest& request)
+static RPCHelpMan getblockhash()
{
- RPCHelpMan{"getblockhash",
+ return RPCHelpMan{"getblockhash",
"\nReturns hash of block in best-block-chain at height provided.\n",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
@@ -710,8 +766,8 @@ static UniValue getblockhash(const JSONRPCRequest& request)
HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
int nHeight = request.params[0].get_int();
@@ -720,11 +776,13 @@ static UniValue getblockhash(const JSONRPCRequest& request)
CBlockIndex* pblockindex = ::ChainActive()[nHeight];
return pblockindex->GetBlockHash().GetHex();
+},
+ };
}
-static UniValue getblockheader(const JSONRPCRequest& request)
+static RPCHelpMan getblockheader()
{
- RPCHelpMan{"getblockheader",
+ return RPCHelpMan{"getblockheader",
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
"If verbose is true, returns an Object with information about blockheader <hash>.\n",
{
@@ -758,8 +816,8 @@ static UniValue getblockheader(const JSONRPCRequest& request)
HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "hash"));
bool fVerbose = true;
@@ -787,6 +845,8 @@ static UniValue getblockheader(const JSONRPCRequest& request)
}
return blockheaderToJSON(tip, pblockindex);
+},
+ };
}
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
@@ -820,9 +880,9 @@ static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
return blockUndo;
}
-static UniValue getblock(const JSONRPCRequest& request)
+static RPCHelpMan getblock()
{
- RPCHelpMan{"getblock",
+ return RPCHelpMan{"getblock",
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
"If verbosity is 1, returns an Object with information about block <hash>.\n"
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
@@ -868,15 +928,14 @@ static UniValue getblock(const JSONRPCRequest& request)
{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
}},
}},
- {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
}},
},
RPCExamples{
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
int verbosity = 1;
@@ -911,11 +970,13 @@ static UniValue getblock(const JSONRPCRequest& request)
}
return blockToJSON(block, tip, pblockindex, verbosity >= 2);
+},
+ };
}
-static UniValue pruneblockchain(const JSONRPCRequest& request)
+static RPCHelpMan pruneblockchain()
{
- RPCHelpMan{"pruneblockchain", "",
+ return RPCHelpMan{"pruneblockchain", "",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n"
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
@@ -926,8 +987,8 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
HelpExampleCli("pruneblockchain", "1000")
+ HelpExampleRpc("pruneblockchain", "1000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
if (!fPruneMode)
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
@@ -966,11 +1027,13 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
block = block->pprev;
}
return uint64_t(block->nHeight);
+},
+ };
}
-static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
+static RPCHelpMan gettxoutsetinfo()
{
- RPCHelpMan{"gettxoutsetinfo",
+ return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{
@@ -992,8 +1055,8 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
@@ -1018,11 +1081,13 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
return ret;
+},
+ };
}
-UniValue gettxout(const JSONRPCRequest& request)
+static RPCHelpMan gettxout()
{
- RPCHelpMan{"gettxout",
+ return RPCHelpMan{"gettxout",
"\nReturns details about an unspent transaction output.\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
@@ -1054,8 +1119,8 @@ UniValue gettxout(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
UniValue ret(UniValue::VOBJ);
@@ -1097,11 +1162,13 @@ UniValue gettxout(const JSONRPCRequest& request)
ret.pushKV("coinbase", (bool)coin.fCoinBase);
return ret;
+},
+ };
}
-static UniValue verifychain(const JSONRPCRequest& request)
+static RPCHelpMan verifychain()
{
- RPCHelpMan{"verifychain",
+ return RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
{"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL),
@@ -1114,14 +1181,16 @@ static UniValue verifychain(const JSONRPCRequest& request)
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
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()};
LOCK(cs_main);
return CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+},
+ };
}
static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1190,9 +1259,9 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
softforks.pushKV(name, rv);
}
-UniValue getblockchaininfo(const JSONRPCRequest& request)
+RPCHelpMan getblockchaininfo()
{
- RPCHelpMan{"getblockchaininfo",
+ return RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
RPCResult{
@@ -1243,8 +1312,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
const CBlockIndex* tip = ::ChainActive().Tip();
@@ -1285,10 +1354,13 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight);
BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
+ BIP9SoftForkDescPushBack(softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
obj.pushKV("softforks", softforks);
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
/** Comparison function for sorting the getchaintips heads. */
@@ -1306,9 +1378,9 @@ struct CompareBlocksByHeight
}
};
-static UniValue getchaintips(const JSONRPCRequest& request)
+static RPCHelpMan getchaintips()
{
- RPCHelpMan{"getchaintips",
+ return RPCHelpMan{"getchaintips",
"Return information about all known tips in the block tree,"
" including the main chain as well as orphaned branches.\n",
{},
@@ -1331,8 +1403,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
HelpExampleCli("getchaintips", "")
+ HelpExampleRpc("getchaintips", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
ChainstateManager& chainman = EnsureChainman(request.context);
LOCK(cs_main);
@@ -1399,6 +1471,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
@@ -1418,9 +1492,9 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
return ret;
}
-static UniValue getmempoolinfo(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolinfo()
{
- RPCHelpMan{"getmempoolinfo",
+ return RPCHelpMan{"getmempoolinfo",
"\nReturns details on the active state of the TX memory pool.\n",
{},
RPCResult{
@@ -1439,14 +1513,16 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
HelpExampleCli("getmempoolinfo", "")
+ HelpExampleRpc("getmempoolinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
return MempoolInfoToJSON(EnsureMemPool(request.context));
+},
+ };
}
-static UniValue preciousblock(const JSONRPCRequest& request)
+static RPCHelpMan preciousblock()
{
- RPCHelpMan{"preciousblock",
+ return RPCHelpMan{"preciousblock",
"\nTreats a block as if it were received before others with the same work.\n"
"\nA later preciousblock call can override the effect of an earlier one.\n"
"\nThe effects of preciousblock are not retained across restarts.\n",
@@ -1458,8 +1534,8 @@ static UniValue preciousblock(const JSONRPCRequest& request)
HelpExampleCli("preciousblock", "\"blockhash\"")
+ HelpExampleRpc("preciousblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CBlockIndex* pblockindex;
@@ -1479,11 +1555,13 @@ static UniValue preciousblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue invalidateblock(const JSONRPCRequest& request)
+static RPCHelpMan invalidateblock()
{
- RPCHelpMan{"invalidateblock",
+ return RPCHelpMan{"invalidateblock",
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
@@ -1493,8 +1571,8 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
HelpExampleCli("invalidateblock", "\"blockhash\"")
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
BlockValidationState state;
@@ -1517,11 +1595,13 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue reconsiderblock(const JSONRPCRequest& request)
+static RPCHelpMan reconsiderblock()
{
- RPCHelpMan{"reconsiderblock",
+ return RPCHelpMan{"reconsiderblock",
"\nRemoves invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n",
{
@@ -1532,8 +1612,8 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
HelpExampleCli("reconsiderblock", "\"blockhash\"")
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
{
@@ -1554,11 +1634,13 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue getchaintxstats(const JSONRPCRequest& request)
+static RPCHelpMan getchaintxstats()
{
- 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"},
@@ -1580,8 +1662,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
HelpExampleCli("getchaintxstats", "")
+ HelpExampleRpc("getchaintxstats", "2016")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
@@ -1631,6 +1713,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
template<typename T>
@@ -1689,9 +1773,9 @@ static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&
// outpoint (needed for the utxo index) + nHeight + fCoinBase
static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool);
-static UniValue getblockstats(const JSONRPCRequest& request)
+static RPCHelpMan getblockstats()
{
- RPCHelpMan{"getblockstats",
+ return RPCHelpMan{"getblockstats",
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
"It won't work for some heights with pruning.\n",
{
@@ -1739,7 +1823,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"},
{RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions divided by segwit scale factor (4)"},
{RPCResult::Type::NUM, "totalfee", "The fee total"},
- {RPCResult::Type::NUM, "txs", "The number of transactions (excluding coinbase)"},
+ {RPCResult::Type::NUM, "txs", "The number of transactions (including coinbase)"},
{RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"},
{RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
}},
@@ -1749,8 +1833,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
CBlockIndex* pindex;
@@ -1946,11 +2030,13 @@ static UniValue getblockstats(const JSONRPCRequest& request)
ret.pushKV(stat, value);
}
return ret;
+},
+ };
}
-static UniValue savemempool(const JSONRPCRequest& request)
+static RPCHelpMan savemempool()
{
- RPCHelpMan{"savemempool",
+ return RPCHelpMan{"savemempool",
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -1958,8 +2044,8 @@ static UniValue savemempool(const JSONRPCRequest& request)
HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!mempool.IsLoaded()) {
@@ -1971,6 +2057,8 @@ static UniValue savemempool(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
namespace {
@@ -2032,9 +2120,9 @@ public:
}
};
-UniValue scantxoutset(const JSONRPCRequest& request)
+static RPCHelpMan scantxoutset()
{
- RPCHelpMan{"scantxoutset",
+ return RPCHelpMan{"scantxoutset",
"\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
"\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
"Examples of output descriptors are:\n"
@@ -2088,8 +2176,8 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
}},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
UniValue result(UniValue::VOBJ);
@@ -2182,11 +2270,13 @@ UniValue scantxoutset(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
}
return result;
+},
+ };
}
-static UniValue getblockfilter(const JSONRPCRequest& request)
+static RPCHelpMan getblockfilter()
{
- RPCHelpMan{"getblockfilter",
+ return 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"},
@@ -2201,9 +2291,9 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"") +
HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 block_hash = ParseHashV(request.params[0], "blockhash");
std::string filtertype_name = "basic";
if (!request.params[1].isNull()) {
@@ -2258,6 +2348,8 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
ret.pushKV("header", filter_header.GetHex());
return ret;
+},
+ };
}
/**
@@ -2265,9 +2357,9 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
*
* @see SnapshotMetadata
*/
-UniValue dumptxoutset(const JSONRPCRequest& request)
+static RPCHelpMan dumptxoutset()
{
- RPCHelpMan{
+ return RPCHelpMan{
"dumptxoutset",
"\nWrite the serialized UTXO set to disk.\n",
{
@@ -2288,9 +2380,9 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
},
RPCExamples{
HelpExampleCli("dumptxoutset", "utxo.dat")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
fs::path path = fs::absolute(request.params[0].get_str(), GetDataDir());
// Write to a temporary path and then move into `path` on completion
// to avoid confusion due to an interruption.
@@ -2364,6 +2456,8 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.string());
return result;
+},
+ };
}
void RegisterBlockchainRPCCommands(CRPCTable &t)
@@ -2386,7 +2480,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} },
{ "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} },
{ "blockchain", "getmempoolinfo", &getmempoolinfo, {} },
- { "blockchain", "getrawmempool", &getrawmempool, {"verbose"} },
+ { "blockchain", "getrawmempool", &getrawmempool, {"verbose", "mempool_sequence"} },
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
@@ -2407,7 +2501,7 @@ static const CRPCCommand commands[] =
{ "hidden", "dumptxoutset", &dumptxoutset, {"path"} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
}
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 5c9a43b13e..5b362bf211 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -43,7 +43,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
/** Mempool to JSON */
-UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false);
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false, bool include_mempool_sequence = false);
/** Block header to JSON */
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 66ace7263a..88c8ebe1f6 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -41,6 +41,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" },
{ "sendtoaddress", 8, "avoid_reuse" },
+ { "sendtoaddress", 9, "verbose"},
{ "settxfee", 0, "amount" },
{ "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" },
@@ -72,6 +73,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "sendmany", 8, "verbose" },
{ "deriveaddresses", 1, "range" },
{ "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
@@ -125,6 +127,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxoutproof", 0, "txids" },
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
+ { "send", 0, "outputs" },
+ { "send", 1, "conf_target" },
+ { "send", 3, "options" },
{ "importprivkey", 2, "rescan" },
{ "importaddress", 2, "rescan" },
{ "importaddress", 3, "p2sh" },
@@ -139,6 +144,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "pruneblockchain", 0, "height" },
{ "keypoolrefill", 0, "newsize" },
{ "getrawmempool", 0, "verbose" },
+ { "getrawmempool", 1, "mempool_sequence" },
{ "estimatesmartfee", 0, "conf_target" },
{ "estimaterawfee", 0, "conf_target" },
{ "estimaterawfee", 1, "threshold" },
@@ -151,6 +157,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
{ "bumpfee", 1, "options" },
+ { "psbtbumpfee", 1, "options" },
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
@@ -172,7 +179,11 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createwallet", 2, "blank"},
{ "createwallet", 4, "avoid_reuse"},
{ "createwallet", 5, "descriptors"},
+ { "createwallet", 6, "load_on_startup"},
+ { "loadwallet", 1, "load_on_startup"},
+ { "unloadwallet", 1, "load_on_startup"},
{ "getnodeaddresses", 0, "count"},
+ { "addpeeraddress", 1, "port"},
{ "stop", 0, "wait" },
};
// clang-format on
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index fee6a893eb..a561b7e93c 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -81,9 +81,9 @@ static UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-static UniValue getnetworkhashps(const JSONRPCRequest& request)
+static RPCHelpMan getnetworkhashps()
{
- RPCHelpMan{"getnetworkhashps",
+ return RPCHelpMan{"getnetworkhashps",
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
"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",
@@ -97,10 +97,12 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
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);
+},
+ };
}
static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
@@ -200,9 +202,9 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri
}
}
-static UniValue generatetodescriptor(const JSONRPCRequest& request)
+static RPCHelpMan generatetodescriptor()
{
- RPCHelpMan{
+ return RPCHelpMan{
"generatetodescriptor",
"\nMine blocks immediately to a specified descriptor (before the RPC call returns)\n",
{
@@ -218,9 +220,8 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
},
RPCExamples{
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
- }
- .Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -234,11 +235,25 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
ChainstateManager& chainman = EnsureChainman(request.context);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
+}
+
+static RPCHelpMan generate()
+{
+ return RPCHelpMan{"generate", "has been replaced by the -generate cli option. Refer to -help for more information.", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+
+ if (request.fHelp) {
+ throw std::runtime_error(self.ToString());
+ } else {
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, self.ToString());
+ }
+ }};
}
-static UniValue generatetoaddress(const JSONRPCRequest& request)
+static RPCHelpMan generatetoaddress()
{
- RPCHelpMan{"generatetoaddress",
+ return RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
@@ -256,8 +271,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -272,11 +287,13 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
CScript coinbase_script = GetScriptForDestination(destination);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
}
-static UniValue generateblock(const JSONRPCRequest& request)
+static RPCHelpMan generateblock()
{
- RPCHelpMan{"generateblock",
+ return RPCHelpMan{"generateblock",
"\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n",
{
{"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
@@ -298,8 +315,8 @@ static UniValue generateblock(const JSONRPCRequest& request)
"\nGenerate a block to myaddress, with txs rawtx and mempool_txid\n"
+ HelpExampleCli("generateblock", R"("myaddress" '["rawtx", "mempool_txid"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const auto address_or_descriptor = request.params[0].get_str();
CScript coinbase_script;
std::string error;
@@ -335,7 +352,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
txs.push_back(MakeTransactionRef(std::move(mtx)));
} else {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Transaction decode failed for %s", str));
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Transaction decode failed for %s. Make sure the tx has at least one input.", str));
}
}
@@ -379,11 +396,13 @@ static UniValue generateblock(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.pushKV("hash", block_hash.GetHex());
return obj;
+},
+ };
}
-static UniValue getmininginfo(const JSONRPCRequest& request)
+static RPCHelpMan getmininginfo()
{
- RPCHelpMan{"getmininginfo",
+ return RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
@@ -402,8 +421,8 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
const CTxMemPool& mempool = EnsureMemPool(request.context);
@@ -412,18 +431,20 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
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("networkhashps", getnetworkhashps(request));
+ obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-static UniValue prioritisetransaction(const JSONRPCRequest& request)
+static RPCHelpMan prioritisetransaction()
{
- RPCHelpMan{"prioritisetransaction",
+ return RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
@@ -440,8 +461,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -453,6 +474,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true;
+},
+ };
}
@@ -484,9 +507,9 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-static UniValue getblocktemplate(const JSONRPCRequest& request)
+static RPCHelpMan getblocktemplate()
{
- RPCHelpMan{"getblocktemplate",
+ return RPCHelpMan{"getblocktemplate",
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
@@ -500,12 +523,13 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{"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",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
},
},
{"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported softfork deployment"},
+ {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
},
},
},
@@ -517,7 +541,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "version", "The preferred block version"},
{RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
{
- {RPCResult::Type::STR, "", "rulename"},
+ {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
}},
{RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
{
@@ -525,7 +549,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
- {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block",
+ {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -541,15 +565,12 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
}},
}},
- {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
{
- {RPCResult::Type::ELISION, "", ""},
+ {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
}},
{RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
- {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
+ {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
{RPCResult::Type::STR, "target", "The hash target"},
{RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
@@ -563,13 +584,14 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
{RPCResult::Type::STR, "bits", "compressed target of next block"},
{RPCResult::Type::NUM, "height", "The height of the next block"},
+ {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}
}},
RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
std::string strMode = "template";
@@ -877,6 +899,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
class submitblock_StateCatcher final : public CValidationInterface
@@ -897,10 +921,10 @@ protected:
}
};
-static UniValue submitblock(const JSONRPCRequest& request)
+static RPCHelpMan submitblock()
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
- RPCHelpMan{"submitblock",
+ return RPCHelpMan{"submitblock",
"\nAttempts to submit new block to network.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
{
@@ -912,8 +936,8 @@ static UniValue submitblock(const JSONRPCRequest& request)
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
@@ -958,11 +982,13 @@ static UniValue submitblock(const JSONRPCRequest& request)
return "inconclusive";
}
return BIP22ValidationResult(sc->state);
+},
+ };
}
-static UniValue submitheader(const JSONRPCRequest& request)
+static RPCHelpMan submitheader()
{
- RPCHelpMan{"submitheader",
+ return RPCHelpMan{"submitheader",
"\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n",
{
@@ -974,8 +1000,8 @@ static UniValue submitheader(const JSONRPCRequest& request)
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CBlockHeader h;
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed");
@@ -994,11 +1020,13 @@ static UniValue submitheader(const JSONRPCRequest& request)
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
}
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
+},
+ };
}
-static UniValue estimatesmartfee(const JSONRPCRequest& request)
+static RPCHelpMan estimatesmartfee()
{
- RPCHelpMan{"estimatesmartfee",
+ return RPCHelpMan{"estimatesmartfee",
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
"confirmation within conf_target blocks if possible and return the number of blocks\n"
"for which the estimate is valid. Uses virtual transaction size as defined\n"
@@ -1019,7 +1047,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
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::ARR, "errors", "Errors encountered during processing",
+ {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)",
{
{RPCResult::Type::STR, "", "error"},
}},
@@ -1032,8 +1060,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1059,11 +1087,13 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
}
result.pushKV("blocks", feeCalc.returnedTarget);
return result;
+},
+ };
}
-static UniValue estimaterawfee(const JSONRPCRequest& request)
+static RPCHelpMan estimaterawfee()
{
- RPCHelpMan{"estimaterawfee",
+ return RPCHelpMan{"estimaterawfee",
"\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
" implementation of fee estimation. The parameters it can be called with\n"
@@ -1098,7 +1128,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
{
{RPCResult::Type::ELISION, "", ""},
}},
- {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing",
+ {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)",
{
{RPCResult::Type::STR, "error", ""},
}},
@@ -1115,8 +1145,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimaterawfee", "6 0.9")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1175,6 +1205,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
}
return result;
+},
+ };
}
void RegisterMiningRPCCommands(CRPCTable &t)
@@ -1198,9 +1230,10 @@ static const CRPCCommand commands[] =
{ "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} },
{ "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} },
+ { "hidden", "generate", &generate, {} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 53d38f4e11..0c982317f5 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -4,6 +4,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <httpserver.h>
+#include <index/blockfilterindex.h>
+#include <index/txindex.h>
#include <interfaces/chain.h>
#include <key_io.h>
#include <node/context.h>
@@ -27,9 +29,9 @@
#include <univalue.h>
-static UniValue validateaddress(const JSONRPCRequest& request)
+static RPCHelpMan validateaddress()
{
- RPCHelpMan{"validateaddress",
+ return RPCHelpMan{"validateaddress",
"\nReturn information about the given bitcoin address.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
@@ -50,8 +52,8 @@ static UniValue validateaddress(const JSONRPCRequest& request)
HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CTxDestination dest = DecodeDestination(request.params[0].get_str());
bool isValid = IsValidDestination(dest);
@@ -69,11 +71,13 @@ static UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKVs(detail);
}
return ret;
+},
+ };
}
-static UniValue createmultisig(const JSONRPCRequest& request)
+static RPCHelpMan createmultisig()
{
- RPCHelpMan{"createmultisig",
+ return RPCHelpMan{"createmultisig",
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n",
{
@@ -98,8 +102,8 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int required = request.params[0].get_int();
// Get the public keys
@@ -135,11 +139,13 @@ static UniValue createmultisig(const JSONRPCRequest& request)
result.pushKV("descriptor", descriptor->ToString());
return result;
+},
+ };
}
-UniValue getdescriptorinfo(const JSONRPCRequest& request)
+static RPCHelpMan getdescriptorinfo()
{
- RPCHelpMan{"getdescriptorinfo",
+ return RPCHelpMan{"getdescriptorinfo",
{"\nAnalyses a descriptor.\n"},
{
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
@@ -157,8 +163,9 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
RPCExamples{
"Analyse a descriptor\n" +
HelpExampleCli("getdescriptorinfo", "\"wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
FlatSigningProvider provider;
@@ -175,11 +182,13 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
result.pushKV("issolvable", desc->IsSolvable());
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
return result;
+},
+ };
}
-UniValue deriveaddresses(const JSONRPCRequest& request)
+static RPCHelpMan deriveaddresses()
{
- RPCHelpMan{"deriveaddresses",
+ return RPCHelpMan{"deriveaddresses",
{"\nDerives one or more addresses corresponding to an output descriptor.\n"
"Examples of output descriptors are:\n"
" pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
@@ -202,8 +211,9 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
RPCExamples{
"First three native segwit receive addresses\n" +
HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
const std::string desc_str = request.params[0].get_str();
@@ -254,11 +264,13 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
}
return addresses;
+},
+ };
}
-static UniValue verifymessage(const JSONRPCRequest& request)
+static RPCHelpMan verifymessage()
{
- RPCHelpMan{"verifymessage",
+ return RPCHelpMan{"verifymessage",
"\nVerify a signed message\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."},
@@ -278,8 +290,8 @@ static UniValue verifymessage(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
std::string strAddress = request.params[0].get_str();
@@ -301,11 +313,13 @@ static UniValue verifymessage(const JSONRPCRequest& request)
}
return false;
+},
+ };
}
-static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
+static RPCHelpMan signmessagewithprivkey()
{
- RPCHelpMan{"signmessagewithprivkey",
+ return RPCHelpMan{"signmessagewithprivkey",
"\nSign a message with the private key of an address\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key to sign the message with."},
@@ -322,8 +336,8 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::string strPrivkey = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
@@ -339,11 +353,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
}
return signature;
+},
+ };
}
-static UniValue setmocktime(const JSONRPCRequest& request)
+static RPCHelpMan setmocktime()
{
- RPCHelpMan{"setmocktime",
+ return RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
@@ -351,8 +367,8 @@ static UniValue setmocktime(const JSONRPCRequest& request)
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
if (!Params().IsMockableChain()) {
throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only");
}
@@ -374,19 +390,21 @@ static UniValue setmocktime(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue mockscheduler(const JSONRPCRequest& request)
+static RPCHelpMan mockscheduler()
{
- RPCHelpMan{"mockscheduler",
+ return RPCHelpMan{"mockscheduler",
"\nBump the scheduler into the future (-regtest only)\n",
{
{"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." },
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
if (!Params().IsMockableChain()) {
throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only");
}
@@ -405,6 +423,8 @@ static UniValue mockscheduler(const JSONRPCRequest& request)
node.scheduler->MockForward(std::chrono::seconds(delta_seconds));
return NullUniValue;
+},
+ };
}
static UniValue RPCLockedMemoryInfo()
@@ -439,12 +459,12 @@ static std::string RPCMallocInfo()
}
#endif
-static UniValue getmemoryinfo(const JSONRPCRequest& request)
+static RPCHelpMan getmemoryinfo()
{
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
*/
- 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"
@@ -474,8 +494,8 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
HelpExampleCli("getmemoryinfo", "")
+ HelpExampleRpc("getmemoryinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
@@ -490,6 +510,8 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
}
+},
+ };
}
static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
@@ -510,9 +532,9 @@ static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
}
}
-UniValue logging(const JSONRPCRequest& request)
+static RPCHelpMan logging()
{
- RPCHelpMan{"logging",
+ return RPCHelpMan{"logging",
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
"When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
@@ -543,8 +565,8 @@ UniValue logging(const JSONRPCRequest& request)
HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint32_t original_log_categories = LogInstance().GetCategoryMask();
if (request.params[0].isArray()) {
EnableOrDisableLogCategories(request.params[0], true);
@@ -575,26 +597,99 @@ UniValue logging(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
-static UniValue echo(const JSONRPCRequest& request)
+static RPCHelpMan echo(const std::string& name)
{
- if (request.fHelp)
- throw std::runtime_error(
- RPCHelpMan{"echo|echojson ...",
+ return RPCHelpMan{name,
"\nSimply echo back the input arguments. This command is for testing.\n"
- "\nIt will return an internal bug report when exactly 100 arguments are passed.\n"
+ "\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n"
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
"bitcoin-cli and the GUI. There is no server-side difference.",
- {},
+ {
+ {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
+ {"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"},
RPCExamples{""},
- }.ToString()
- );
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ if (request.fHelp) throw std::runtime_error(self.ToString());
- CHECK_NONFATAL(request.params.size() != 100);
+ if (request.params[9].isStr()) {
+ CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug");
+ }
return request.params;
+},
+ };
+}
+
+static RPCHelpMan echo() { return echo("echo"); }
+static RPCHelpMan echojson() { return echo("echojson"); }
+
+static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
+{
+ UniValue ret_summary(UniValue::VOBJ);
+ if (!index_name.empty() && index_name != summary.name) return ret_summary;
+
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("synced", summary.synced);
+ entry.pushKV("best_block_height", summary.best_block_height);
+ ret_summary.pushKV(summary.name, entry);
+ return ret_summary;
+}
+
+static RPCHelpMan getindexinfo()
+{
+ return RPCHelpMan{"getindexinfo",
+ "\nReturns the status of one or all available indices currently running in the node.\n",
+ {
+ {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "", {
+ {
+ RPCResult::Type::OBJ, "name", "The name of the index",
+ {
+ {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"},
+ {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"},
+ }
+ },
+ },
+ },
+ RPCExamples{
+ HelpExampleCli("getindexinfo", "")
+ + HelpExampleRpc("getindexinfo", "")
+ + HelpExampleCli("getindexinfo", "txindex")
+ + HelpExampleRpc("getindexinfo", "txindex")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ UniValue result(UniValue::VOBJ);
+ const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str();
+
+ if (g_txindex) {
+ result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name));
+ }
+
+ ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
+ result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
+ });
+
+ return result;
+},
+ };
}
void RegisterMiscRPCCommands(CRPCTable &t)
@@ -611,15 +706,16 @@ static const CRPCCommand commands[] =
{ "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
+ { "util", "getindexinfo", &getindexinfo, {"index_name"} },
/* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
{ "hidden", "mockscheduler", &mockscheduler, {"delta_time"}},
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "echojson", &echojson, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 9981ea35df..b81e6414a5 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -29,9 +29,18 @@
#include <univalue.h>
-static UniValue getconnectioncount(const JSONRPCRequest& request)
+const std::vector<std::string> CONNECTION_TYPE_DOC{
+ "outbound-full-relay (default automatic connections)",
+ "block-relay-only (does not relay transactions or addresses)",
+ "inbound (initiated by the peer)",
+ "manual (added via addnode RPC or -addnode/-connect configuration options)",
+ "addr-fetch (short-lived automatic connection for soliciting addresses)",
+ "feeler (short-lived automatic connection for testing addresses)"
+};
+
+static RPCHelpMan getconnectioncount()
{
- RPCHelpMan{"getconnectioncount",
+ return RPCHelpMan{"getconnectioncount",
"\nReturns the number of connections to other nodes.\n",
{},
RPCResult{
@@ -41,18 +50,20 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
HelpExampleCli("getconnectioncount", "")
+ HelpExampleRpc("getconnectioncount", "")
},
- }.Check(request);
-
+ [&](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");
return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+},
+ };
}
-static UniValue ping(const JSONRPCRequest& request)
+static RPCHelpMan ping()
{
- RPCHelpMan{"ping",
+ return RPCHelpMan{"ping",
"\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
"Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
"Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n",
@@ -62,8 +73,8 @@ static UniValue ping(const JSONRPCRequest& request)
HelpExampleCli("ping", "")
+ HelpExampleRpc("ping", "")
},
- }.Check(request);
-
+ [&](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");
@@ -73,11 +84,13 @@ static UniValue ping(const JSONRPCRequest& request)
pnode->fPingQueued = true;
});
return NullUniValue;
+},
+ };
}
-static UniValue getpeerinfo(const JSONRPCRequest& request)
+static RPCHelpMan getpeerinfo()
{
- RPCHelpMan{"getpeerinfo",
+ return RPCHelpMan{"getpeerinfo",
"\nReturns data about each connected network node as a json array of objects.\n",
{},
RPCResult{
@@ -90,6 +103,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
{RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
{RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
+ {RPCResult::Type::STR, "network", "Network (ipv4, ipv6, or onion) the peer connected through"},
{RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
@@ -100,6 +114,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"},
{RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
{RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
+ {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
+ {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"},
{RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
{RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
{RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
@@ -110,7 +126,11 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
- {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"},
+ {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
+ "(DEPRECATED, returned only if the config option -deprecatedrpc=getpeerinfo_addnode is passed)"},
+ {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n"
+ "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n"
+ "best capture connection behaviors."},
{RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"},
{RPCResult::Type::NUM, "banscore", "The ban score (DEPRECATED, returned only if config option -deprecatedrpc=banscore is passed)"},
{RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
@@ -119,7 +139,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{
{RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"},
}},
- {RPCResult::Type::BOOL, "whitelisted", "Whether the peer is whitelisted"},
+ {RPCResult::Type::BOOL, "whitelisted", /* optional */ true, "Whether the peer is whitelisted with default permissions\n"
+ "(DEPRECATED, returned only if config option -deprecatedrpc=whitelisted is passed)"},
{RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
{RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
{
@@ -131,7 +152,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{
{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"
- "Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."}
+ "Only known message types can appear as keys in the object and all bytes received\n"
+ "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."}
}},
}},
}},
@@ -140,8 +162,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
HelpExampleCli("getpeerinfo", "")
+ HelpExampleRpc("getpeerinfo", "")
},
- }.Check(request);
-
+ [&](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");
@@ -157,10 +179,13 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
obj.pushKV("id", stats.nodeid);
obj.pushKV("addr", stats.addrName);
- if (!(stats.addrLocal.empty()))
- obj.pushKV("addrlocal", stats.addrLocal);
- if (stats.addrBind.IsValid())
+ if (stats.addrBind.IsValid()) {
obj.pushKV("addrbind", stats.addrBind.ToString());
+ }
+ if (!(stats.addrLocal.empty())) {
+ obj.pushKV("addrlocal", stats.addrLocal);
+ }
+ obj.pushKV("network", stats.m_network);
if (stats.m_mapped_as != 0) {
obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as));
}
@@ -169,6 +194,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
obj.pushKV("relaytxes", stats.fRelayTxes);
obj.pushKV("lastsend", stats.nLastSend);
obj.pushKV("lastrecv", stats.nLastRecv);
+ obj.pushKV("last_transaction", stats.nLastTXTime);
+ obj.pushKV("last_block", stats.nLastBlockTime);
obj.pushKV("bytessent", stats.nSendBytes);
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
@@ -188,12 +215,15 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
// their ver message.
obj.pushKV("subver", stats.cleanSubVer);
obj.pushKV("inbound", stats.fInbound);
- obj.pushKV("addnode", stats.m_manual_connection);
+ if (IsDeprecatedRPCEnabled("getpeerinfo_addnode")) {
+ // addnode is deprecated in v0.21 for removal in v0.22
+ obj.pushKV("addnode", stats.m_manual_connection);
+ }
obj.pushKV("startingheight", stats.nStartingHeight);
if (fStateStats) {
if (IsDeprecatedRPCEnabled("banscore")) {
// banscore is deprecated in v0.21 for removal in v0.22
- obj.pushKV("banscore", statestats.nMisbehavior);
+ obj.pushKV("banscore", statestats.m_misbehavior_score);
}
obj.pushKV("synced_headers", statestats.nSyncHeight);
obj.pushKV("synced_blocks", statestats.nCommonHeight);
@@ -203,7 +233,10 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}
obj.pushKV("inflight", heights);
}
- obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
+ if (IsDeprecatedRPCEnabled("whitelisted")) {
+ // whitelisted is deprecated in v0.21 for removal in v0.22
+ obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
+ }
UniValue permissions(UniValue::VARR);
for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
permissions.push_back(permission);
@@ -224,22 +257,19 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
recvPerMsgCmd.pushKV(i.first, i.second);
}
obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
+ obj.pushKV("connection_type", stats.m_conn_type_string);
ret.push_back(obj);
}
return ret;
+},
+ };
}
-static UniValue addnode(const JSONRPCRequest& request)
+static RPCHelpMan addnode()
{
- std::string strCommand;
- if (!request.params[1].isNull())
- strCommand = request.params[1].get_str();
- if (request.fHelp || request.params.size() != 2 ||
- (strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
- throw std::runtime_error(
- RPCHelpMan{"addnode",
+ return 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"
@@ -253,7 +283,15 @@ static UniValue addnode(const JSONRPCRequest& request)
HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"")
+ HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
},
- }.ToString());
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ std::string strCommand;
+ if (!request.params[1].isNull())
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.params.size() != 2 ||
+ (strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
+ throw std::runtime_error(
+ self.ToString());
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
@@ -264,7 +302,7 @@ static UniValue addnode(const JSONRPCRequest& request)
if (strCommand == "onetry")
{
CAddress addr;
- node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true);
+ node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), ConnectionType::MANUAL);
return NullUniValue;
}
@@ -276,15 +314,17 @@ static UniValue addnode(const JSONRPCRequest& request)
else if(strCommand == "remove")
{
if(!node.connman->RemoveAddedNode(strNode))
- throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
+ throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node could not be removed. It has not been added previously.");
}
return NullUniValue;
+},
+ };
}
-static UniValue disconnectnode(const JSONRPCRequest& request)
+static RPCHelpMan disconnectnode()
{
- RPCHelpMan{"disconnectnode",
+ return RPCHelpMan{"disconnectnode",
"\nImmediately disconnects from the specified peer node.\n"
"\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",
@@ -299,8 +339,8 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleRpc("disconnectnode", "\"\", 1")
},
- }.Check(request);
-
+ [&](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");
@@ -325,11 +365,13 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue getaddednodeinfo(const JSONRPCRequest& request)
+static RPCHelpMan getaddednodeinfo()
{
- RPCHelpMan{"getaddednodeinfo",
+ return RPCHelpMan{"getaddednodeinfo",
"\nReturns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
{
@@ -357,8 +399,8 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
+ HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
},
- }.Check(request);
-
+ [&](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");
@@ -397,11 +439,13 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue getnettotals(const JSONRPCRequest& request)
+static RPCHelpMan getnettotals()
{
- RPCHelpMan{"getnettotals",
+ return RPCHelpMan{"getnettotals",
"\nReturns information about network traffic, including bytes in, bytes out,\n"
"and current time.\n",
{},
@@ -410,7 +454,7 @@ static UniValue getnettotals(const JSONRPCRequest& request)
{
{RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
{RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
- {RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"},
+ {RPCResult::Type::NUM_TIME, "timemillis", "Current " + UNIX_EPOCH_TIME + " in milliseconds"},
{RPCResult::Type::OBJ, "uploadtarget", "",
{
{RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
@@ -426,7 +470,8 @@ static UniValue getnettotals(const JSONRPCRequest& request)
HelpExampleCli("getnettotals", "")
+ HelpExampleRpc("getnettotals", "")
},
- }.Check(request);
+ [&](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");
@@ -445,6 +490,8 @@ static UniValue getnettotals(const JSONRPCRequest& request)
outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle());
obj.pushKV("uploadtarget", outboundLimit);
return obj;
+},
+ };
}
static UniValue GetNetworksInfo()
@@ -468,9 +515,9 @@ static UniValue GetNetworksInfo()
return networks;
}
-static UniValue getnetworkinfo(const JSONRPCRequest& request)
+static RPCHelpMan getnetworkinfo()
{
- RPCHelpMan{"getnetworkinfo",
+ return RPCHelpMan{"getnetworkinfo",
"Returns an object containing various state info regarding P2P networking.\n",
{},
RPCResult{
@@ -486,7 +533,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
}},
{RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"},
{RPCResult::Type::NUM, "timeoffset", "the time offset"},
- {RPCResult::Type::NUM, "connections", "the number of connections"},
+ {RPCResult::Type::NUM, "connections", "the total number of connections"},
+ {RPCResult::Type::NUM, "connections_in", "the number of inbound connections"},
+ {RPCResult::Type::NUM, "connections_out", "the number of outbound connections"},
{RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"},
{RPCResult::Type::ARR, "networks", "information per network",
{
@@ -517,8 +566,8 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
HelpExampleCli("getnetworkinfo", "")
+ HelpExampleRpc("getnetworkinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", CLIENT_VERSION);
@@ -534,7 +583,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
- obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
+ obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
+ obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@@ -554,11 +605,13 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("localaddresses", localAddresses);
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
-static UniValue setban(const JSONRPCRequest& request)
+static RPCHelpMan setban()
{
- const RPCHelpMan help{"setban",
+ return RPCHelpMan{"setban",
"\nAttempts to add or remove an IP/Subnet from the banned list.\n",
{
{"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
@@ -572,7 +625,8 @@ static UniValue setban(const JSONRPCRequest& request)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
},
- };
+ [&](const RPCHelpMan& help, const JSONRPCRequest& request) -> UniValue
+{
std::string strCommand;
if (!request.params[1].isNull())
strCommand = request.params[1].get_str();
@@ -635,11 +689,13 @@ static UniValue setban(const JSONRPCRequest& request)
}
}
return NullUniValue;
+},
+ };
}
-static UniValue listbanned(const JSONRPCRequest& request)
+static RPCHelpMan listbanned()
{
- RPCHelpMan{"listbanned",
+ return RPCHelpMan{"listbanned",
"\nList all manually banned IPs/Subnets.\n",
{},
RPCResult{RPCResult::Type::ARR, "", "",
@@ -655,8 +711,8 @@ static UniValue listbanned(const JSONRPCRequest& request)
HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
@@ -678,11 +734,13 @@ static UniValue listbanned(const JSONRPCRequest& request)
}
return bannedAddresses;
+},
+ };
}
-static UniValue clearbanned(const JSONRPCRequest& request)
+static RPCHelpMan clearbanned()
{
- RPCHelpMan{"clearbanned",
+ return RPCHelpMan{"clearbanned",
"\nClear all banned IPs.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -690,7 +748,8 @@ static UniValue clearbanned(const JSONRPCRequest& request)
HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
@@ -699,19 +758,21 @@ static UniValue clearbanned(const JSONRPCRequest& request)
node.banman->ClearBanned();
return NullUniValue;
+},
+ };
}
-static UniValue setnetworkactive(const JSONRPCRequest& request)
+static RPCHelpMan setnetworkactive()
{
- RPCHelpMan{"setnetworkactive",
+ return RPCHelpMan{"setnetworkactive",
"\nDisable/enable all p2p network activity.\n",
{
{"state", RPCArg::Type::BOOL, RPCArg::Optional::NO, "true to enable networking, false to disable"},
},
RPCResult{RPCResult::Type::BOOL, "", "The value that was passed in"},
RPCExamples{""},
- }.Check(request);
-
+ [&](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");
@@ -720,14 +781,16 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
node.connman->SetNetworkActive(request.params[0].get_bool());
return node.connman->GetNetworkActive();
+},
+ };
}
-static UniValue getnodeaddresses(const JSONRPCRequest& request)
+static RPCHelpMan getnodeaddresses()
{
- RPCHelpMan{"getnodeaddresses",
+ return RPCHelpMan{"getnodeaddresses",
"\nReturn known addresses which can potentially be used to find new nodes in the network\n",
{
- {"count", RPCArg::Type::NUM, /* default */ "1", "How many addresses to return. Limited to the smaller of " + ToString(ADDRMAN_GETADDR_MAX) + " or " + ToString(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."},
+ {"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."},
},
RPCResult{
RPCResult::Type::ARR, "", "",
@@ -745,7 +808,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
HelpExampleCli("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "8")
},
- }.Check(request);
+ [&](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");
@@ -754,18 +818,16 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
int count = 1;
if (!request.params[0].isNull()) {
count = request.params[0].get_int();
- if (count <= 0) {
+ if (count < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
}
}
// returns a shuffled list of CAddress
- std::vector<CAddress> vAddr = node.connman->GetAddresses();
+ std::vector<CAddress> vAddr = node.connman->GetAddresses(count, /* max_pct */ 0);
UniValue ret(UniValue::VARR);
- int address_return_count = std::min<int>(count, vAddr.size());
- for (int i = 0; i < address_return_count; ++i) {
+ for (const CAddress& addr : vAddr) {
UniValue obj(UniValue::VOBJ);
- const CAddress& addr = vAddr[i];
obj.pushKV("time", (int)addr.nTime);
obj.pushKV("services", (uint64_t)addr.nServices);
obj.pushKV("address", addr.ToStringIP());
@@ -773,6 +835,58 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
ret.push_back(obj);
}
return ret;
+},
+ };
+}
+
+static RPCHelpMan addpeeraddress()
+{
+ return RPCHelpMan{"addpeeraddress",
+ "\nAdd the address of a potential peer to the address manager. This RPC is for testing only.\n",
+ {
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
+ {"port", RPCArg::Type::NUM, RPCArg::Optional::NO, "The port of the peer"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "success", "whether the peer address was successfully added to the address manager"},
+ },
+ },
+ RPCExamples{
+ HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333")
+ + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")
+ },
+ [&](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");
+ }
+
+ UniValue obj(UniValue::VOBJ);
+
+ std::string addr_string = request.params[0].get_str();
+ uint16_t port = request.params[1].get_int();
+
+ 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.connman->AddNewAddresses({address}, address)) {
+ obj.pushKV("success", false);
+ return obj;
+ }
+
+ obj.pushKV("success", true);
+ return obj;
+},
+ };
}
void RegisterNetRPCCommands(CRPCTable &t)
@@ -794,9 +908,10 @@ static const CRPCCommand commands[] =
{ "network", "clearbanned", &clearbanned, {} },
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
{ "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
+ { "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9b6ef15785..c6d7fea443 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -67,9 +67,9 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
}
}
-static UniValue getrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan getrawtransaction()
{
- RPCHelpMan{
+ return RPCHelpMan{
"getrawtransaction",
"\nReturn the raw transaction data.\n"
@@ -155,8 +155,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const NodeContext& node = EnsureNodeContext(request.context);
bool in_active_chain = true;
@@ -191,7 +191,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
}
uint256 hash_block;
- const CTransactionRef tx = GetTransaction(blockindex, node.mempool, hash, Params().GetConsensus(), hash_block);
+ const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block);
if (!tx) {
std::string errmsg;
if (blockindex) {
@@ -217,11 +217,13 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
if (blockindex) result.pushKV("in_active_chain", in_active_chain);
TxToJSON(*tx, hash_block, result);
return result;
+},
+ };
}
-static UniValue gettxoutproof(const JSONRPCRequest& request)
+static RPCHelpMan gettxoutproof()
{
- RPCHelpMan{"gettxoutproof",
+ return RPCHelpMan{"gettxoutproof",
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
"\nNOTE: By default this function only works sometimes. This is when there is an\n"
"unspent output in the utxo for this transaction. To make it always work,\n"
@@ -239,8 +241,8 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::set<uint256> setTxids;
uint256 oneTxid;
UniValue txids = request.params[0].get_array();
@@ -315,11 +317,13 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
ssMB << mb;
std::string strHex = HexStr(ssMB);
return strHex;
+},
+ };
}
-static UniValue verifytxoutproof(const JSONRPCRequest& request)
+static RPCHelpMan verifytxoutproof()
{
- RPCHelpMan{"verifytxoutproof",
+ return RPCHelpMan{"verifytxoutproof",
"\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n",
{
@@ -332,8 +336,8 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
}
},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
@@ -360,11 +364,13 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
-static UniValue createrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan createrawtransaction()
{
- RPCHelpMan{"createrawtransaction",
+ return RPCHelpMan{"createrawtransaction",
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
@@ -412,8 +418,8 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -429,11 +435,13 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
return EncodeHexTx(CTransaction(rawTx));
+},
+ };
}
-static UniValue decoderawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan decoderawtransaction()
{
- RPCHelpMan{"decoderawtransaction",
+ return 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"},
@@ -498,8 +506,8 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
CMutableTransaction mtx;
@@ -515,6 +523,8 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
return result;
+},
+ };
}
static std::string GetAllOutputTypes()
@@ -527,9 +537,9 @@ static std::string GetAllOutputTypes()
return Join(ret, ", ");
}
-static UniValue decodescript(const JSONRPCRequest& request)
+static RPCHelpMan decodescript()
{
- RPCHelpMan{"decodescript",
+ return RPCHelpMan{"decodescript",
"\nDecode a hex-encoded script.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
@@ -563,8 +573,8 @@ static UniValue decodescript(const JSONRPCRequest& request)
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
UniValue r(UniValue::VOBJ);
@@ -616,18 +626,20 @@ static UniValue decodescript(const JSONRPCRequest& request)
}
return r;
+},
+ };
}
-static UniValue combinerawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan combinerawtransaction()
{
- RPCHelpMan{"combinerawtransaction",
+ return RPCHelpMan{"combinerawtransaction",
"\nCombine multiple partially signed transactions into one transaction.\n"
"The combined transaction may be another partially signed transaction or a \n"
"fully signed transaction.",
{
{"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex strings of partially signed transactions",
{
- {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A hex-encoded raw transaction"},
},
},
},
@@ -637,15 +649,15 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue txs = request.params[0].get_array();
std::vector<CMutableTransaction> txVariants(txs.size());
for (unsigned int idx = 0; idx < txs.size(); idx++) {
- if (!DecodeHexTx(txVariants[idx], txs[idx].get_str(), true)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d", idx));
+ if (!DecodeHexTx(txVariants[idx], txs[idx].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d. Make sure the tx has at least one input.", idx));
}
}
@@ -699,11 +711,13 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
}
return EncodeHexTx(CTransaction(mergedTx));
+},
+ };
}
-static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
+static RPCHelpMan signrawtransactionwithkey()
{
- RPCHelpMan{"signrawtransactionwithkey",
+ return RPCHelpMan{"signrawtransactionwithkey",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second argument is an array of base58-encoded private\n"
"keys that will be the only keys used to sign the transaction.\n"
@@ -744,7 +758,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
{RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
- {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
+ {RPCResult::Type::ARR, "errors", /* optional */ true, "Script verification errors (if there are any)",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -761,13 +775,13 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ if (!DecodeHexTx(mtx, request.params[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
}
FillableSigningProvider keystore;
@@ -795,11 +809,13 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
SignTransaction(mtx, &keystore, coins, request.params[3], result);
return result;
+},
+ };
}
-static UniValue sendrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan sendrawtransaction()
{
- RPCHelpMan{"sendrawtransaction",
+ return RPCHelpMan{"sendrawtransaction",
"\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nNote that the transaction will be sent unconditionally to all peers, so using this\n"
"for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
@@ -824,17 +840,17 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VSTR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
});
- // parse hex string from parameter
CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_str()))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ if (!DecodeHexTx(mtx, request.params[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() ?
@@ -853,11 +869,13 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
}
return tx->GetHash().GetHex();
+},
+ };
}
-static UniValue testmempoolaccept(const JSONRPCRequest& request)
+static RPCHelpMan testmempoolaccept()
{
- 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"
"\nSee sendrawtransaction call.\n",
@@ -878,6 +896,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
+ {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)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
+ }},
{RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
}},
}
@@ -892,8 +915,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
@@ -905,7 +928,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256& tx_hash = tx->GetHash();
@@ -924,13 +947,30 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
TxValidationState state;
bool test_accept_res;
+ CAmount fee{0};
{
LOCK(cs_main);
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
+ }
+
+ // Check that fee does not exceed maximum fee
+ if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
+ result_0.pushKV("allowed", false);
+ result_0.pushKV("reject-reason", "max-fee-exceeded");
+ result.push_back(std::move(result_0));
+ return result;
}
result_0.pushKV("allowed", test_accept_res);
- if (!test_accept_res) {
+
+ // Only return the fee and vsize if the transaction would pass ATMP.
+ // These can be used to calculate the feerate.
+ if (test_accept_res) {
+ result_0.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_0.pushKV("fees", fees);
+ } else {
if (state.IsInvalid()) {
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
result_0.pushKV("reject-reason", "missing-inputs");
@@ -944,11 +984,13 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
result.push_back(std::move(result_0));
return result;
+},
+ };
}
-UniValue decodepsbt(const JSONRPCRequest& request)
+static RPCHelpMan decodepsbt()
{
- RPCHelpMan{"decodepsbt",
+ return RPCHelpMan{"decodepsbt",
"\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
@@ -1060,8 +1102,8 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("decodepsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
// Unserialize the transactions
@@ -1253,11 +1295,13 @@ UniValue decodepsbt(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
-UniValue combinepsbt(const JSONRPCRequest& request)
+static RPCHelpMan combinepsbt()
{
- RPCHelpMan{"combinepsbt",
+ return RPCHelpMan{"combinepsbt",
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n",
{
@@ -1273,8 +1317,8 @@ UniValue combinepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VARR}, true);
// Unserialize the transactions
@@ -1300,12 +1344,14 @@ UniValue combinepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue finalizepsbt(const JSONRPCRequest& request)
+static RPCHelpMan finalizepsbt()
{
- RPCHelpMan{"finalizepsbt",
+ return RPCHelpMan{"finalizepsbt",
"Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
"network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
"created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
@@ -1326,8 +1372,8 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("finalizepsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
// Unserialize the transactions
@@ -1358,11 +1404,13 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
result.pushKV("complete", complete);
return result;
+},
+ };
}
-UniValue createpsbt(const JSONRPCRequest& request)
+static RPCHelpMan createpsbt()
{
- RPCHelpMan{"createpsbt",
+ return RPCHelpMan{"createpsbt",
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n",
{
@@ -1404,8 +1452,8 @@ UniValue createpsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -1435,12 +1483,14 @@ UniValue createpsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue converttopsbt(const JSONRPCRequest& request)
+static RPCHelpMan converttopsbt()
{
- RPCHelpMan{"converttopsbt",
+ return RPCHelpMan{"converttopsbt",
"\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
{
@@ -1464,8 +1514,8 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\nConvert the transaction to a PSBT\n"
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
// parse hex string from parameter
@@ -1502,12 +1552,14 @@ UniValue converttopsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue utxoupdatepsbt(const JSONRPCRequest& request)
+static RPCHelpMan utxoupdatepsbt()
{
- RPCHelpMan{"utxoupdatepsbt",
+ return RPCHelpMan{"utxoupdatepsbt",
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
@@ -1524,8 +1576,9 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
// Unserialize the transactions
@@ -1590,12 +1643,14 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue joinpsbts(const JSONRPCRequest& request)
+static RPCHelpMan joinpsbts()
{
- RPCHelpMan{"joinpsbts",
+ return RPCHelpMan{"joinpsbts",
"\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
"No input in any of the PSBTs can be in more than one of the PSBTs.\n",
{
@@ -1609,8 +1664,9 @@ UniValue joinpsbts(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VARR}, true);
// Unserialize the transactions
@@ -1683,12 +1739,14 @@ UniValue joinpsbts(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << shuffled_psbt;
- return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
+ return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue analyzepsbt(const JSONRPCRequest& request)
+static RPCHelpMan analyzepsbt()
{
- RPCHelpMan{"analyzepsbt",
+ return RPCHelpMan{"analyzepsbt",
"\nAnalyzes and provides information about the current status of a PSBT and its inputs\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
@@ -1722,13 +1780,14 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
{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, "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", "Error message if there is one"},
+ {RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"},
}
},
RPCExamples {
HelpExampleCli("analyzepsbt", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
// Unserialize the transaction
@@ -1792,6 +1851,8 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
void RegisterRawTransactionRPCCommands(CRPCTable &t)
@@ -1821,7 +1882,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
};
// clang-format on
-
- for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ for (const auto& c : commands) {
+ t.appendCommand(c.name, &c);
+ }
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 1031716b4a..f004ecc20c 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -21,10 +21,17 @@
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
- if (inputs_in.isNull() || outputs_in.isNull())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+ if (outputs_in.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
+ }
+
+ UniValue inputs;
+ if (inputs_in.isNull()) {
+ inputs = UniValue::VARR;
+ } else {
+ inputs = inputs_in.get_array();
+ }
- UniValue inputs = inputs_in.get_array();
const bool outputs_is_obj = outputs_in.isObject();
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
@@ -48,7 +55,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
int nOutput = vout_v.get_int();
if (nOutput < 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
uint32_t nSequence;
if (rbf) {
@@ -170,7 +177,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative");
}
COutPoint out(txid, nOut);
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index e5f6b1b9f1..f32d9abac6 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -256,23 +256,16 @@ static const CRPCCommand vRPCCommands[] =
CRPCTable::CRPCTable()
{
- unsigned int vcidx;
- for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
- {
- const CRPCCommand *pcmd;
-
- pcmd = &vRPCCommands[vcidx];
- mapCommands[pcmd->name].push_back(pcmd);
+ for (const auto& c : vRPCCommands) {
+ appendCommand(c.name, &c);
}
}
-bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
+void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
{
- if (IsRPCRunning())
- return false;
+ CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
mapCommands[name].push_back(pcmd);
- return true;
}
bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 6da3e94ea2..b2358ac5b2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -160,7 +160,7 @@ public:
/**
* Appends a CRPCCommand to the dispatch table.
*
- * Returns false if RPC server is already running (dump concurrency protection).
+ * Precondition: RPC server is not running
*
* Commands with different method names but the same unique_id will
* be considered aliases, and only the first registered method name will
@@ -169,7 +169,7 @@ public:
* between calls based on method name, and aliased commands can also
* register different names, types, and numbers of parameters.
*/
- bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
+ void appendCommand(const std::string& name, const CRPCCommand* pcmd);
bool removeCommand(const std::string& name, const CRPCCommand* pcmd);
};
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 073a7688a9..40dfdb587e 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -504,7 +504,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
- if (arg.m_hidden) continue;
+ if (arg.m_hidden) break; // Any arg that follows is also hidden
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@@ -526,7 +526,7 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
- if (arg.m_hidden) continue;
+ if (arg.m_hidden) break; // Any arg that follows is also hidden
if (i == 0) ret += "\nArguments:\n";
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 39feb4ccc9..5735e7df66 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -342,13 +342,10 @@ public:
};
}
-/** Helper for OP_CHECKSIG and OP_CHECKSIGVERIFY
- *
- * A return value of false means the script fails entirely. When true is returned, the
- * fSuccess variable indicates whether the signature check itself succeeded.
- */
-static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
+static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
{
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
+
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
@@ -363,7 +360,7 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
//serror is set
return false;
}
- fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+ fSuccess = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
@@ -371,7 +368,67 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
return true;
}
-bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
+static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
+{
+ assert(sigversion == SigVersion::TAPSCRIPT);
+
+ /*
+ * The following validation sequence is consensus critical. Please note how --
+ * upgradable public key versions precede other rules;
+ * the script execution fails when using empty signature with invalid public key;
+ * the script execution fails when using non-empty invalid signature.
+ */
+ success = !sig.empty();
+ if (success) {
+ // Implement the sigops/witnesssize ratio test.
+ // Passing with an upgradable public key version is also counted.
+ assert(execdata.m_validation_weight_left_init);
+ execdata.m_validation_weight_left -= VALIDATION_WEIGHT_PER_SIGOP_PASSED;
+ if (execdata.m_validation_weight_left < 0) {
+ return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
+ }
+ }
+ if (pubkey.size() == 0) {
+ return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
+ } else if (pubkey.size() == 32) {
+ if (success && !checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror)) {
+ return false; // serror is set
+ }
+ } else {
+ /*
+ * New public key version softforks should be defined before this `else` block.
+ * Generally, the new code should not do anything but failing the script execution. To avoid
+ * consensus bugs, it should not modify any existing values (including `success`).
+ */
+ if ((flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) != 0) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
+ }
+ }
+
+ return true;
+}
+
+/** Helper for OP_CHECKSIG, OP_CHECKSIGVERIFY, and (in Tapscript) OP_CHECKSIGADD.
+ *
+ * A return value of false means the script fails entirely. When true is returned, the
+ * success variable indicates whether the signature check itself succeeded.
+ */
+static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
+{
+ switch (sigversion) {
+ case SigVersion::BASE:
+ case SigVersion::WITNESS_V0:
+ return EvalChecksigPreTapscript(sig, pubkey, pbegincodehash, pend, flags, checker, sigversion, serror, success);
+ case SigVersion::TAPSCRIPT:
+ return EvalChecksigTapscript(sig, pubkey, execdata, flags, checker, sigversion, serror, success);
+ case SigVersion::TAPROOT:
+ // Key path spending in Taproot has no script, so this is unreachable.
+ break;
+ }
+ assert(false);
+}
+
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
{
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
@@ -381,6 +438,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
+ // sigversion cannot be TAPROOT here, as it admits no script execution.
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0 || sigversion == SigVersion::TAPSCRIPT);
+
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
@@ -389,15 +449,18 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
ConditionStack vfExec;
std::vector<valtype> altstack;
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
- if (script.size() > MAX_SCRIPT_SIZE)
+ if ((sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) && script.size() > MAX_SCRIPT_SIZE) {
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
+ }
int nOpCount = 0;
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
+ uint32_t opcode_pos = 0;
+ execdata.m_codeseparator_pos = 0xFFFFFFFFUL;
+ execdata.m_codeseparator_pos_init = true;
try
{
- while (pc < pend)
- {
+ for (; pc < pend; ++opcode_pos) {
bool fExec = vfExec.all_true();
//
@@ -408,9 +471,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
- // Note how OP_RESERVED does not count towards the opcode limit.
- if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT)
- return set_error(serror, SCRIPT_ERR_OP_COUNT);
+ if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
+ // Note how OP_RESERVED does not count towards the opcode limit.
+ if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
+ return set_error(serror, SCRIPT_ERR_OP_COUNT);
+ }
+ }
if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
@@ -568,6 +634,15 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
valtype& vch = stacktop(-1);
+ // Tapscript requires minimal IF/NOTIF inputs as a consensus rule.
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ // The input argument to the OP_IF and OP_NOTIF opcodes must be either
+ // exactly 0 (the empty vector) or exactly 1 (the one-byte vector with value 1).
+ if (vch.size() > 1 || (vch.size() == 1 && vch[0] != 1)) {
+ return set_error(serror, SCRIPT_ERR_TAPSCRIPT_MINIMALIF);
+ }
+ }
+ // Under witness v0 rules it is only a policy rule, enabled through SCRIPT_VERIFY_MINIMALIF.
if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
if (vch.size() > 1)
return set_error(serror, SCRIPT_ERR_MINIMALIF);
@@ -1001,6 +1076,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Hash starts after the code separator
pbegincodehash = pc;
+ execdata.m_codeseparator_pos = opcode_pos;
}
break;
@@ -1015,7 +1091,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
valtype& vchPubKey = stacktop(-1);
bool fSuccess = true;
- if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, flags, checker, sigversion, serror, fSuccess)) return false;
+ if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, fSuccess)) return false;
popstack(stack);
popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse);
@@ -1029,9 +1105,32 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;
+ case OP_CHECKSIGADD:
+ {
+ // OP_CHECKSIGADD is only available in Tapscript
+ if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
+
+ // (sig num pubkey -- num)
+ if (stack.size() < 3) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
+
+ const valtype& sig = stacktop(-3);
+ const CScriptNum num(stacktop(-2), fRequireMinimal);
+ const valtype& pubkey = stacktop(-1);
+
+ bool success = true;
+ if (!EvalChecksig(sig, pubkey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, success)) return false;
+ popstack(stack);
+ popstack(stack);
+ popstack(stack);
+ stack.push_back((num + (success ? 1 : 0)).getvch());
+ }
+ break;
+
case OP_CHECKMULTISIG:
case OP_CHECKMULTISIGVERIFY:
{
+ if (sigversion == SigVersion::TAPSCRIPT) return set_error(serror, SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG);
+
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
int i = 1;
@@ -1089,7 +1188,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
// Check signature
- bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
+ bool fOk = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
if (fOk) {
isig++;
@@ -1159,6 +1258,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
return set_success(serror);
}
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
+{
+ ScriptExecutionData execdata;
+ return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
+}
+
namespace {
/**
@@ -1258,65 +1363,216 @@ public:
}
};
+/** Compute the (single) SHA256 of the concatenation of all prevouts of a tx. */
template <class T>
-uint256 GetPrevoutHash(const T& txTo)
+uint256 GetPrevoutsSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.prevout;
}
- return ss.GetHash();
+ return ss.GetSHA256();
}
+/** Compute the (single) SHA256 of the concatenation of all nSequences of a tx. */
template <class T>
-uint256 GetSequenceHash(const T& txTo)
+uint256 GetSequencesSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.nSequence;
}
- return ss.GetHash();
+ return ss.GetSHA256();
}
+/** Compute the (single) SHA256 of the concatenation of all txouts of a tx. */
template <class T>
-uint256 GetOutputsHash(const T& txTo)
+uint256 GetOutputsSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : txTo.vout) {
ss << txout;
}
- return ss.GetHash();
+ return ss.GetSHA256();
+}
+
+/** Compute the (single) SHA256 of the concatenation of all amounts spent by a tx. */
+uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
+{
+ CHashWriter ss(SER_GETHASH, 0);
+ for (const auto& txout : outputs_spent) {
+ ss << txout.nValue;
+ }
+ return ss.GetSHA256();
}
+/** Compute the (single) SHA256 of the concatenation of all scriptPubKeys spent by a tx. */
+uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
+{
+ CHashWriter ss(SER_GETHASH, 0);
+ for (const auto& txout : outputs_spent) {
+ ss << txout.scriptPubKey;
+ }
+ return ss.GetSHA256();
+}
+
+
} // namespace
template <class T>
-void PrecomputedTransactionData::Init(const T& txTo)
+void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
{
- assert(!m_ready);
+ assert(!m_spent_outputs_ready);
+
+ m_spent_outputs = std::move(spent_outputs);
+ if (!m_spent_outputs.empty()) {
+ assert(m_spent_outputs.size() == txTo.vin.size());
+ m_spent_outputs_ready = true;
+ }
- // Cache is calculated only for transactions with witness
- if (txTo.HasWitness()) {
- hashPrevouts = GetPrevoutHash(txTo);
- hashSequence = GetSequenceHash(txTo);
- hashOutputs = GetOutputsHash(txTo);
+ // Determine which precomputation-impacting features this transaction uses.
+ bool uses_bip143_segwit = false;
+ bool uses_bip341_taproot = false;
+ for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
+ if (!txTo.vin[inpos].scriptWitness.IsNull()) {
+ if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
+ m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
+ // Treat every witness-bearing spend with 34-byte scriptPubKey that starts with OP_1 as a Taproot
+ // spend. This only works if spent_outputs was provided as well, but if it wasn't, actual validation
+ // will fail anyway. Note that this branch may trigger for scriptPubKeys that aren't actually segwit
+ // but in that case validation will fail as SCRIPT_ERR_WITNESS_UNEXPECTED anyway.
+ uses_bip341_taproot = true;
+ } else {
+ // Treat every spend that's not known to native witness v1 as a Witness v0 spend. This branch may
+ // also be taken for unknown witness versions, but it is harmless, and being precise would require
+ // P2SH evaluation to find the redeemScript.
+ uses_bip143_segwit = true;
+ }
+ }
+ if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all.
}
- m_ready = true;
+ if (uses_bip143_segwit || uses_bip341_taproot) {
+ // Computations shared between both sighash schemes.
+ m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
+ m_sequences_single_hash = GetSequencesSHA256(txTo);
+ m_outputs_single_hash = GetOutputsSHA256(txTo);
+ }
+ if (uses_bip143_segwit) {
+ hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
+ hashSequence = SHA256Uint256(m_sequences_single_hash);
+ hashOutputs = SHA256Uint256(m_outputs_single_hash);
+ m_bip143_segwit_ready = true;
+ }
+ if (uses_bip341_taproot) {
+ m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs);
+ m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs);
+ m_bip341_taproot_ready = true;
+ }
}
template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
- Init(txTo);
+ Init(txTo, {});
}
// explicit instantiation
-template void PrecomputedTransactionData::Init(const CTransaction& txTo);
-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
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");
+
+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)
+{
+ uint8_t ext_flag, key_version;
+ switch (sigversion) {
+ case SigVersion::TAPROOT:
+ ext_flag = 0;
+ // key_version is not used and left uninitialized.
+ break;
+ case SigVersion::TAPSCRIPT:
+ ext_flag = 1;
+ // key_version must be 0 for now, representing the current version of
+ // 32-byte public keys in the tapscript signature opcode execution.
+ // An upgradable public key version (with a size not 32-byte) may
+ // request a different key_version with a new sigversion.
+ key_version = 0;
+ break;
+ default:
+ assert(false);
+ }
+ assert(in_pos < tx_to.vin.size());
+ assert(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready);
+
+ CHashWriter ss = HASHER_TAPSIGHASH;
+
+ // Epoch
+ static constexpr uint8_t EPOCH = 0;
+ ss << EPOCH;
+
+ // Hash type
+ const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL
+ const uint8_t input_type = hash_type & SIGHASH_INPUT_MASK;
+ if (!(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83))) return false;
+ ss << hash_type;
+
+ // Transaction level data
+ ss << tx_to.nVersion;
+ ss << tx_to.nLockTime;
+ if (input_type != SIGHASH_ANYONECANPAY) {
+ ss << cache.m_prevouts_single_hash;
+ ss << cache.m_spent_amounts_single_hash;
+ ss << cache.m_spent_scripts_single_hash;
+ ss << cache.m_sequences_single_hash;
+ }
+ if (output_type == SIGHASH_ALL) {
+ ss << cache.m_outputs_single_hash;
+ }
+
+ // Data about the input/prevout being spent
+ assert(execdata.m_annex_init);
+ const bool have_annex = execdata.m_annex_present;
+ const uint8_t spend_type = (ext_flag << 1) + (have_annex ? 1 : 0); // The low bit indicates whether an annex is present.
+ ss << spend_type;
+ if (input_type == SIGHASH_ANYONECANPAY) {
+ ss << tx_to.vin[in_pos].prevout;
+ ss << cache.m_spent_outputs[in_pos];
+ ss << tx_to.vin[in_pos].nSequence;
+ } else {
+ ss << in_pos;
+ }
+ if (have_annex) {
+ ss << execdata.m_annex_hash;
+ }
+
+ // Data about the output (if only one).
+ if (output_type == SIGHASH_SINGLE) {
+ if (in_pos >= tx_to.vout.size()) return false;
+ CHashWriter sha_single_output(SER_GETHASH, 0);
+ sha_single_output << tx_to.vout[in_pos];
+ ss << sha_single_output.GetSHA256();
+ }
+
+ // Additional data for BIP 342 signatures
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ assert(execdata.m_tapleaf_hash_init);
+ ss << execdata.m_tapleaf_hash;
+ ss << key_version;
+ assert(execdata.m_codeseparator_pos_init);
+ ss << execdata.m_codeseparator_pos;
+ }
+
+ hash_out = ss.GetSHA256();
+ return true;
+}
+
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
@@ -1326,19 +1582,19 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
- const bool cacheready = cache && cache->m_ready;
+ const bool cacheready = cache && cache->m_bip143_segwit_ready;
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
- hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
+ hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo));
}
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
- hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
+ hashSequence = cacheready ? cache->hashSequence : SHA256Uint256(GetSequencesSHA256(txTo));
}
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
- hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
+ hashOutputs = cacheready ? cache->hashOutputs : SHA256Uint256(GetOutputsSHA256(txTo));
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn];
@@ -1372,7 +1628,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
if (nIn >= txTo.vout.size()) {
// nOut out of range
- return UINT256_ONE();
+ return uint256::ONE;
}
}
@@ -1386,13 +1642,19 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
}
template <class T>
-bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+bool GenericTransactionSignatureChecker<T>::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}
template <class T>
-bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+bool GenericTransactionSignatureChecker<T>::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
+{
+ return pubkey.VerifySchnorr(sighash, sig);
+}
+
+template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
@@ -1407,13 +1669,41 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
- if (!VerifySignature(vchSig, pubkey, sighash))
+ if (!VerifyECDSASignature(vchSig, pubkey, sighash))
return false;
return true;
}
template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const
+{
+ assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
+ // Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
+ assert(pubkey_in.size() == 32);
+ // Note that in Tapscript evaluation, empty signatures are treated specially (invalid signature that does not
+ // abort script execution). This is implemented in EvalChecksigTapscript, which won't invoke
+ // CheckSchnorrSignature in that case. In other contexts, they are invalid like every other signature with
+ // size different from 64 or 65.
+ if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
+
+ XOnlyPubKey pubkey{pubkey_in};
+
+ uint8_t hashtype = SIGHASH_DEFAULT;
+ if (sig.size() == 65) {
+ hashtype = SpanPopBack(sig);
+ if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ uint256 sighash;
+ assert(this->txdata);
+ if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata)) {
+ return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
+ }
+ if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
+ return true;
+}
+
+template <class T>
bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
{
// There are two kinds of nLockTime: lock-by-blockheight
@@ -1501,17 +1791,39 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;
-static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror)
+static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
{
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ // OP_SUCCESSx processing overrides everything, including stack element size limits
+ CScript::const_iterator pc = scriptPubKey.begin();
+ while (pc < scriptPubKey.end()) {
+ opcodetype opcode;
+ if (!scriptPubKey.GetOp(pc, opcode)) {
+ // Note how this condition would not be reached if an unknown OP_SUCCESSx was found
+ return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
+ }
+ // New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
+ if (IsOpSuccess(opcode)) {
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
+ }
+ return set_success(serror);
+ }
+ }
+
+ // Tapscript enforces initial stack size limits (altstack is empty here)
+ if (stack.size() > MAX_STACK_SIZE) return set_error(serror, SCRIPT_ERR_STACK_SIZE);
+ }
+
// Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
for (const valtype& elem : stack) {
if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
}
// Run the script interpreter.
- if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror)) return false;
+ if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, execdata, serror)) return false;
// Scripts inside witness implicitly require cleanstack behaviour
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
@@ -1519,40 +1831,104 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
return true;
}
-static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
+static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
+{
+ const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
+ const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
+ const XOnlyPubKey q{uint256(program)};
+ tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
+ uint256 k = tapleaf_hash;
+ for (int i = 0; i < path_len; ++i) {
+ CHashWriter ss_branch{HASHER_TAPBRANCH};
+ Span<const unsigned char> node(control.data() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE);
+ if (std::lexicographical_compare(k.begin(), k.end(), node.begin(), node.end())) {
+ ss_branch << k << node;
+ } else {
+ ss_branch << node << k;
+ }
+ k = ss_branch.GetSHA256();
+ }
+ k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
+ return q.CheckPayToContract(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)
{
- CScript scriptPubKey;
+ CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
Span<const valtype> stack{witness.stack};
+ ScriptExecutionData execdata;
if (witversion == 0) {
if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
- // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness
+ // BIP141 P2WSH: 32-byte witness v0 program (which encodes SHA256(script))
if (stack.size() == 0) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
}
const valtype& script_bytes = SpanPopBack(stack);
- scriptPubKey = CScript(script_bytes.begin(), script_bytes.end());
- uint256 hashScriptPubKey;
- CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
- if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
+ 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());
+ if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
- return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
} else if (program.size() == WITNESS_V0_KEYHASH_SIZE) {
- // Special case for pay-to-pubkeyhash; signature + pubkey in witness
+ // BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey))
if (stack.size() != 2) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness
}
- scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
- return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
+ exec_script << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
} else {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
}
+ } else if (witversion == 1 && program.size() == WITNESS_V1_TAPROOT_SIZE && !is_p2sh) {
+ // BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
+ if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
+ if (stack.size() == 0) return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
+ if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
+ // Drop annex (this is non-standard; see IsWitnessStandard)
+ const valtype& annex = SpanPopBack(stack);
+ execdata.m_annex_hash = (CHashWriter(SER_GETHASH, 0) << annex).GetSHA256();
+ execdata.m_annex_present = true;
+ } else {
+ execdata.m_annex_present = false;
+ }
+ execdata.m_annex_init = true;
+ if (stack.size() == 1) {
+ // Key path spending (stack size is 1 after removing optional annex)
+ if (!checker.CheckSchnorrSignature(stack.front(), program, SigVersion::TAPROOT, execdata, serror)) {
+ return false; // serror is set
+ }
+ return set_success(serror);
+ } else {
+ // Script path spending (stack size is >1 after removing optional annex)
+ const valtype& control = SpanPopBack(stack);
+ const valtype& script_bytes = SpanPopBack(stack);
+ exec_script = CScript(script_bytes.begin(), script_bytes.end());
+ if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) {
+ return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
+ }
+ if (!VerifyTaprootCommitment(control, program, exec_script, execdata.m_tapleaf_hash)) {
+ return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
+ }
+ execdata.m_tapleaf_hash_init = true;
+ if ((control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
+ // Tapscript (leaf version 0xc0)
+ execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET;
+ execdata.m_validation_weight_left_init = true;
+ return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);
+ }
+ if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
+ return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION);
+ }
+ return set_success(serror);
+ }
} else {
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
}
- // Higher version witness scripts return true for future softfork compatibility
+ // Other version/size/p2sh combinations return true for future softfork compatibility
return true;
}
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
@@ -1598,7 +1974,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ false)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
@@ -1643,7 +2019,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ true)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 71f2436369..c0c2b012c6 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -7,14 +7,17 @@
#define BITCOIN_SCRIPT_INTERPRETER_H
#include <script/script_error.h>
+#include <span.h>
#include <primitives/transaction.h>
#include <vector>
#include <stdint.h>
class CPubKey;
+class XOnlyPubKey;
class CScript;
class CTransaction;
+class CTxOut;
class uint256;
/** Signature hash types/flags */
@@ -24,6 +27,10 @@ enum
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,
+
+ SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL
+ SIGHASH_OUTPUT_MASK = 3,
+ SIGHASH_INPUT_MASK = 0x80,
};
/** Script verification flags.
@@ -79,6 +86,8 @@ enum
// "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
// (BIP62 rule 6)
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
+ // Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
+ // consensus rules. It is automatic there and does not need this flag.
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
// Verify CHECKLOCKTIMEVERIFY
@@ -101,6 +110,8 @@ enum
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
//
+ // Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
+ // rules. It is automatic there and does not depend on this flag.
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
@@ -114,19 +125,49 @@ enum
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
//
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
+
+ // Taproot/Tapscript validation (BIPs 341 & 342)
+ //
+ SCRIPT_VERIFY_TAPROOT = (1U << 17),
+
+ // Making unknown Taproot leaf versions non-standard
+ //
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
+
+ // Making unknown OP_SUCCESS non-standard
+ SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
+
+ // Making unknown public key versions (in BIP 342 scripts) non-standard
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
struct PrecomputedTransactionData
{
+ // BIP341 precomputed data.
+ // These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-15.
+ uint256 m_prevouts_single_hash;
+ uint256 m_sequences_single_hash;
+ uint256 m_outputs_single_hash;
+ uint256 m_spent_amounts_single_hash;
+ uint256 m_spent_scripts_single_hash;
+ //! Whether the 5 fields above are initialized.
+ bool m_bip341_taproot_ready = false;
+
+ // BIP143 precomputed data (double-SHA256).
uint256 hashPrevouts, hashSequence, hashOutputs;
- bool m_ready = false;
+ //! Whether the 3 fields above are initialized.
+ bool m_bip143_segwit_ready = false;
+
+ std::vector<CTxOut> m_spent_outputs;
+ //! Whether m_spent_outputs is initialized.
+ bool m_spent_outputs_ready = false;
PrecomputedTransactionData() = default;
template <class T>
- void Init(const T& tx);
+ void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
@@ -134,13 +175,48 @@ struct PrecomputedTransactionData
enum class SigVersion
{
- BASE = 0,
- WITNESS_V0 = 1,
+ BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts
+ WITNESS_V0 = 1, //!< Witness v0 (P2WPKH and P2WSH); see BIP 141
+ TAPROOT = 2, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
+ TAPSCRIPT = 3, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending, leaf version 0xc0; see BIP 342
+};
+
+struct ScriptExecutionData
+{
+ //! Whether m_tapleaf_hash is initialized.
+ bool m_tapleaf_hash_init = false;
+ //! The tapleaf hash.
+ uint256 m_tapleaf_hash;
+
+ //! Whether m_codeseparator_pos is initialized.
+ bool m_codeseparator_pos_init = false;
+ //! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed).
+ uint32_t m_codeseparator_pos;
+
+ //! Whether m_annex_present and (when needed) m_annex_hash are initialized.
+ bool m_annex_init = false;
+ //! Whether an annex is present.
+ bool m_annex_present;
+ //! Hash of the annex data.
+ uint256 m_annex_hash;
+
+ //! Whether m_validation_weight_left is initialized.
+ bool m_validation_weight_left_init = false;
+ //! How much validation weight is left (decremented for every successful non-empty signature check).
+ int64_t m_validation_weight_left;
};
/** Signature hash sizes */
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
+static constexpr size_t WITNESS_V1_TAPROOT_SIZE = 32;
+
+static constexpr uint8_t TAPROOT_LEAF_MASK = 0xfe;
+static constexpr uint8_t TAPROOT_LEAF_TAPSCRIPT = 0xc0;
+static constexpr size_t TAPROOT_CONTROL_BASE_SIZE = 33;
+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;
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);
@@ -148,7 +224,12 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
class BaseSignatureChecker
{
public:
- virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+ virtual bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+ {
+ return false;
+ }
+
+ virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
{
return false;
}
@@ -176,12 +257,14 @@ private:
const PrecomputedTransactionData* txdata;
protected:
- virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
+ virtual bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
+ 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) {}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
+ 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;
bool CheckSequence(const CScriptNum& nSequence) const override;
};
@@ -189,6 +272,7 @@ public:
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
+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);
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 92c6fe7785..f31472e42d 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -140,6 +140,9 @@ std::string GetOpName(opcodetype opcode)
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";
+ // Opcode added by BIP 342 (Tapscript)
+ case OP_CHECKSIGADD : return "OP_CHECKSIGADD";
+
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
@@ -328,3 +331,11 @@ bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator en
opcodeRet = static_cast<opcodetype>(opcode);
return true;
}
+
+bool IsOpSuccess(const opcodetype& opcode)
+{
+ return opcode == 80 || opcode == 98 || (opcode >= 126 && opcode <= 129) ||
+ (opcode >= 131 && opcode <= 134) || (opcode >= 137 && opcode <= 138) ||
+ (opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 153) ||
+ (opcode >= 187 && opcode <= 254);
+}
diff --git a/src/script/script.h b/src/script/script.h
index c1f2b66921..974cde4984 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -44,6 +44,17 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20
// SEQUENCE_FINAL).
static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
+// Tag for input annex. If there are at least two witness elements for a transaction input,
+// and the first byte of the last element is 0x50, this last element is called annex, and
+// has meanings independent of the script
+static constexpr unsigned int ANNEX_TAG = 0x50;
+
+// Validation weight per passing signature (Tapscript only, see BIP 342).
+static constexpr uint64_t VALIDATION_WEIGHT_PER_SIGOP_PASSED = 50;
+
+// How much weight budget is added to the witness size (Tapscript only, see BIP 342).
+static constexpr uint64_t VALIDATION_WEIGHT_OFFSET = 50;
+
template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
@@ -187,6 +198,9 @@ enum opcodetype
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
+ // Opcode added by BIP 342 (Tapscript)
+ OP_CHECKSIGADD = 0xba,
+
OP_INVALIDOPCODE = 0xff,
};
@@ -555,4 +569,7 @@ struct CScriptWitness
std::string ToString() const;
};
+/** Test for OP_SUCCESSx opcodes as defined by BIP342. */
+bool IsOpSuccess(const opcodetype& opcode);
+
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 69e14803f1..fadc04262c 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -73,10 +73,16 @@ std::string ScriptErrorString(const ScriptError serror)
return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:
return "Witness version reserved for soft-fork upgrades";
+ case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION:
+ return "Taproot version reserved for soft-fork upgrades";
+ case SCRIPT_ERR_DISCOURAGE_OP_SUCCESS:
+ return "OP_SUCCESSx reserved for soft-fork upgrades";
+ case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE:
+ return "Public key version reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed";
case SCRIPT_ERR_CLEANSTACK:
- return "Extra items left on stack after execution";
+ return "Stack size must be exactly one after execution";
case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH:
return "Witness program has incorrect length";
case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY:
@@ -91,6 +97,20 @@ std::string ScriptErrorString(const ScriptError serror)
return "Witness provided for non-witness script";
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
return "Using non-compressed keys in segwit";
+ case SCRIPT_ERR_SCHNORR_SIG_SIZE:
+ return "Invalid Schnorr signature size";
+ case SCRIPT_ERR_SCHNORR_SIG_HASHTYPE:
+ return "Invalid Schnorr signature hash type";
+ case SCRIPT_ERR_SCHNORR_SIG:
+ return "Invalid Schnorr signature";
+ case SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE:
+ return "Invalid Taproot control block size";
+ case SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT:
+ return "Too much signature validation relative to witness weight";
+ case SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG:
+ return "OP_CHECKMULTISIG(VERIFY) is not available in tapscript";
+ case SCRIPT_ERR_TAPSCRIPT_MINIMALIF:
+ return "OP_IF/NOTIF argument must be minimal in tapscript";
case SCRIPT_ERR_OP_CODESEPARATOR:
return "Using OP_CODESEPARATOR in non-witness script";
case SCRIPT_ERR_SIG_FINDANDDELETE:
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 2978c147e1..b071681613 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -56,6 +56,9 @@ typedef enum ScriptError_t
/* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
+ SCRIPT_ERR_DISCOURAGE_OP_SUCCESS,
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
/* segregated witness */
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH,
@@ -66,6 +69,15 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_UNEXPECTED,
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
+ /* Taproot */
+ SCRIPT_ERR_SCHNORR_SIG_SIZE,
+ SCRIPT_ERR_SCHNORR_SIG_HASHTYPE,
+ SCRIPT_ERR_SCHNORR_SIG,
+ SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE,
+ SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT,
+ SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG,
+ SCRIPT_ERR_TAPSCRIPT_MINIMALIF,
+
/* Constant scriptCode */
SCRIPT_ERR_OP_CODESEPARATOR,
SCRIPT_ERR_SIG_FINDANDDELETE,
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index aaecab1ef2..c1786140de 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -22,8 +22,9 @@ namespace {
class CSignatureCache
{
private:
- //! Entries are SHA256(nonce || signature hash || public key || signature):
- CSHA256 m_salted_hasher;
+ //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
+ CSHA256 m_salted_hasher_ecdsa;
+ CSHA256 m_salted_hasher_schnorr;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
@@ -34,18 +35,30 @@ public:
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
// this chunk, which makes later hash computations more efficient. We
- // just write our 32-byte entropy twice to fill the 64 bytes.
- m_salted_hasher.Write(nonce.begin(), 32);
- m_salted_hasher.Write(nonce.begin(), 32);
+ // just write our 32-byte entropy, and then pad with 'E' for ECDSA and
+ // 'S' for Schnorr (followed by 0 bytes).
+ static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
+ static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
+ m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
+ m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
+ m_salted_hasher_schnorr.Write(nonce.begin(), 32);
+ m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
}
void
- ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
+ ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
{
- CSHA256 hasher = m_salted_hasher;
+ CSHA256 hasher = m_salted_hasher_ecdsa;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], 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());
+ }
+
bool
Get(const uint256& entry, const bool erase)
{
@@ -85,15 +98,25 @@ void InitSignatureCache()
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}
-bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
- signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
+ signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
if (signatureCache.Get(entry, !store))
return true;
- if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
+ if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
return false;
if (store)
signatureCache.Set(entry);
return true;
}
+
+bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
+{
+ uint256 entry;
+ signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
+ if (signatureCache.Get(entry, !store)) return true;
+ if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
+ if (store) signatureCache.Set(entry);
+ return true;
+}
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 807b61b542..00534f9758 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -7,6 +7,7 @@
#define BITCOIN_SCRIPT_SIGCACHE_H
#include <script/interpreter.h>
+#include <span.h>
#include <vector>
@@ -48,7 +49,8 @@ private:
public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
- bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
+ 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;
};
void InitSignatureCache();
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index f425215549..0e6864d547 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -111,6 +111,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT:
return false;
case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
@@ -186,6 +187,8 @@ static CScript PushAll(const std::vector<valtype>& values)
result << OP_0;
} else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) {
result << CScript::EncodeOP_N(v[0]);
+ } else if (v.size() == 1 && v[0] == 0x81) {
+ result << OP_1NEGATE;
} else {
result << v;
}
@@ -258,9 +261,9 @@ private:
public:
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
{
- if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
+ if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) {
CPubKey pubkey(vchPubKey);
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
return true;
@@ -337,7 +340,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
const valtype& pubkey = solutions[i+1];
// We either have a signature for this pubkey, or we have found a signature and it is valid
- if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
+ if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckECDSASignature(sig, pubkey, next_script, sigversion)) {
last_success_key = i + 1;
break;
}
@@ -398,7 +401,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
{
public:
DummySignatureChecker() {}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
+ bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
};
const DummySignatureChecker DUMMY_CHECKER;
diff --git a/src/script/sign.h b/src/script/sign.h
index b77d26c0d7..a1cfe1574d 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -11,6 +11,7 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
+#include <span.h>
#include <streams.h>
class CKey;
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 3a4882f280..f2f81664f6 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -55,6 +55,7 @@ std::string GetTxnOutputType(TxoutType t)
case TxoutType::NULL_DATA: return "nulldata";
case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
+ case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
} // no default case, so the compiler can warn about missing cases
assert(false);
@@ -130,6 +131,11 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
vSolutionsRet.push_back(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;
+ }
if (witnessversion != 0) {
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
@@ -203,7 +209,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TxoutType::WITNESS_UNKNOWN) {
+ } else if (whichType == TxoutType::WITNESS_UNKNOWN || whichType == TxoutType::WITNESS_V1_TAPROOT) {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -313,18 +319,6 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
return script;
}
-CScript GetScriptForWitness(const CScript& redeemscript)
-{
- std::vector<std::vector<unsigned char> > vSolutions;
- TxoutType typ = Solver(redeemscript, vSolutions);
- if (typ == TxoutType::PUBKEY) {
- return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0])));
- } else if (typ == TxoutType::PUBKEYHASH) {
- return GetScriptForDestination(WitnessV0KeyHash(uint160{vSolutions[0]}));
- }
- return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
-}
-
bool IsValidDestination(const CTxDestination& dest) {
return dest.which() != 0;
}
diff --git a/src/script/standard.h b/src/script/standard.h
index 992e37675f..721203385e 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -129,6 +129,7 @@ enum class TxoutType {
NULL_DATA, //!< unspendable OP_RETURN script that carries data
WITNESS_V0_SCRIPTHASH,
WITNESS_V0_KEYHASH,
+ WITNESS_V1_TAPROOT,
WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
@@ -206,7 +207,8 @@ 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 destination (P2W???)
+ * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
+ * (taproot outputs do not require their own type as long as no wallet support exists)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
@@ -263,14 +265,4 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
-/**
- * Generate a pay-to-witness script for the given redeem script. If the redeem
- * script is P2PK or P2PKH, this returns a P2WPKH script, otherwise it returns a
- * P2WSH script.
- *
- * TODO: replace calls to GetScriptForWitness with GetScriptForDestination using
- * the various witness-specific CTxDestination subtypes.
- */
-CScript GetScriptForWitness(const CScript& redeemscript);
-
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index cb4331aa90..ccdef02b29 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -1,9 +1,9 @@
bench_inv
bench_ecdh
bench_ecmult
+bench_schnorrsig
bench_sign
bench_verify
-bench_schnorr_verify
bench_recover
bench_internal
tests
@@ -31,6 +31,8 @@ libtool
*.lo
*.o
*~
+*.log
+*.trs
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index a6ad6fb27e..bcc8c210f5 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -17,33 +17,29 @@ compiler:
- gcc
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
+ - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
matrix:
- - SCALAR=32bit RECOVERY=yes
- - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- - SCALAR=64bit
- - FIELD=64bit RECOVERY=yes
- - FIELD=64bit ENDOMORPHISM=yes
- - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- - FIELD=64bit ASM=x86_64
- - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- - FIELD=32bit ENDOMORPHISM=yes
+ - WIDEMUL=int64 RECOVERY=yes
+ - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int128
+ - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int128 ASM=x86_64
- BIGNUM=no
- - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
+ - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- - BUILD=distcheck CTIMETEST= BENCH=
+ - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
- CPPFLAGS=-DDETERMINISTIC
- - CFLAGS=-O0 CTIMETEST=
+ - CFLAGS=-O0 CTIMETEST=no
- ECMULTGENPRECISION=2
- ECMULTGENPRECISION=8
- - VALGRIND=yes ENDOMORPHISM=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
- - VALGRIND=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" CPPFLAGS=-DVALGRIND BUILD=
+ - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes EXTRAFLAGS="--disable-openssl-tests" BUILD=
matrix:
fast_finish: true
include:
- compiler: clang
os: linux
- env: HOST=i686-linux-gnu ENDOMORPHISM=yes
+ env: HOST=i686-linux-gnu
addons:
apt:
packages:
@@ -63,7 +59,7 @@ matrix:
- libtool-bin
- libc6-dbg:i386
- compiler: gcc
- env: HOST=i686-linux-gnu ENDOMORPHISM=yes
+ env: HOST=i686-linux-gnu
os: linux
addons:
apt:
@@ -83,6 +79,10 @@ matrix:
- valgrind
- libtool-bin
- libc6-dbg:i386
+ # S390x build (big endian system)
+ - compiler: gcc
+ env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes CTIMETEST=
+ arch: s390x
# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index d8c1c79e8c..023fa6067f 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -34,9 +34,11 @@ noinst_HEADERS += src/field_5x52.h
noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
+noinst_HEADERS += src/assumptions.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/scratch.h
noinst_HEADERS += src/scratch_impl.h
+noinst_HEADERS += src/selftest.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
@@ -99,7 +101,7 @@ if VALGRIND_ENABLED
tests_CPPFLAGS += -DVALGRIND
noinst_PROGRAMS += valgrind_ctime_test
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
-valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_LIBS) $(COMMON_LIB)
endif
if !ENABLE_COVERAGE
tests_CPPFLAGS += -DVERIFY
@@ -152,3 +154,11 @@ endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
+
+if ENABLE_MODULE_EXTRAKEYS
+include src/modules/extrakeys/Makefile.am.include
+endif
+
+if ENABLE_MODULE_SCHNORRSIG
+include src/modules/schnorrsig/Makefile.am.include
+endif
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 434178b372..2602475787 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -48,7 +48,7 @@ Implementation details
* Use wNAF notation for point multiplicands.
* Use a much larger window for multiples of G, using precomputed multiples.
* Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
- * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
+ * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
* Point multiplication for signing
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
* Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
diff --git a/src/secp256k1/TODO b/src/secp256k1/TODO
deleted file mode 100644
index a300e1c5eb..0000000000
--- a/src/secp256k1/TODO
+++ /dev/null
@@ -1,3 +0,0 @@
-* Unit tests for fieldelem/groupelem, including ones intended to
- trigger fieldelem's boundary cases.
-* Complete constant-time operations for signing/keygen
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index 1b2b71e6ab..57595f4499 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -1,8 +1,3 @@
-dnl libsecp25k1 helper checks
-AC_DEFUN([SECP_INT128_CHECK],[
-has_int128=$ac_cv_type___int128
-])
-
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
AC_MSG_CHECKING(for x86_64 assembly availability)
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 6021b760b5..5a078e6c81 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -67,7 +67,7 @@ esac
CFLAGS="-W $CFLAGS"
-warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
+warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
saved_CFLAGS="$CFLAGS"
CFLAGS="$warn_CFLAGS $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
@@ -116,11 +116,6 @@ AC_ARG_ENABLE(exhaustive_tests,
[use_exhaustive_tests=$enableval],
[use_exhaustive_tests=yes])
-AC_ARG_ENABLE(endomorphism,
- AS_HELP_STRING([--enable-endomorphism],[enable endomorphism [default=no]]),
- [use_endomorphism=$enableval],
- [use_endomorphism=no])
-
AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]),
[use_ecmult_static_precomputation=$enableval],
@@ -136,28 +131,35 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
+AC_ARG_ENABLE(module_extrakeys,
+ AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]),
+ [enable_module_extrakeys=$enableval],
+ [enable_module_extrakeys=no])
+
+AC_ARG_ENABLE(module_schnorrsig,
+ AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
+ [enable_module_schnorrsig=$enableval],
+ [enable_module_schnorrsig=no])
+
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
[use_external_default_callbacks=$enableval],
[use_external_default_callbacks=no])
-AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
-[finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto])
+dnl Test-only override of the (autodetected by the C code) "widemul" setting.
+dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
+AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
-AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
-[scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto])
-
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
-[The table will store 2^(SIZE-2) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
-[If the endomorphism optimization is enabled, two tables of this size are used instead of only one.]
+[The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
)],
[req_ecmult_window=$withval], [req_ecmult_window=auto])
@@ -170,9 +172,21 @@ AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision
)],
[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
-AC_CHECK_TYPES([__int128])
+AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
+[Build with extra checks for running inside Valgrind [default=auto]]
+)],
+[req_valgrind=$withval], [req_valgrind=auto])
-AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [enable_valgrind=no], [])
+if test x"$req_valgrind" = x"no"; then
+ enable_valgrind=no
+else
+ AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [
+ if test x"$req_valgrind" = x"yes"; then
+ AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
+ fi
+ enable_valgrind=no
+ ], [])
+fi
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
if test x"$enable_coverage" = x"yes"; then
@@ -265,63 +279,6 @@ else
esac
fi
-if test x"$req_field" = x"auto"; then
- if test x"set_asm" = x"x86_64"; then
- set_field=64bit
- fi
- if test x"$set_field" = x; then
- SECP_INT128_CHECK
- if test x"$has_int128" = x"yes"; then
- set_field=64bit
- fi
- fi
- if test x"$set_field" = x; then
- set_field=32bit
- fi
-else
- set_field=$req_field
- case $set_field in
- 64bit)
- if test x"$set_asm" != x"x86_64"; then
- SECP_INT128_CHECK
- if test x"$has_int128" != x"yes"; then
- AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available])
- fi
- fi
- ;;
- 32bit)
- ;;
- *)
- AC_MSG_ERROR([invalid field implementation selection])
- ;;
- esac
-fi
-
-if test x"$req_scalar" = x"auto"; then
- SECP_INT128_CHECK
- if test x"$has_int128" = x"yes"; then
- set_scalar=64bit
- fi
- if test x"$set_scalar" = x; then
- set_scalar=32bit
- fi
-else
- set_scalar=$req_scalar
- case $set_scalar in
- 64bit)
- SECP_INT128_CHECK
- if test x"$has_int128" != x"yes"; then
- AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available])
- fi
- ;;
- 32bit)
- ;;
- *)
- AC_MSG_ERROR([invalid scalar implementation selected])
- ;;
- esac
-fi
-
if test x"$req_bignum" = x"auto"; then
SECP_GMP_CHECK
if test x"$has_gmp" = x"yes"; then
@@ -365,16 +322,18 @@ no)
;;
esac
-# select field implementation
-case $set_field in
-64bit)
- AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation])
+# select wide multiplication implementation
+case $set_widemul in
+int128)
+ AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation])
+ ;;
+int64)
+ AC_DEFINE(USE_FORCE_WIDEMUL_INT64, 1, [Define this symbol to force the use of the (u)int64_t based wide multiplication implementation])
;;
-32bit)
- AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation])
+auto)
;;
*)
- AC_MSG_ERROR([invalid field implementation])
+ AC_MSG_ERROR([invalid wide multiplication implementation])
;;
esac
@@ -396,19 +355,6 @@ no)
;;
esac
-#select scalar implementation
-case $set_scalar in
-64bit)
- AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation])
- ;;
-32bit)
- AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation])
- ;;
-*)
- AC_MSG_ERROR([invalid scalar implementation])
- ;;
-esac
-
#set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
@@ -477,10 +423,6 @@ if test x"$set_bignum" = x"gmp"; then
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
fi
-if test x"$use_endomorphism" = x"yes"; then
- AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
-fi
-
if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi
@@ -493,7 +435,16 @@ if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
-AC_C_BIGENDIAN()
+if test x"$enable_module_schnorrsig" = x"yes"; then
+ AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
+ enable_module_extrakeys=yes
+fi
+
+# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
+# module to set enable_module_extrakeys=yes
+if test x"$enable_module_extrakeys" = x"yes"; then
+ AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
+fi
if test x"$use_external_asm" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
@@ -508,11 +459,19 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
+ AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
+ AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
+ if test x"$enable_module_extrakeys" = x"yes"; then
+ AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.])
+ fi
+ if test x"$enable_module_schnorrsig" = x"yes"; then
+ AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
+ fi
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
@@ -531,6 +490,8 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
+AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
+AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -543,20 +504,23 @@ AC_OUTPUT
echo
echo "Build Options:"
-echo " with endomorphism = $use_endomorphism"
echo " with ecmult precomp = $set_precomp"
echo " with external callbacks = $use_external_default_callbacks"
echo " with benchmarks = $use_benchmark"
echo " with coverage = $enable_coverage"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
+echo " module extrakeys = $enable_module_extrakeys"
+echo " module schnorrsig = $enable_module_schnorrsig"
echo
echo " asm = $set_asm"
echo " bignum = $set_bignum"
-echo " field = $set_field"
-echo " scalar = $set_scalar"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
+dnl Hide test-only options unless they're used.
+if test x"$set_widemul" != xauto; then
+echo " wide multiplication = $set_widemul"
+fi
echo
echo " valgrind = $enable_valgrind"
echo " CC = $CC"
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index e177a0562d..f71db4b535 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -112,7 +112,6 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
return 0;
}
spos = pos;
- pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/contrib/travis.sh
index 3909d16a27..24cc9315cb 100755
--- a/src/secp256k1/contrib/travis.sh
+++ b/src/secp256k1/contrib/travis.sh
@@ -3,10 +3,6 @@
set -e
set -x
-if [ -n "$HOST" ]
-then
- export USE_HOST="--host=$HOST"
-fi
if [ "$HOST" = "i686-linux-gnu" ]
then
export CC="$CC -m32"
@@ -17,25 +13,28 @@ then
fi
./configure \
- --enable-experimental="$EXPERIMENTAL" --enable-endomorphism="$ENDOMORPHISM" \
- --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \
+ --enable-experimental="$EXPERIMENTAL" \
+ --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
- --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" "$EXTRAFLAGS" "$USE_HOST"
+ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
+ --enable-module-schnorrsig="$SCHNORRSIG" \
+ --with-valgrind="$WITH_VALGRIND" \
+ --host="$HOST" $EXTRAFLAGS
if [ -n "$BUILD" ]
then
make -j2 "$BUILD"
fi
-if [ -n "$VALGRIND" ]
+if [ "$RUN_VALGRIND" = "yes" ]
then
make -j2
# the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html)
valgrind --error-exitcode=42 ./tests 16
valgrind --error-exitcode=42 ./exhaustive_tests
fi
-if [ -n "$BENCH" ]
+if [ "$BENCH" = "yes" ]
then
- if [ -n "$VALGRIND" ]
+ if [ "$RUN_VALGRIND" = "yes" ]
then
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
EXEC='./libtool --mode=execute valgrind --error-exitcode=42'
@@ -58,8 +57,12 @@ then
then
$EXEC ./bench_ecdh >> bench.log 2>&1
fi
+ if [ "$SCHNORRSIG" = "yes" ]
+ then
+ $EXEC ./bench_schnorrsig >> bench.log 2>&1
+ fi
fi
-if [ -n "$CTIMETEST" ]
+if [ "$CTIMETEST" = "yes" ]
then
./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
fi
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 2ba2dca388..2178c8e2d6 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -134,7 +134,7 @@ typedef int (*secp256k1_nonce_function)(
# else
# define SECP256K1_API
# endif
-# elif defined(__GNUC__) && defined(SECP256K1_BUILD)
+# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
# define SECP256K1_API __attribute__ ((visibility ("default")))
# else
# define SECP256K1_API
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
new file mode 100644
index 0000000000..0c5dff2c94
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -0,0 +1,236 @@
+#ifndef SECP256K1_EXTRAKEYS_H
+#define SECP256K1_EXTRAKEYS_H
+
+#include "secp256k1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Opaque data structure that holds a parsed and valid "x-only" public key.
+ * An x-only pubkey encodes a point whose Y coordinate is even. It is
+ * serialized using only its X coordinate (32 bytes). See BIP-340 for more
+ * information about x-only pubkeys.
+ *
+ * The exact representation of data inside is implementation defined and not
+ * guaranteed to be portable between different platforms or versions. It is
+ * however guaranteed to be 64 bytes in size, and can be safely copied/moved.
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use secp256k1_xonly_pubkey_serialize and
+ * secp256k1_xonly_pubkey_parse.
+ */
+typedef struct {
+ unsigned char data[64];
+} secp256k1_xonly_pubkey;
+
+/** Opaque data structure that holds a keypair consisting of a secret and a
+ * public key.
+ *
+ * The exact representation of data inside is implementation defined and not
+ * guaranteed to be portable between different platforms or versions. It is
+ * however guaranteed to be 96 bytes in size, and can be safely copied/moved.
+ */
+typedef struct {
+ unsigned char data[96];
+} secp256k1_keypair;
+
+/** Parse a 32-byte sequence into a xonly_pubkey object.
+ *
+ * Returns: 1 if the public key was fully valid.
+ * 0 if the public key could not be parsed or is invalid.
+ *
+ * Args: ctx: a secp256k1 context object (cannot be NULL).
+ * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
+ * parsed version of input. If not, it's set to an invalid value.
+ * (cannot be NULL).
+ * In: input32: pointer to a serialized xonly_pubkey (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey* pubkey,
+ const unsigned char *input32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Serialize an xonly_pubkey object into a 32-byte sequence.
+ *
+ * Returns: 1 always.
+ *
+ * Args: ctx: a secp256k1 context object (cannot be NULL).
+ * Out: output32: a pointer to a 32-byte array to place the serialized key in
+ * (cannot be NULL).
+ * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an
+ * initialized public key (cannot be NULL).
+ */
+SECP256K1_API int secp256k1_xonly_pubkey_serialize(
+ const secp256k1_context* ctx,
+ unsigned char *output32,
+ const secp256k1_xonly_pubkey* pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
+ *
+ * Returns: 1 if the public key was successfully converted
+ * 0 otherwise
+ *
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: xonly_pubkey: pointer to an x-only public key object for placing the
+ * converted public key (cannot be NULL)
+ * pk_parity: pointer to an integer that will be set to 1 if the point
+ * encoded by xonly_pubkey is the negation of the pubkey and
+ * set to 0 otherwise. (can be NULL)
+ * In: pubkey: pointer to a public key that is converted (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey *xonly_pubkey,
+ int *pk_parity,
+ const secp256k1_pubkey *pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+
+/** Tweak an x-only public key by adding the generator multiplied with tweak32
+ * to it.
+ *
+ * Note that the resulting point can not in general be represented by an x-only
+ * pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
+ * is a normal secp256k1_pubkey.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting public key would be
+ * invalid (only when the tweak is the negation of the corresponding
+ * secret key). 1 otherwise.
+ *
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * Out: output_pubkey: pointer to a public key to store the result. Will be set
+ * to an invalid value if this function returns 0 (cannot
+ * be NULL)
+ * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
+ * (cannot be NULL).
+ * tweak32: pointer to a 32-byte tweak. If the tweak is invalid
+ * according to secp256k1_ec_seckey_verify, this function
+ * returns 0. For uniformly random 32-byte arrays the
+ * chance of being invalid is negligible (around 1 in
+ * 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
+ const secp256k1_context* ctx,
+ secp256k1_pubkey *output_pubkey,
+ const secp256k1_xonly_pubkey *internal_pubkey,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Checks that a tweaked pubkey is the result of calling
+ * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
+ *
+ * The tweaked pubkey is represented by its 32-byte x-only serialization and
+ * its pk_parity, which can both be obtained by converting the result of
+ * tweak_add to a secp256k1_xonly_pubkey.
+ *
+ * Note that this alone does _not_ verify that the tweaked pubkey is a
+ * commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
+ * can easily be the result of a different internal_pubkey and tweak.
+ *
+ * Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
+ * result of tweaking the internal_pubkey with tweak32. 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL)
+ * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
+ * is passed in as tweaked_pubkey32). This must match the
+ * pk_parity value that is returned when calling
+ * secp256k1_xonly_pubkey with the tweaked pubkey, or
+ * this function will fail.
+ * internal_pubkey: pointer to an x-only public key object to apply the
+ * tweak to (cannot be NULL)
+ * tweak32: pointer to a 32-byte tweak (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check(
+ const secp256k1_context* ctx,
+ const unsigned char *tweaked_pubkey32,
+ int tweaked_pk_parity,
+ const secp256k1_xonly_pubkey *internal_pubkey,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
+
+/** Compute the keypair for a secret key.
+ *
+ * Returns: 1: secret was valid, keypair is ready to use
+ * 0: secret was invalid, try again with a different secret
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: keypair: pointer to the created keypair (cannot be NULL)
+ * In: seckey: pointer to a 32-byte secret key (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
+ const secp256k1_context* ctx,
+ secp256k1_keypair *keypair,
+ const unsigned char *seckey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the public key from a keypair.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
+ * the keypair public key. If not, it's set to an invalid value.
+ * (cannot be NULL)
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
+ const secp256k1_context* ctx,
+ secp256k1_pubkey *pubkey,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the x-only public key from a keypair.
+ *
+ * This is the same as calling secp256k1_keypair_pub and then
+ * secp256k1_xonly_pubkey_from_pubkey.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
+ * to the keypair public key after converting it to an
+ * xonly_pubkey. If not, it's set to an invalid value (cannot be
+ * NULL).
+ * pk_parity: pointer to an integer that will be set to the pk_parity
+ * argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey *pubkey,
+ int *pk_parity,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+
+/** Tweak a keypair by adding tweak32 to the secret key and updating the public
+ * key accordingly.
+ *
+ * Calling this function and then secp256k1_keypair_pub results in the same
+ * public key as calling secp256k1_keypair_xonly_pub and then
+ * secp256k1_xonly_pubkey_tweak_add.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting keypair would be
+ * invalid (only when the tweak is the negation of the keypair's
+ * secret key). 1 otherwise.
+ *
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
+ * an invalid value if this function returns 0 (cannot be
+ * NULL).
+ * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
+ * to secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
+ const secp256k1_context* ctx,
+ secp256k1_keypair *keypair,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_EXTRAKEYS_H */
diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h
new file mode 100644
index 0000000000..0150cd3395
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_schnorrsig.h
@@ -0,0 +1,111 @@
+#ifndef SECP256K1_SCHNORRSIG_H
+#define SECP256K1_SCHNORRSIG_H
+
+#include "secp256k1.h"
+#include "secp256k1_extrakeys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This module implements a variant of Schnorr signatures compliant with
+ * Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1"
+ * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
+ */
+
+/** A pointer to a function to deterministically generate a nonce.
+ *
+ * Same as secp256k1_nonce function with the exception of accepting an
+ * additional pubkey argument and not requiring an attempt argument. The pubkey
+ * argument can protect signature schemes with key-prefixed challenge hash
+ * inputs against reusing the nonce when signing with the wrong precomputed
+ * pubkey.
+ *
+ * Returns: 1 if a nonce was successfully generated. 0 will cause signing to
+ * return an error.
+ * Out: nonce32: pointer to a 32-byte array to be filled by the function.
+ * In: msg32: the 32-byte message hash being verified (will not be NULL)
+ * key32: pointer to a 32-byte secret key (will not be NULL)
+ * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
+ * (will not be NULL)
+ * algo16: pointer to a 16-byte array describing the signature
+ * algorithm (will not be NULL).
+ * data: Arbitrary data pointer that is passed through.
+ *
+ * Except for test cases, this function should compute some cryptographic hash of
+ * the message, the key, the pubkey, the algorithm description, and data.
+ */
+typedef int (*secp256k1_nonce_function_hardened)(
+ unsigned char *nonce32,
+ const unsigned char *msg32,
+ const unsigned char *key32,
+ const unsigned char *xonly_pk32,
+ const unsigned char *algo16,
+ void *data
+);
+
+/** An implementation of the nonce generation function as defined in Bitcoin
+ * Improvement Proposal 340 "Schnorr Signatures for secp256k1"
+ * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
+ *
+ * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
+ * auxiliary random data as defined in BIP-340. If the data pointer is NULL,
+ * schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
+ * argument must be non-NULL, otherwise the function will fail and return 0.
+ * The hash will be tagged with algo16 after removing all terminating null
+ * bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
+ * to "BIP0340/nonce\0\0\0"
+ */
+SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
+
+/** Create a Schnorr signature.
+ *
+ * Does _not_ strictly follow BIP-340 because it does not verify the resulting
+ * signature. Instead, you can manually use secp256k1_schnorrsig_verify and
+ * abort if it fails.
+ *
+ * Otherwise BIP-340 compliant if the noncefp argument is NULL or
+ * secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary
+ * randomness.
+ *
+ * Returns 1 on success, 0 on failure.
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL)
+ * In: msg32: the 32-byte message being signed (cannot be NULL)
+ * keypair: pointer to an initialized keypair (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bip340 is used
+ * ndata: pointer to arbitrary data used by the nonce generation
+ * function (can be NULL). If it is non-NULL and
+ * secp256k1_nonce_function_bip340 is used, then ndata must be a
+ * pointer to 32-byte auxiliary randomness as per BIP-340.
+ */
+SECP256K1_API int secp256k1_schnorrsig_sign(
+ const secp256k1_context* ctx,
+ unsigned char *sig64,
+ const unsigned char *msg32,
+ const secp256k1_keypair *keypair,
+ secp256k1_nonce_function_hardened noncefp,
+ void *ndata
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Verify a Schnorr signature.
+ *
+ * Returns: 1: correct signature
+ * 0: incorrect signature
+ * Args: ctx: a secp256k1 context object, initialized for verification.
+ * In: sig64: pointer to the 64-byte signature to verify (cannot be NULL)
+ * msg32: the 32-byte message being verified (cannot be NULL)
+ * pubkey: pointer to an x-only public key to verify with (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
+ const secp256k1_context* ctx,
+ const unsigned char *sig64,
+ const unsigned char *msg32,
+ const secp256k1_xonly_pubkey *pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_SCHNORRSIG_H */
diff --git a/src/secp256k1/sage/gen_exhaustive_groups.sage b/src/secp256k1/sage/gen_exhaustive_groups.sage
new file mode 100644
index 0000000000..3c3c984811
--- /dev/null
+++ b/src/secp256k1/sage/gen_exhaustive_groups.sage
@@ -0,0 +1,129 @@
+# Define field size and field
+P = 2^256 - 2^32 - 977
+F = GF(P)
+BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee)
+
+assert(BETA != F(1) and BETA^3 == F(1))
+
+orders_done = set()
+results = {}
+first = True
+for b in range(1, P):
+ # There are only 6 curves (up to isomorphism) of the form y^2=x^3+B. Stop once we have tried all.
+ if len(orders_done) == 6:
+ break
+
+ E = EllipticCurve(F, [0, b])
+ print("Analyzing curve y^2 = x^3 + %i" % b)
+ n = E.order()
+ # Skip curves with an order we've already tried
+ if n in orders_done:
+ print("- Isomorphic to earlier curve")
+ continue
+ orders_done.add(n)
+ # Skip curves isomorphic to the real secp256k1
+ if n.is_pseudoprime():
+ print(" - Isomorphic to secp256k1")
+ continue
+
+ print("- Finding subgroups")
+
+ # Find what prime subgroups exist
+ for f, _ in n.factor():
+ print("- Analyzing subgroup of order %i" % f)
+ # Skip subgroups of order >1000
+ if f < 4 or f > 1000:
+ print(" - Bad size")
+ continue
+
+ # Iterate over X coordinates until we find one that is on the curve, has order f,
+ # and for which curve isomorphism exists that maps it to X coordinate 1.
+ for x in range(1, P):
+ # Skip X coordinates not on the curve, and construct the full point otherwise.
+ if not E.is_x_coord(x):
+ continue
+ G = E.lift_x(F(x))
+
+ print(" - Analyzing (multiples of) point with X=%i" % x)
+
+ # Skip points whose order is not a multiple of f. Project the point to have
+ # order f otherwise.
+ if (G.order() % f):
+ print(" - Bad order")
+ continue
+ G = G * (G.order() // f)
+
+ # Find lambda for endomorphism. Skip if none can be found.
+ lam = None
+ for l in Integers(f)(1).nth_root(3, all=True):
+ if int(l)*G == E(BETA*G[0], G[1]):
+ lam = int(l)
+ break
+ if lam is None:
+ print(" - No endomorphism for this subgroup")
+ break
+
+ # Now look for an isomorphism of the curve that gives this point an X
+ # coordinate equal to 1.
+ # If (x,y) is on y^2 = x^3 + b, then (a^2*x, a^3*y) is on y^2 = x^3 + a^6*b.
+ # So look for m=a^2=1/x.
+ m = F(1)/G[0]
+ if not m.is_square():
+ print(" - No curve isomorphism maps it to a point with X=1")
+ continue
+ a = m.sqrt()
+ rb = a^6*b
+ RE = EllipticCurve(F, [0, rb])
+
+ # Use as generator twice the image of G under the above isormorphism.
+ # This means that generator*(1/2 mod f) will have X coordinate 1.
+ RG = RE(1, a^3*G[1]) * 2
+ # And even Y coordinate.
+ if int(RG[1]) % 2:
+ RG = -RG
+ assert(RG.order() == f)
+ assert(lam*RG == RE(BETA*RG[0], RG[1]))
+
+ # We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it
+ results[f] = {"b": rb, "G": RG, "lambda": lam}
+ print(" - Found solution")
+ break
+
+ print("")
+
+print("")
+print("")
+print("/* To be put in src/group_impl.h: */")
+first = True
+for f in sorted(results.keys()):
+ b = results[f]["b"]
+ G = results[f]["G"]
+ print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
+ first = False
+ print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(")
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
+ print(");")
+ print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(")
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
+ print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
+ print(");")
+print("# else")
+print("# error No known generator for the specified exhaustive test group order.")
+print("# endif")
+
+print("")
+print("")
+print("/* To be put in src/scalar_impl.h: */")
+first = True
+for f in sorted(results.keys()):
+ lam = results[f]["lambda"]
+ print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
+ first = False
+ print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam)
+print("# else")
+print("# error No known lambda for the specified exhaustive test group order.")
+print("# endif")
+print("")
diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h
new file mode 100644
index 0000000000..77204de2b8
--- /dev/null
+++ b/src/secp256k1/src/assumptions.h
@@ -0,0 +1,80 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_ASSUMPTIONS_H
+#define SECP256K1_ASSUMPTIONS_H
+
+#include <limits.h>
+
+#include "util.h"
+
+/* This library, like most software, relies on a number of compiler implementation defined (but not undefined)
+ behaviours. Although the behaviours we require are essentially universal we test them specifically here to
+ reduce the odds of experiencing an unwelcome surprise.
+*/
+
+struct secp256k1_assumption_checker {
+ /* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not
+ allowed. */
+ int dummy_array[(
+ /* Bytes are 8 bits. */
+ (CHAR_BIT == 8) &&
+
+ /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32
+ without signed overflow, which would be undefined behaviour. */
+ (UINT_MAX <= UINT32_MAX) &&
+
+ /* Conversions from unsigned to signed outside of the bounds of the signed type are
+ implementation-defined. Verify that they function as reinterpreting the lower
+ bits of the input in two's complement notation. Do this for conversions:
+ - from uint(N)_t to int(N)_t with negative result
+ - from uint(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with positive result */
+
+ /* To int8_t. */
+ ((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) &&
+ ((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) &&
+ ((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) &&
+ ((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) &&
+
+ /* To int16_t. */
+ ((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) &&
+ ((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) &&
+ ((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) &&
+ ((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) &&
+
+ /* To int32_t. */
+ ((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) &&
+ ((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) &&
+ ((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) &&
+ ((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) &&
+
+ /* To int64_t. */
+ ((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) &&
+#if defined(SECP256K1_WIDEMUL_INT128)
+ ((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) &&
+ (((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) &&
+ (((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) &&
+
+ /* To int128_t. */
+ ((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) &&
+#endif
+
+ /* Right shift on negative signed values is implementation defined. Verify that it
+ acts as a right shift in two's complement with sign extension (i.e duplicating
+ the top bit into newly added bits). */
+ ((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) &&
+ ((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) &&
+ ((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) &&
+ ((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) &&
+#if defined(SECP256K1_WIDEMUL_INT128)
+ ((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) &&
+#endif
+ 1) * 2 - 1];
+};
+
+#endif /* SECP256K1_ASSUMPTIONS_H */
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index e9be39d4ca..b0d82e89b4 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -11,26 +11,22 @@
#undef USE_ASM_X86_64
#undef USE_ECMULT_STATIC_PRECOMPUTATION
-#undef USE_ENDOMORPHISM
#undef USE_EXTERNAL_ASM
#undef USE_EXTERNAL_DEFAULT_CALLBACKS
-#undef USE_FIELD_10X26
-#undef USE_FIELD_5X52
#undef USE_FIELD_INV_BUILTIN
#undef USE_FIELD_INV_NUM
#undef USE_NUM_GMP
#undef USE_NUM_NONE
-#undef USE_SCALAR_4X64
-#undef USE_SCALAR_8X32
#undef USE_SCALAR_INV_BUILTIN
#undef USE_SCALAR_INV_NUM
+#undef USE_FORCE_WIDEMUL_INT64
+#undef USE_FORCE_WIDEMUL_INT128
#undef ECMULT_WINDOW_SIZE
#define USE_NUM_NONE 1
#define USE_FIELD_INV_BUILTIN 1
#define USE_SCALAR_INV_BUILTIN 1
-#define USE_FIELD_10X26 1
-#define USE_SCALAR_8X32 1
+#define USE_WIDEMUL_64 1
#define ECMULT_WINDOW_SIZE 15
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 20759127d3..5f2b7a9759 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -7,6 +7,7 @@
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
#include "hash_impl.h"
#include "num_impl.h"
@@ -19,10 +20,10 @@
#include "secp256k1.c"
typedef struct {
- secp256k1_scalar scalar_x, scalar_y;
- secp256k1_fe fe_x, fe_y;
- secp256k1_ge ge_x, ge_y;
- secp256k1_gej gej_x, gej_y;
+ secp256k1_scalar scalar[2];
+ secp256k1_fe fe[4];
+ secp256k1_ge ge[2];
+ secp256k1_gej gej[2];
unsigned char data[64];
int wnaf[256];
} bench_inv;
@@ -30,30 +31,53 @@ typedef struct {
void bench_setup(void* arg) {
bench_inv *data = (bench_inv*)arg;
- static const unsigned char init_x[32] = {
- 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
- 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
- 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
- 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
+ static const unsigned char init[4][32] = {
+ /* Initializer for scalar[0], fe[0], first half of data, the X coordinate of ge[0],
+ and the (implied affine) X coordinate of gej[0]. */
+ {
+ 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
+ 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
+ 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
+ 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
+ },
+ /* Initializer for scalar[1], fe[1], first half of data, the X coordinate of ge[1],
+ and the (implied affine) X coordinate of gej[1]. */
+ {
+ 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
+ 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
+ 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
+ 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
+ },
+ /* Initializer for fe[2] and the Z coordinate of gej[0]. */
+ {
+ 0x3d, 0x2d, 0xef, 0xf4, 0x25, 0x98, 0x4f, 0x5d,
+ 0xe2, 0xca, 0x5f, 0x41, 0x3f, 0x3f, 0xce, 0x44,
+ 0xaa, 0x2c, 0x53, 0x8a, 0xc6, 0x59, 0x1f, 0x38,
+ 0x38, 0x23, 0xe4, 0x11, 0x27, 0xc6, 0xa0, 0xe7
+ },
+ /* Initializer for fe[3] and the Z coordinate of gej[1]. */
+ {
+ 0xbd, 0x21, 0xa5, 0xe1, 0x13, 0x50, 0x73, 0x2e,
+ 0x52, 0x98, 0xc8, 0x9e, 0xab, 0x00, 0xa2, 0x68,
+ 0x43, 0xf5, 0xd7, 0x49, 0x80, 0x72, 0xa7, 0xf3,
+ 0xd7, 0x60, 0xe6, 0xab, 0x90, 0x92, 0xdf, 0xc5
+ }
};
- static const unsigned char init_y[32] = {
- 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
- 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
- 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
- 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
- };
-
- secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL);
- secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL);
- secp256k1_fe_set_b32(&data->fe_x, init_x);
- secp256k1_fe_set_b32(&data->fe_y, init_y);
- CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0));
- CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1));
- secp256k1_gej_set_ge(&data->gej_x, &data->ge_x);
- secp256k1_gej_set_ge(&data->gej_y, &data->ge_y);
- memcpy(data->data, init_x, 32);
- memcpy(data->data + 32, init_y, 32);
+ secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL);
+ secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL);
+ secp256k1_fe_set_b32(&data->fe[0], init[0]);
+ secp256k1_fe_set_b32(&data->fe[1], init[1]);
+ secp256k1_fe_set_b32(&data->fe[2], init[2]);
+ secp256k1_fe_set_b32(&data->fe[3], init[3]);
+ CHECK(secp256k1_ge_set_xo_var(&data->ge[0], &data->fe[0], 0));
+ CHECK(secp256k1_ge_set_xo_var(&data->ge[1], &data->fe[1], 1));
+ secp256k1_gej_set_ge(&data->gej[0], &data->ge[0]);
+ secp256k1_gej_rescale(&data->gej[0], &data->fe[2]);
+ secp256k1_gej_set_ge(&data->gej[1], &data->ge[1]);
+ secp256k1_gej_rescale(&data->gej[1], &data->fe[3]);
+ memcpy(data->data, init[0], 32);
+ memcpy(data->data + 32, init[1], 32);
}
void bench_scalar_add(void* arg, int iters) {
@@ -61,7 +85,7 @@ void bench_scalar_add(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -71,7 +95,7 @@ void bench_scalar_negate(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
+ secp256k1_scalar_negate(&data->scalar[0], &data->scalar[0]);
}
}
@@ -80,7 +104,7 @@ void bench_scalar_sqr(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
+ secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]);
}
}
@@ -89,30 +113,28 @@ void bench_scalar_mul(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_mul(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
}
-#ifdef USE_ENDOMORPHISM
void bench_scalar_split(void* arg, int iters) {
int i, j = 0;
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_split_lambda(&data->scalar_x, &data->scalar_y, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_split_lambda(&data->scalar[0], &data->scalar[1], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
-#endif
void bench_scalar_inverse(void* arg, int iters) {
int i, j = 0;
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_inverse(&data->scalar[0], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -122,8 +144,8 @@ void bench_scalar_inverse_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_inverse_var(&data->scalar[0], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -133,7 +155,7 @@ void bench_field_normalize(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_normalize(&data->fe_x);
+ secp256k1_fe_normalize(&data->fe[0]);
}
}
@@ -142,7 +164,7 @@ void bench_field_normalize_weak(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_normalize_weak(&data->fe_x);
+ secp256k1_fe_normalize_weak(&data->fe[0]);
}
}
@@ -151,7 +173,7 @@ void bench_field_mul(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
+ secp256k1_fe_mul(&data->fe[0], &data->fe[0], &data->fe[1]);
}
}
@@ -160,7 +182,7 @@ void bench_field_sqr(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
+ secp256k1_fe_sqr(&data->fe[0], &data->fe[0]);
}
}
@@ -169,8 +191,8 @@ void bench_field_inverse(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_inv(&data->fe_x, &data->fe_x);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ secp256k1_fe_inv(&data->fe[0], &data->fe[0]);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
}
@@ -179,8 +201,8 @@ void bench_field_inverse_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ secp256k1_fe_inv_var(&data->fe[0], &data->fe[0]);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
}
@@ -190,9 +212,9 @@ void bench_field_sqrt(void* arg, int iters) {
secp256k1_fe t;
for (i = 0; i < iters; i++) {
- t = data->fe_x;
- j += secp256k1_fe_sqrt(&data->fe_x, &t);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ t = data->fe[0];
+ j += secp256k1_fe_sqrt(&data->fe[0], &t);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
CHECK(j <= iters);
}
@@ -202,7 +224,7 @@ void bench_group_double_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
+ secp256k1_gej_double_var(&data->gej[0], &data->gej[0], NULL);
}
}
@@ -211,7 +233,7 @@ void bench_group_add_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
+ secp256k1_gej_add_var(&data->gej[0], &data->gej[0], &data->gej[1], NULL);
}
}
@@ -220,7 +242,7 @@ void bench_group_add_affine(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
+ secp256k1_gej_add_ge(&data->gej[0], &data->gej[0], &data->ge[1]);
}
}
@@ -229,7 +251,7 @@ void bench_group_add_affine_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
+ secp256k1_gej_add_ge_var(&data->gej[0], &data->gej[0], &data->ge[1], NULL);
}
}
@@ -238,9 +260,37 @@ void bench_group_jacobi_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- j += secp256k1_gej_has_quad_y_var(&data->gej_x);
+ j += secp256k1_gej_has_quad_y_var(&data->gej[0]);
+ /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to
+ secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will
+ generally not correspond to a point on the curve, but this is not a problem
+ for the code being benchmarked here. Adding and normalizing have less
+ overhead than EC operations (which could guarantee the point remains on the
+ curve). */
+ secp256k1_fe_add(&data->gej[0].y, &data->fe[1]);
+ secp256k1_fe_add(&data->gej[0].z, &data->fe[2]);
+ secp256k1_fe_normalize_var(&data->gej[0].y);
+ secp256k1_fe_normalize_var(&data->gej[0].z);
+ }
+ CHECK(j <= iters);
+}
+
+void bench_group_to_affine_var(void* arg, int iters) {
+ int i;
+ bench_inv *data = (bench_inv*)arg;
+
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]);
+ /* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates.
+ Similar to bench_group_jacobi_var, this approach does not result in
+ coordinates of points on the curve. */
+ secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y);
+ secp256k1_fe_add(&data->gej[0].y, &data->fe[2]);
+ secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x);
+ secp256k1_fe_normalize_var(&data->gej[0].x);
+ secp256k1_fe_normalize_var(&data->gej[0].y);
+ secp256k1_fe_normalize_var(&data->gej[0].z);
}
- CHECK(j == iters);
}
void bench_ecmult_wnaf(void* arg, int iters) {
@@ -248,8 +298,8 @@ void bench_ecmult_wnaf(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
- overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar[0], WINDOW_A);
+ overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(overflow >= 0);
CHECK(bits <= 256*iters);
@@ -260,8 +310,8 @@ void bench_wnaf_const(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- bits += secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256);
- overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ bits += secp256k1_wnaf_const(data->wnaf, &data->scalar[0], WINDOW_A, 256);
+ overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(overflow >= 0);
CHECK(bits <= 256*iters);
@@ -323,14 +373,15 @@ void bench_context_sign(void* arg, int iters) {
void bench_num_jacobi(void* arg, int iters) {
int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- secp256k1_num nx, norder;
+ secp256k1_num nx, na, norder;
- secp256k1_scalar_get_num(&nx, &data->scalar_x);
+ secp256k1_scalar_get_num(&nx, &data->scalar[0]);
secp256k1_scalar_order_get_num(&norder);
- secp256k1_scalar_get_num(&norder, &data->scalar_y);
+ secp256k1_scalar_get_num(&na, &data->scalar[1]);
for (i = 0; i < iters; i++) {
j += secp256k1_num_jacobi(&nx, &norder);
+ secp256k1_num_add(&nx, &nx, &na);
}
CHECK(j <= iters);
}
@@ -344,9 +395,7 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10);
-#ifdef USE_ENDOMORPHISM
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
-#endif
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
@@ -363,6 +412,7 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters);
diff --git a/src/secp256k1/src/bench_schnorrsig.c b/src/secp256k1/src/bench_schnorrsig.c
new file mode 100644
index 0000000000..315f5af28e
--- /dev/null
+++ b/src/secp256k1/src/bench_schnorrsig.c
@@ -0,0 +1,102 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_schnorrsig.h"
+#include "util.h"
+#include "bench.h"
+
+typedef struct {
+ secp256k1_context *ctx;
+ int n;
+
+ const secp256k1_keypair **keypairs;
+ const unsigned char **pk;
+ const unsigned char **sigs;
+ const unsigned char **msgs;
+} bench_schnorrsig_data;
+
+void bench_schnorrsig_sign(void* arg, int iters) {
+ bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
+ int i;
+ unsigned char msg[32] = "benchmarkexamplemessagetemplate";
+ unsigned char sig[64];
+
+ for (i = 0; i < iters; i++) {
+ msg[0] = i;
+ msg[1] = i >> 8;
+ CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL));
+ }
+}
+
+void bench_schnorrsig_verify(void* arg, int iters) {
+ bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
+ int i;
+
+ for (i = 0; i < iters; i++) {
+ secp256k1_xonly_pubkey pk;
+ CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1);
+ CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
+ }
+}
+
+int main(void) {
+ int i;
+ bench_schnorrsig_data data;
+ int iters = get_iters(10000);
+
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
+ data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *));
+ data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+ data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+ data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+
+ for (i = 0; i < iters; i++) {
+ unsigned char sk[32];
+ unsigned char *msg = (unsigned char *)malloc(32);
+ unsigned char *sig = (unsigned char *)malloc(64);
+ secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
+ unsigned char *pk_char = (unsigned char *)malloc(32);
+ secp256k1_xonly_pubkey pk;
+ msg[0] = sk[0] = i;
+ msg[1] = sk[1] = i >> 8;
+ msg[2] = sk[2] = i >> 16;
+ msg[3] = sk[3] = i >> 24;
+ memset(&msg[4], 'm', 28);
+ memset(&sk[4], 's', 28);
+
+ data.keypairs[i] = keypair;
+ data.pk[i] = pk_char;
+ data.msgs[i] = msg;
+ data.sigs[i] = sig;
+
+ CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL));
+ CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
+ CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
+ }
+
+ run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters);
+ run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters);
+
+ for (i = 0; i < iters; i++) {
+ free((void *)data.keypairs[i]);
+ free((void *)data.pk[i]);
+ free((void *)data.msgs[i]);
+ free((void *)data.sigs[i]);
+ }
+ free(data.keypairs);
+ free(data.pk);
+ free(data.msgs);
+ free(data.sigs);
+
+ secp256k1_context_destroy(data.ctx);
+ return 0;
+}
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index c9b198239d..09e8146414 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -15,9 +15,7 @@
typedef struct {
/* For accelerating the computation of a*P + b*G: */
secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */
-#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */
-#endif
} secp256k1_ecmult_context;
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 6d6d354aa4..bb9511108b 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -105,16 +105,22 @@ static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
do {
- int sign;
int even;
/* 4.1 4.4 */
u = secp256k1_scalar_shr_int(&s, w);
/* 4.2 */
even = ((u & 1) == 0);
- sign = 2 * (u_last > 0) - 1;
- u += sign * even;
- u_last -= sign * even * (1 << w);
+ /* In contrast to the original algorithm, u_last is always > 0 and
+ * therefore we do not need to check its sign. In particular, it's easy
+ * to see that u_last is never < 0 because u is never < 0. Moreover,
+ * u_last is never = 0 because u is never even after a loop
+ * iteration. The same holds analogously for the initial value of
+ * u_last (in the first loop iteration). */
+ VERIFY_CHECK(u_last > 0);
+ VERIFY_CHECK((u_last & 1) == 1);
+ u += even;
+ u_last -= even * (1 << w);
/* 4.3, adapted for global sign change */
wnaf[word++] = u_last * global_sign;
@@ -134,19 +140,16 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_fe Z;
int skew_1;
-#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
int skew_lam;
secp256k1_scalar q_1, q_lam;
-#endif
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i;
/* build wnaf representation for q. */
int rsize = size;
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
rsize = 128;
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
@@ -154,12 +157,9 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128);
skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128);
} else
-#endif
{
skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size);
-#ifdef USE_ENDOMORPHISM
skew_lam = 0;
-#endif
}
/* Calculate odd multiples of a.
@@ -173,14 +173,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_fe_normalize_weak(&pre_a[i].y);
}
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
}
}
-#endif
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
@@ -189,34 +187,30 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
secp256k1_gej_add_ge(r, r, &tmpa);
}
-#endif
/* remaining loop iterations */
for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) {
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
- secp256k1_gej_double_nonzero(r, r);
+ secp256k1_gej_double(r, r);
}
n = wnaf_1[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
n = wnaf_lam[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
}
-#endif
}
secp256k1_fe_mul(&r->z, &r->z, &Z);
@@ -225,43 +219,35 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
/* Correct for wNAF skew */
secp256k1_ge correction = *a;
secp256k1_ge_storage correction_1_stor;
-#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage correction_lam_stor;
-#endif
secp256k1_ge_storage a2_stor;
secp256k1_gej tmpj;
secp256k1_gej_set_ge(&tmpj, &correction);
secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
secp256k1_ge_to_storage(&correction_lam_stor, a);
}
-#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
}
-#endif
/* Apply the correction */
secp256k1_ge_from_storage(&correction, &correction_1_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
-#ifdef USE_ENDOMORPHISM
if (size > 128) {
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_ge_mul_lambda(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
}
-#endif
}
}
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index f03fa9469d..057a69cf73 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -38,8 +38,8 @@
* (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
* where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
* be larger due to platform-specific padding and alignment.
- * If the endomorphism optimization is enabled (USE_ENDOMORMPHSIM)
- * two tables of this size are used instead of only one.
+ * Two tables of this size are used (due to the endomorphism
+ * optimization).
*/
# define WINDOW_G ECMULT_WINDOW_SIZE
#endif
@@ -59,11 +59,7 @@
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
#endif
-#ifdef USE_ENDOMORPHISM
- #define WNAF_BITS 128
-#else
- #define WNAF_BITS 256
-#endif
+#define WNAF_BITS 128
#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w))
#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
@@ -77,17 +73,9 @@
#define PIPPENGER_MAX_BUCKET_WINDOW 12
/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
-#ifdef USE_ENDOMORPHISM
- #define ECMULT_PIPPENGER_THRESHOLD 88
-#else
- #define ECMULT_PIPPENGER_THRESHOLD 160
-#endif
+#define ECMULT_PIPPENGER_THRESHOLD 88
-#ifdef USE_ENDOMORPHISM
- #define ECMULT_MAX_POINTS_PER_BATCH 5000000
-#else
- #define ECMULT_MAX_POINTS_PER_BATCH 10000000
-#endif
+#define ECMULT_MAX_POINTS_PER_BATCH 5000000
/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
* the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
@@ -313,16 +301,12 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE =
ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
-#ifdef USE_ENDOMORPHISM
+ ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
-#endif
;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
ctx->pre_g = NULL;
-#ifdef USE_ENDOMORPHISM
ctx->pre_g_128 = NULL;
-#endif
}
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) {
@@ -347,7 +331,6 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void *
/* precompute the tables with odd multiples */
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
-#ifdef USE_ENDOMORPHISM
{
secp256k1_gej g_128j;
int i;
@@ -364,7 +347,6 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void *
}
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j);
}
-#endif
}
static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) {
@@ -372,11 +354,9 @@ static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *d
/* We cast to void* first to suppress a -Wcast-align warning. */
dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src));
}
-#ifdef USE_ENDOMORPHISM
if (src->pre_g_128 != NULL) {
dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src));
}
-#endif
}
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) {
@@ -447,16 +427,11 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
}
struct secp256k1_strauss_point_state {
-#ifdef USE_ENDOMORPHISM
secp256k1_scalar na_1, na_lam;
- int wnaf_na_1[130];
- int wnaf_na_lam[130];
+ int wnaf_na_1[129];
+ int wnaf_na_lam[129];
int bits_na_1;
int bits_na_lam;
-#else
- int wnaf_na[256];
- int bits_na;
-#endif
size_t input_pos;
};
@@ -464,26 +439,19 @@ struct secp256k1_strauss_state {
secp256k1_gej* prej;
secp256k1_fe* zr;
secp256k1_ge* pre_a;
-#ifdef USE_ENDOMORPHISM
secp256k1_ge* pre_a_lam;
-#endif
struct secp256k1_strauss_point_state* ps;
};
static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
secp256k1_ge tmpa;
secp256k1_fe Z;
-#ifdef USE_ENDOMORPHISM
/* Splitted G factors. */
secp256k1_scalar ng_1, ng_128;
int wnaf_ng_1[129];
int bits_ng_1 = 0;
int wnaf_ng_128[129];
int bits_ng_128 = 0;
-#else
- int wnaf_ng[256];
- int bits_ng = 0;
-#endif
int i;
int bits = 0;
int np;
@@ -494,28 +462,20 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
continue;
}
state->ps[no].input_pos = np;
-#ifdef USE_ENDOMORPHISM
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]);
/* build wnaf representation for na_1 and na_lam. */
- state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A);
- state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A);
- VERIFY_CHECK(state->ps[no].bits_na_1 <= 130);
- VERIFY_CHECK(state->ps[no].bits_na_lam <= 130);
+ state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &state->ps[no].na_1, WINDOW_A);
+ state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &state->ps[no].na_lam, WINDOW_A);
+ VERIFY_CHECK(state->ps[no].bits_na_1 <= 129);
+ VERIFY_CHECK(state->ps[no].bits_na_lam <= 129);
if (state->ps[no].bits_na_1 > bits) {
bits = state->ps[no].bits_na_1;
}
if (state->ps[no].bits_na_lam > bits) {
bits = state->ps[no].bits_na_lam;
}
-#else
- /* build wnaf representation for na. */
- state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A);
- if (state->ps[no].bits_na > bits) {
- bits = state->ps[no].bits_na;
- }
-#endif
++no;
}
@@ -547,7 +507,6 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
secp256k1_fe_set_int(&Z, 1);
}
-#ifdef USE_ENDOMORPHISM
for (np = 0; np < no; ++np) {
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]);
@@ -568,21 +527,12 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
bits = bits_ng_128;
}
}
-#else
- if (ng) {
- bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
- if (bits_ng > bits) {
- bits = bits_ng;
- }
- }
-#endif
secp256k1_gej_set_infinity(r);
for (i = bits - 1; i >= 0; i--) {
int n;
secp256k1_gej_double_var(r, r, NULL);
-#ifdef USE_ENDOMORPHISM
for (np = 0; np < no; ++np) {
if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
@@ -601,18 +551,6 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
-#else
- for (np = 0; np < no; ++np) {
- if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) {
- ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
- secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
- }
- }
- if (i < bits_ng && (n = wnaf_ng[i])) {
- ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
- secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
- }
-#endif
}
if (!r->infinity) {
@@ -625,27 +563,19 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
struct secp256k1_strauss_point_state ps[1];
-#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
-#endif
struct secp256k1_strauss_state state;
state.prej = prej;
state.zr = zr;
state.pre_a = pre_a;
-#ifdef USE_ENDOMORPHISM
state.pre_a_lam = pre_a_lam;
-#endif
state.ps = ps;
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng);
}
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
-#ifdef USE_ENDOMORPHISM
static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
-#else
- static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
-#endif
return n_points*point_size;
}
@@ -665,12 +595,8 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
-#ifdef USE_ENDOMORPHISM
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
-#else
- state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
-#endif
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
@@ -868,7 +794,6 @@ static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_wi
* set of buckets) for a given number of points.
*/
static int secp256k1_pippenger_bucket_window(size_t n) {
-#ifdef USE_ENDOMORPHISM
if (n <= 1) {
return 1;
} else if (n <= 4) {
@@ -892,33 +817,6 @@ static int secp256k1_pippenger_bucket_window(size_t n) {
} else {
return PIPPENGER_MAX_BUCKET_WINDOW;
}
-#else
- if (n <= 1) {
- return 1;
- } else if (n <= 11) {
- return 2;
- } else if (n <= 45) {
- return 3;
- } else if (n <= 100) {
- return 4;
- } else if (n <= 275) {
- return 5;
- } else if (n <= 625) {
- return 6;
- } else if (n <= 1850) {
- return 7;
- } else if (n <= 3400) {
- return 8;
- } else if (n <= 9630) {
- return 9;
- } else if (n <= 17900) {
- return 10;
- } else if (n <= 32800) {
- return 11;
- } else {
- return PIPPENGER_MAX_BUCKET_WINDOW;
- }
-#endif
}
/**
@@ -926,7 +824,6 @@ static int secp256k1_pippenger_bucket_window(size_t n) {
*/
static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
switch(bucket_window) {
-#ifdef USE_ENDOMORPHISM
case 1: return 1;
case 2: return 4;
case 3: return 20;
@@ -939,26 +836,11 @@ static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
case 10: return 7880;
case 11: return 16050;
case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
-#else
- case 1: return 1;
- case 2: return 11;
- case 3: return 45;
- case 4: return 100;
- case 5: return 275;
- case 6: return 625;
- case 7: return 1850;
- case 8: return 3400;
- case 9: return 9630;
- case 10: return 17900;
- case 11: return 32800;
- case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
-#endif
}
return 0;
}
-#ifdef USE_ENDOMORPHISM
SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
secp256k1_scalar tmp = *s1;
secp256k1_scalar_split_lambda(s1, s2, &tmp);
@@ -973,32 +855,23 @@ SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, s
secp256k1_ge_neg(p2, p2);
}
}
-#endif
/**
* Returns the scratch size required for a given number of points (excluding
* base point G) without considering alignment.
*/
static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
-#ifdef USE_ENDOMORPHISM
size_t entries = 2*n_points + 2;
-#else
- size_t entries = n_points + 1;
-#endif
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
}
static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
- /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
+ /* Use 2(n+1) with the endomorphism, when calculating batch
* sizes. The reason for +1 is that we add the G scalar to the list of
* other scalars. */
-#ifdef USE_ENDOMORPHISM
size_t entries = 2*n_points + 2;
-#else
- size_t entries = n_points + 1;
-#endif
secp256k1_ge *points;
secp256k1_scalar *scalars;
secp256k1_gej *buckets;
@@ -1035,10 +908,8 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
scalars[0] = *inp_g_sc;
points[0] = secp256k1_ge_const_g;
idx++;
-#ifdef USE_ENDOMORPHISM
secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
idx++;
-#endif
}
while (point_idx < n_points) {
@@ -1047,10 +918,8 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
return 0;
}
idx++;
-#ifdef USE_ENDOMORPHISM
secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
idx++;
-#endif
point_idx++;
}
@@ -1093,9 +962,7 @@ static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_cal
size_t space_overhead;
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
-#ifdef USE_ENDOMORPHISM
entry_size = 2*entry_size;
-#endif
space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
if (space_overhead > max_alloc) {
break;
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index 7993a1f11e..aca1fb72c5 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -22,16 +22,16 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_FIELD_10X26)
-#include "field_10x26.h"
-#elif defined(USE_FIELD_5X52)
+#include "util.h"
+
+#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52.h"
+#elif defined(SECP256K1_WIDEMUL_INT64)
+#include "field_10x26.h"
#else
-#error "Please select field implementation"
+#error "Please select wide multiplication implementation"
#endif
-#include "util.h"
-
/** Normalize a field element. This brings the field element to a canonical representation, reduces
* its magnitude to 1, and reduces it modulo field size `p`.
*/
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index fc5bfe357e..6a068484c2 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -46,4 +46,10 @@ typedef struct {
(d6) | (((uint64_t)(d7)) << 32) \
}}
+#define SECP256K1_FE_STORAGE_CONST_GET(d) \
+ (uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \
+ (uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \
+ (uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \
+ (uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0]
+
#endif /* SECP256K1_FIELD_REPR_H */
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 485921a60e..18e4d2f30e 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -14,12 +14,12 @@
#include "util.h"
#include "num.h"
-#if defined(USE_FIELD_10X26)
-#include "field_10x26_impl.h"
-#elif defined(USE_FIELD_5X52)
+#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52_impl.h"
+#elif defined(SECP256K1_WIDEMUL_INT64)
+#include "field_10x26_impl.h"
#else
-#error "Please select field implementation"
+#error "Please select wide multiplication implementation"
#endif
SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 539f574bfd..8b7729aee4 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -13,6 +13,7 @@
#include "basic-config.h"
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
#include "field_impl.h"
#include "scalar_impl.h"
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 863644f0f0..36e39ecf0f 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -59,6 +59,7 @@ static int secp256k1_ge_is_infinity(const secp256k1_ge *a);
/** Check whether a group element is valid (i.e., on the curve). */
static int secp256k1_ge_is_valid_var(const secp256k1_ge *a);
+/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
/** Set a group element equal to another which is given in jacobian coordinates */
@@ -95,8 +96,8 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
/** Check whether a group element's y coordinate is a quadratic residue. */
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
-/** Set r equal to the double of a, a cannot be infinity. Constant time. */
-static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a);
+/** Set r equal to the double of a. Constant time. */
+static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
@@ -115,10 +116,8 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c
/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */
static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv);
-#ifdef USE_ENDOMORPHISM
/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */
static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a);
-#endif
/** Clear a secp256k1_gej to prevent leaking sensitive information. */
static void secp256k1_gej_clear(secp256k1_gej *r);
@@ -138,4 +137,15 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);
+/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve.
+ *
+ * In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the
+ * group, and this function returns always true.
+ *
+ * When compiling in exhaustive test mode, a slightly different curve equation is used, leading to a group with a
+ * (very) small subgroup, and that subgroup is what is used for all cryptographic operations. In that mode, this
+ * function checks whether a point that is on the curve is in fact also in that subgroup.
+ */
+static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge);
+
#endif /* SECP256K1_GROUP_H */
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 43b039becf..a5fbc91a0f 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -11,49 +11,38 @@
#include "field.h"
#include "group.h"
-/* These points can be generated in sage as follows:
+/* These exhaustive group test orders and generators are chosen such that:
+ * - The field size is equal to that of secp256k1, so field code is the same.
+ * - The curve equation is of the form y^2=x^3+B for some constant B.
+ * - The subgroup has a generator 2*P, where P.x=1.
+ * - The subgroup has size less than 1000 to permit exhaustive testing.
+ * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).
*
- * 0. Setup a worksheet with the following parameters.
- * b = 4 # whatever CURVE_B will be set to
- * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
- * C = EllipticCurve ([F (0), F (b)])
- *
- * 1. Determine all the small orders available to you. (If there are
- * no satisfactory ones, go back and change b.)
- * print C.order().factor(limit=1000)
- *
- * 2. Choose an order as one of the prime factors listed in the above step.
- * (You can also multiply some to get a composite order, though the
- * tests will crash trying to invert scalars during signing.) We take a
- * random point and scale it to drop its order to the desired value.
- * There is some probability this won't work; just try again.
- * order = 199
- * P = C.random_point()
- * P = (int(P.order()) / int(order)) * P
- * assert(P.order() == order)
- *
- * 3. Print the values. You'll need to use a vim macro or something to
- * split the hex output into 4-byte chunks.
- * print "%x %x" % P.xy()
+ * These parameters are generated using sage/gen_exhaustive_groups.sage.
*/
#if defined(EXHAUSTIVE_TEST_ORDER)
-# if EXHAUSTIVE_TEST_ORDER == 199
+# if EXHAUSTIVE_TEST_ORDER == 13
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
- 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
- 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
- 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
- 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
+ 0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,
+ 0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,
+ 0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,
+ 0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24
);
-
-static const int CURVE_B = 4;
-# elif EXHAUSTIVE_TEST_ORDER == 13
+static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
+ 0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
+ 0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
+);
+# elif EXHAUSTIVE_TEST_ORDER == 199
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
- 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
- 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
- 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
- 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
+ 0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,
+ 0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,
+ 0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,
+ 0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae
+);
+static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
+ 0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1,
+ 0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb
);
-static const int CURVE_B = 2;
# else
# error No known generator for the specified exhaustive test group order.
# endif
@@ -68,7 +57,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
);
-static const int CURVE_B = 7;
+static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7);
#endif
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
@@ -219,14 +208,13 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
}
static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
- secp256k1_fe x2, x3, c;
+ secp256k1_fe x2, x3;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0;
- secp256k1_fe_set_int(&c, CURVE_B);
- secp256k1_fe_add(&c, &x3);
- return secp256k1_fe_sqrt(&r->y, &c);
+ secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
+ return secp256k1_fe_sqrt(&r->y, &x3);
}
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
@@ -269,41 +257,20 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a) {
return a->infinity;
}
-static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
- secp256k1_fe y2, x3, z2, z6;
- if (a->infinity) {
- return 0;
- }
- /** y^2 = x^3 + 7
- * (Y/Z^3)^2 = (X/Z^2)^3 + 7
- * Y^2 / Z^6 = X^3 / Z^6 + 7
- * Y^2 = X^3 + 7*Z^6
- */
- secp256k1_fe_sqr(&y2, &a->y);
- secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
- secp256k1_fe_sqr(&z2, &a->z);
- secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
- secp256k1_fe_mul_int(&z6, CURVE_B);
- secp256k1_fe_add(&x3, &z6);
- secp256k1_fe_normalize_weak(&x3);
- return secp256k1_fe_equal_var(&y2, &x3);
-}
-
static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
- secp256k1_fe y2, x3, c;
+ secp256k1_fe y2, x3;
if (a->infinity) {
return 0;
}
/* y^2 = x^3 + 7 */
secp256k1_fe_sqr(&y2, &a->y);
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
- secp256k1_fe_set_int(&c, CURVE_B);
- secp256k1_fe_add(&x3, &c);
+ secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3);
}
-static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a) {
+static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) {
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
*
* Note that there is an implementation described at
@@ -313,8 +280,7 @@ static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, cons
*/
secp256k1_fe t1,t2,t3,t4;
- VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
- r->infinity = 0;
+ r->infinity = a->infinity;
secp256k1_fe_mul(&r->z, &a->z, &a->y);
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
@@ -363,7 +329,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
secp256k1_fe_mul_int(rzr, 2);
}
- secp256k1_gej_double_nonzero(r, a);
+ secp256k1_gej_double(r, a);
}
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {
@@ -400,7 +366,7 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 0);
}
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
@@ -450,7 +416,7 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 0);
}
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
@@ -509,7 +475,7 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a,
if (secp256k1_fe_normalizes_to_zero_var(&i)) {
secp256k1_gej_double_var(r, a, NULL);
} else {
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
@@ -680,7 +646,6 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r,
secp256k1_fe_storage_cmov(&r->y, &a->y, flag);
}
-#ifdef USE_ENDOMORPHISM
static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
static const secp256k1_fe beta = SECP256K1_FE_CONST(
0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul,
@@ -689,7 +654,6 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
*r = *a;
secp256k1_fe_mul(&r->x, &r->x, &beta);
}
-#endif
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
secp256k1_fe yz;
@@ -705,4 +669,25 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
return secp256k1_fe_is_quad_var(&yz);
}
+static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
+#ifdef EXHAUSTIVE_TEST_ORDER
+ secp256k1_gej out;
+ int i;
+
+ /* A very simple EC multiplication ladder that avoids a dependecy on ecmult. */
+ secp256k1_gej_set_infinity(&out);
+ for (i = 0; i < 32; ++i) {
+ secp256k1_gej_double_var(&out, &out, NULL);
+ if ((((uint32_t)EXHAUSTIVE_TEST_ORDER) >> (31 - i)) & 1) {
+ secp256k1_gej_add_ge_var(&out, &out, ge, NULL);
+ }
+ }
+ return secp256k1_gej_is_infinity(&out);
+#else
+ (void)ge;
+ /* The real secp256k1 group has cofactor 1, so the subgroup is the entire curve. */
+ return 1;
+#endif
+}
+
#endif /* SECP256K1_GROUP_IMPL_H */
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 782f97216c..409772587b 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -8,6 +8,7 @@
#define SECP256K1_HASH_IMPL_H
#include "hash.h"
+#include "util.h"
#include <stdlib.h>
#include <stdint.h>
@@ -27,9 +28,9 @@
(h) = t1 + t2; \
} while(0)
-#ifdef WORDS_BIGENDIAN
+#if defined(SECP256K1_BIG_ENDIAN)
#define BE32(x) (x)
-#else
+#elif defined(SECP256K1_LITTLE_ENDIAN)
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
#endif
@@ -163,6 +164,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out
memcpy(out32, (const unsigned char*)out, 32);
}
+/* Initializes a sha256 struct and writes the 64 byte string
+ * SHA256(tag)||SHA256(tag) into it. */
+static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
+ unsigned char buf[32];
+ secp256k1_sha256_initialize(hash);
+ secp256k1_sha256_write(hash, tag, taglen);
+ secp256k1_sha256_finalize(hash, buf);
+
+ secp256k1_sha256_initialize(hash);
+ secp256k1_sha256_write(hash, buf, 32);
+ secp256k1_sha256_write(hash, buf, 32);
+}
+
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
size_t n;
unsigned char rkey[64];
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index fe26e8fb69..e8d2aeab9a 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -80,7 +80,7 @@ void test_ecdh_generator_basepoint(void) {
/* compute "explicitly" */
CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1);
/* compare */
- CHECK(memcmp(output_ecdh, point_ser, 65) == 0);
+ CHECK(secp256k1_memcmp_var(output_ecdh, point_ser, 65) == 0);
/* compute using ECDH function with default hash function */
CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1);
@@ -90,7 +90,7 @@ void test_ecdh_generator_basepoint(void) {
secp256k1_sha256_write(&sha, point_ser, point_ser_len);
secp256k1_sha256_finalize(&sha, output_ser);
/* compare */
- CHECK(memcmp(output_ecdh, output_ser, 32) == 0);
+ CHECK(secp256k1_memcmp_var(output_ecdh, output_ser, 32) == 0);
}
}
diff --git a/src/secp256k1/src/modules/extrakeys/Makefile.am.include b/src/secp256k1/src/modules/extrakeys/Makefile.am.include
new file mode 100644
index 0000000000..0d901ec1f4
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/Makefile.am.include
@@ -0,0 +1,4 @@
+include_HEADERS += include/secp256k1_extrakeys.h
+noinst_HEADERS += src/modules/extrakeys/tests_impl.h
+noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
+noinst_HEADERS += src/modules/extrakeys/main_impl.h
diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h
new file mode 100644
index 0000000000..5378d2f301
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/main_impl.h
@@ -0,0 +1,251 @@
+/**********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_extrakeys.h"
+
+static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
+ return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey);
+}
+
+static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) {
+ secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge);
+}
+
+int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) {
+ secp256k1_ge pk;
+ secp256k1_fe x;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(input32 != NULL);
+
+ if (!secp256k1_fe_set_b32(&x, input32)) {
+ return 0;
+ }
+ if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) {
+ return 0;
+ }
+ if (!secp256k1_ge_is_in_correct_subgroup(&pk)) {
+ return 0;
+ }
+ secp256k1_xonly_pubkey_save(pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) {
+ secp256k1_ge pk;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(output32 != NULL);
+ memset(output32, 0, 32);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+ secp256k1_fe_get_b32(output32, &pk.x);
+ return 1;
+}
+
+/** Keeps a group element as is if it has an even Y and otherwise negates it.
+ * y_parity is set to 0 in the former case and to 1 in the latter case.
+ * Requires that the coordinates of r are normalized. */
+static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) {
+ int y_parity = 0;
+ VERIFY_CHECK(!secp256k1_ge_is_infinity(r));
+
+ if (secp256k1_fe_is_odd(&r->y)) {
+ secp256k1_fe_negate(&r->y, &r->y, 1);
+ y_parity = 1;
+ }
+ return y_parity;
+}
+
+int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) {
+ secp256k1_ge pk;
+ int tmp;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(xonly_pubkey != NULL);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+ tmp = secp256k1_extrakeys_ge_even_y(&pk);
+ if (pk_parity != NULL) {
+ *pk_parity = tmp;
+ }
+ secp256k1_xonly_pubkey_save(xonly_pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(output_pubkey != NULL);
+ memset(output_pubkey, 0, sizeof(*output_pubkey));
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(internal_pubkey != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+ || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+ return 0;
+ }
+ secp256k1_pubkey_save(output_pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+ unsigned char pk_expected32[32];
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(internal_pubkey != NULL);
+ ARG_CHECK(tweaked_pubkey32 != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+ || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+ return 0;
+ }
+ secp256k1_fe_normalize_var(&pk.x);
+ secp256k1_fe_normalize_var(&pk.y);
+ secp256k1_fe_get_b32(pk_expected32, &pk.x);
+
+ return secp256k1_memcmp_var(&pk_expected32, tweaked_pubkey32, 32) == 0
+ && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
+}
+
+static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
+ secp256k1_scalar_get_b32(&keypair->data[0], sk);
+ secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
+}
+
+
+static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
+ int ret;
+
+ ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
+ /* We can declassify ret here because sk is only zero if a keypair function
+ * failed (which zeroes the keypair) and its return value is ignored. */
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ ARG_CHECK(ret);
+ return ret;
+}
+
+/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
+ * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
+ * pk with dummy values. */
+static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
+ int ret;
+ const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];
+
+ /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
+ * invalid. */
+ secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
+ ret = secp256k1_pubkey_load(ctx, pk, pubkey);
+ if (sk != NULL) {
+ ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
+ }
+ if (!ret) {
+ *pk = secp256k1_ge_const_g;
+ if (sk != NULL) {
+ *sk = secp256k1_scalar_one;
+ }
+ }
+ return ret;
+}
+
+int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
+ secp256k1_scalar sk;
+ secp256k1_ge pk;
+ int ret = 0;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(keypair != NULL);
+ memset(keypair, 0, sizeof(*keypair));
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(seckey32 != NULL);
+
+ ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
+ secp256k1_keypair_save(keypair, &sk, &pk);
+ memczero(keypair, sizeof(*keypair), !ret);
+
+ secp256k1_scalar_clear(&sk);
+ return ret;
+}
+
+int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(keypair != NULL);
+
+ memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
+ return 1;
+}
+
+int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
+ secp256k1_ge pk;
+ int tmp;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(keypair != NULL);
+
+ if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
+ return 0;
+ }
+ tmp = secp256k1_extrakeys_ge_even_y(&pk);
+ if (pk_parity != NULL) {
+ *pk_parity = tmp;
+ }
+ secp256k1_xonly_pubkey_save(pubkey, &pk);
+
+ return 1;
+}
+
+int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+ secp256k1_scalar sk;
+ int y_parity;
+ int ret;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(keypair != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair);
+ memset(keypair, 0, sizeof(*keypair));
+
+ y_parity = secp256k1_extrakeys_ge_even_y(&pk);
+ if (y_parity == 1) {
+ secp256k1_scalar_negate(&sk, &sk);
+ }
+
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32);
+ ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32);
+
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ if (ret) {
+ secp256k1_keypair_save(keypair, &sk, &pk);
+ }
+
+ secp256k1_scalar_clear(&sk);
+ return ret;
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
new file mode 100644
index 0000000000..0e29bc6b09
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
+#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
+
+#include "src/modules/extrakeys/main_impl.h"
+#include "include/secp256k1_extrakeys.h"
+
+static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) {
+ secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
+ secp256k1_pubkey pubkey[EXHAUSTIVE_TEST_ORDER - 1];
+ secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
+ int parities[EXHAUSTIVE_TEST_ORDER - 1];
+ unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
+ int i;
+
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
+ secp256k1_fe fe;
+ secp256k1_scalar scalar_i;
+ unsigned char buf[33];
+ int parity;
+
+ secp256k1_scalar_set_int(&scalar_i, i);
+ secp256k1_scalar_get_b32(buf, &scalar_i);
+
+ /* Construct pubkey and keypair. */
+ CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey[i - 1], buf));
+
+ /* Construct serialized xonly_pubkey from keypair. */
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parities[i - 1], &keypair[i - 1]));
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
+
+ /* Parse the xonly_pubkey back and verify it matches the previously serialized value. */
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey[i - 1], xonly_pubkey_bytes[i - 1]));
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
+ CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
+
+ /* Construct the xonly_pubkey from the pubkey, and verify it matches the same. */
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pubkey[i - 1], &parity, &pubkey[i - 1]));
+ CHECK(parity == parities[i - 1]);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
+ CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
+
+ /* Compare the xonly_pubkey bytes against the precomputed group. */
+ secp256k1_fe_set_b32(&fe, xonly_pubkey_bytes[i - 1]);
+ CHECK(secp256k1_fe_equal_var(&fe, &group[i].x));
+
+ /* Check the parity against the precomputed group. */
+ fe = group[i].y;
+ secp256k1_fe_normalize_var(&fe);
+ CHECK(secp256k1_fe_is_odd(&fe) == parities[i - 1]);
+
+ /* Verify that the higher half is identical to the lower half mirrored. */
+ if (i > EXHAUSTIVE_TEST_ORDER / 2) {
+ CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - i - 1], 32) == 0);
+ CHECK(parities[i - 1] == 1 - parities[EXHAUSTIVE_TEST_ORDER - i - 1]);
+ }
+ }
+
+ /* TODO: keypair/xonly_pubkey tweak tests */
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h
new file mode 100644
index 0000000000..5ee135849e
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h
@@ -0,0 +1,524 @@
+/**********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+
+#include "secp256k1_extrakeys.h"
+
+static secp256k1_context* api_test_context(int flags, int *ecount) {
+ secp256k1_context *ctx0 = secp256k1_context_create(flags);
+ secp256k1_context_set_error_callback(ctx0, counting_illegal_callback_fn, ecount);
+ secp256k1_context_set_illegal_callback(ctx0, counting_illegal_callback_fn, ecount);
+ return ctx0;
+}
+
+void test_xonly_pubkey(void) {
+ secp256k1_pubkey pk;
+ secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
+ secp256k1_ge pk1;
+ secp256k1_ge pk2;
+ secp256k1_fe y;
+ unsigned char sk[32];
+ unsigned char xy_sk[32];
+ unsigned char buf32[32];
+ unsigned char ones32[32];
+ unsigned char zeros64[64] = { 0 };
+ int pk_parity;
+ int i;
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ secp256k1_testrand256(sk);
+ memset(ones32, 0xFF, 32);
+ secp256k1_testrand256(xy_sk);
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+
+ /* Test xonly_pubkey_from_pubkey */
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(sign, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, NULL, &pk_parity, &pk) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, NULL) == 0);
+ CHECK(ecount == 2);
+ memset(&pk, 0, sizeof(pk));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 0);
+ CHECK(ecount == 3);
+
+ /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */
+ memset(sk, 0, sizeof(sk));
+ sk[0] = 1;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_memcmp_var(&pk, &xonly_pk, sizeof(pk)) == 0);
+ CHECK(pk_parity == 0);
+
+ /* Choose a secret key such that pubkey and xonly_pubkey are each others
+ * negation. */
+ sk[0] = 2;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, &pk, sizeof(xonly_pk)) != 0);
+ CHECK(pk_parity == 1);
+ secp256k1_pubkey_load(ctx, &pk1, &pk);
+ secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk);
+ CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1);
+ secp256k1_fe_negate(&y, &pk2.y, 1);
+ CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1);
+
+ /* Test xonly_pubkey_serialize and xonly_pubkey_parse */
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_serialize(none, NULL, &xonly_pk) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, NULL) == 0);
+ CHECK(secp256k1_memcmp_var(buf32, zeros64, 32) == 0);
+ CHECK(ecount == 2);
+ {
+ /* A pubkey filled with 0s will fail to serialize due to pubkey_load
+ * special casing. */
+ secp256k1_xonly_pubkey pk_tmp;
+ memset(&pk_tmp, 0, sizeof(pk_tmp));
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk_tmp) == 0);
+ }
+ /* pubkey_load called illegal callback */
+ CHECK(ecount == 3);
+
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &xonly_pk) == 1);
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_parse(none, NULL, buf32) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, NULL) == 0);
+ CHECK(ecount == 2);
+
+ /* Serialization and parse roundtrip */
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0);
+
+ /* Test parsing invalid field elements */
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ /* Overflowing field element */
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, ones32) == 0);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ /* There's no point with x-coordinate 0 on secp256k1 */
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, zeros64) == 0);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ /* If a random 32-byte string can not be parsed with ec_pubkey_parse
+ * (because interpreted as X coordinate it does not correspond to a point on
+ * the curve) then xonly_pubkey_parse should fail as well. */
+ for (i = 0; i < count; i++) {
+ unsigned char rand33[33];
+ secp256k1_testrand256(&rand33[1]);
+ rand33[0] = SECP256K1_TAG_PUBKEY_EVEN;
+ if (!secp256k1_ec_pubkey_parse(ctx, &pk, rand33, 33)) {
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 0);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ } else {
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 1);
+ }
+ }
+ CHECK(ecount == 2);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_xonly_pubkey_tweak(void) {
+ unsigned char zeros64[64] = { 0 };
+ unsigned char overflows[32];
+ unsigned char sk[32];
+ secp256k1_pubkey internal_pk;
+ secp256k1_xonly_pubkey internal_xonly_pk;
+ secp256k1_pubkey output_pk;
+ int pk_parity;
+ unsigned char tweak[32];
+ int i;
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ memset(overflows, 0xff, sizeof(overflows));
+ secp256k1_testrand256(tweak);
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(none, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, NULL, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, NULL, tweak) == 0);
+ CHECK(ecount == 4);
+ /* NULL internal_xonly_pk zeroes the output_pk */
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, NULL) == 0);
+ CHECK(ecount == 5);
+ /* NULL tweak zeroes the output_pk */
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ /* Invalid tweak zeroes the output_pk */
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, overflows) == 0);
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ /* A zero tweak is fine */
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, zeros64) == 1);
+
+ /* Fails if the resulting key was infinity */
+ for (i = 0; i < count; i++) {
+ secp256k1_scalar scalar_tweak;
+ /* Because sk may be negated before adding, we need to try with tweak =
+ * sk as well as tweak = -sk. */
+ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
+ secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
+ secp256k1_scalar_get_b32(tweak, &scalar_tweak);
+ CHECK((secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, sk) == 0)
+ || (secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0));
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ }
+
+ /* Invalid pk with a valid tweak */
+ memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk));
+ secp256k1_testrand256(tweak);
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_xonly_pubkey_tweak_check(void) {
+ unsigned char zeros64[64] = { 0 };
+ unsigned char overflows[32];
+ unsigned char sk[32];
+ secp256k1_pubkey internal_pk;
+ secp256k1_xonly_pubkey internal_xonly_pk;
+ secp256k1_pubkey output_pk;
+ secp256k1_xonly_pubkey output_xonly_pk;
+ unsigned char output_pk32[32];
+ unsigned char buf32[32];
+ int pk_parity;
+ unsigned char tweak[32];
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ memset(overflows, 0xff, sizeof(overflows));
+ secp256k1_testrand256(tweak);
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(none, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(sign, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, NULL, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ /* invalid pk_parity value */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, 2, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, NULL, tweak) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, NULL) == 0);
+ CHECK(ecount == 5);
+
+ memset(tweak, 1, sizeof(tweak));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1);
+
+ /* Wrong pk_parity */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0);
+ /* Wrong public key */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+
+ /* Overflowing tweak not allowed */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0);
+ CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ CHECK(ecount == 5);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1
+ * additional pubkeys by calling tweak_add. Then verifies every tweak starting
+ * from the last pubkey. */
+#define N_PUBKEYS 32
+void test_xonly_pubkey_tweak_recursive(void) {
+ unsigned char sk[32];
+ secp256k1_pubkey pk[N_PUBKEYS];
+ unsigned char pk_serialized[32];
+ unsigned char tweak[N_PUBKEYS - 1][32];
+ int i;
+
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1);
+ /* Add tweaks */
+ for (i = 0; i < N_PUBKEYS - 1; i++) {
+ secp256k1_xonly_pubkey xonly_pk;
+ memset(tweak[i], i + 1, sizeof(tweak[i]));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1);
+ }
+
+ /* Verify tweaks */
+ for (i = N_PUBKEYS - 1; i > 0; i--) {
+ secp256k1_xonly_pubkey xonly_pk;
+ int pk_parity;
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1);
+ }
+}
+#undef N_PUBKEYS
+
+void test_keypair(void) {
+ unsigned char sk[32];
+ unsigned char zeros96[96] = { 0 };
+ unsigned char overflows[32];
+ secp256k1_keypair keypair;
+ secp256k1_pubkey pk, pk_tmp;
+ secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
+ int pk_parity, pk_parity_tmp;
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ CHECK(sizeof(zeros96) == sizeof(keypair));
+ memset(overflows, 0xFF, sizeof(overflows));
+
+ /* Test keypair_create */
+ ecount = 0;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 4);
+
+ /* Invalid secret key */
+ CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
+
+ /* Test keypair_pub */
+ ecount = 0;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+ CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0);
+
+ /* Using an invalid keypair is fine for keypair_pub */
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0);
+
+ /* keypair holds the same pubkey as pubkey_create */
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(&pk, &pk_tmp, sizeof(pk)) == 0);
+
+ /** Test keypair_xonly_pub **/
+ ecount = 0;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+ /* Using an invalid keypair will set the xonly_pk to 0 (first reset
+ * xonly_pk). */
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
+ CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+ CHECK(ecount == 3);
+
+ /** keypair holds the same xonly pubkey as pubkey_create **/
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
+ CHECK(pk_parity == pk_parity_tmp);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_keypair_add(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ unsigned char overflows[32];
+ unsigned char zeros96[96] = { 0 };
+ unsigned char tweak[32];
+ int i;
+ int ecount = 0;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ CHECK(sizeof(zeros96) == sizeof(keypair));
+ secp256k1_testrand256(sk);
+ secp256k1_testrand256(tweak);
+ memset(overflows, 0xFF, 32);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+
+ CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0);
+ CHECK(ecount == 4);
+ /* This does not set the keypair to zeroes */
+ CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) != 0);
+
+ /* Invalid tweak zeroes the keypair */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0);
+ CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0);
+
+ /* A zero tweak is fine */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1);
+
+ /* Fails if the resulting keypair was (sk=0, pk=infinity) */
+ for (i = 0; i < count; i++) {
+ secp256k1_scalar scalar_tweak;
+ secp256k1_keypair keypair_tmp;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memcpy(&keypair_tmp, &keypair, sizeof(keypair));
+ /* Because sk may be negated before adding, we need to try with tweak =
+ * sk as well as tweak = -sk. */
+ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
+ secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
+ secp256k1_scalar_get_b32(tweak, &scalar_tweak);
+ CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0)
+ || (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0));
+ CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0
+ || secp256k1_memcmp_var(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0);
+ }
+
+ /* Invalid keypair with a valid tweak */
+ memset(&keypair, 0, sizeof(keypair));
+ secp256k1_testrand256(tweak);
+ ecount = 0;
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0);
+ /* Only seckey part of keypair invalid */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memset(&keypair, 0, 32);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 2);
+ /* Only pubkey part of keypair invalid */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memset(&keypair.data[32], 0, 64);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 3);
+
+ /* Check that the keypair_tweak_add implementation is correct */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ for (i = 0; i < count; i++) {
+ secp256k1_xonly_pubkey internal_pk;
+ secp256k1_xonly_pubkey output_pk;
+ secp256k1_pubkey output_pk_xy;
+ secp256k1_pubkey output_pk_expected;
+ unsigned char pk32[32];
+ int pk_parity;
+
+ secp256k1_testrand256(tweak);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
+
+ /* Check that it passes xonly_pubkey_tweak_add_check */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1);
+
+ /* Check that the resulting pubkey matches xonly_pubkey_tweak_add */
+ CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1);
+ CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
+
+ /* Check that the secret key in the keypair is tweaked correctly */
+ CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
+ CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
+ }
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void run_extrakeys_tests(void) {
+ /* xonly key test cases */
+ test_xonly_pubkey();
+ test_xonly_pubkey_tweak();
+ test_xonly_pubkey_tweak_check();
+ test_xonly_pubkey_tweak_recursive();
+
+ /* keypair tests */
+ test_keypair();
+ test_keypair_add();
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/recovery/Makefile.am.include b/src/secp256k1/src/modules/recovery/Makefile.am.include
index bf23c26e71..e2d3f1248d 100644
--- a/src/secp256k1/src/modules/recovery/Makefile.am.include
+++ b/src/secp256k1/src/modules/recovery/Makefile.am.include
@@ -1,6 +1,7 @@
include_HEADERS += include/secp256k1_recovery.h
noinst_HEADERS += src/modules/recovery/main_impl.h
noinst_HEADERS += src/modules/recovery/tests_impl.h
+noinst_HEADERS += src/modules/recovery/tests_exhaustive_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_recover
bench_recover_SOURCES = src/bench_recover.c
diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
new file mode 100644
index 0000000000..a2f381d77a
--- /dev/null
+++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
@@ -0,0 +1,149 @@
+/**********************************************************************
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
+#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
+
+#include "src/modules/recovery/main_impl.h"
+#include "include/secp256k1_recovery.h"
+
+void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
+ int i, j, k;
+ uint64_t iter = 0;
+
+ /* Loop */
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */
+ for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */
+ if (skip_section(&iter)) continue;
+ for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
+ const int starting_k = k;
+ secp256k1_fe r_dot_y_normalized;
+ secp256k1_ecdsa_recoverable_signature rsig;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_scalar sk, msg, r, s, expected_r;
+ unsigned char sk32[32], msg32[32];
+ int expected_recid;
+ int recid;
+ int overflow;
+ secp256k1_scalar_set_int(&msg, i);
+ secp256k1_scalar_set_int(&sk, j);
+ secp256k1_scalar_get_b32(sk32, &sk);
+ secp256k1_scalar_get_b32(msg32, &msg);
+
+ secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+
+ /* Check directly */
+ secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
+ r_from_k(&expected_r, group, k, &overflow);
+ CHECK(r == expected_r);
+ CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
+ /* The recid's second bit is for conveying overflow (R.x value >= group order).
+ * In the actual secp256k1 this is an astronomically unlikely event, but in the
+ * small group used here, it will be the case for all points except the ones where
+ * R.x=1 (which the group is specifically selected to have).
+ * Note that this isn't actually useful; full recovery would need to convey
+ * floor(R.x / group_order), but only one bit is used as that is sufficient
+ * in the real group. */
+ expected_recid = overflow ? 2 : 0;
+ r_dot_y_normalized = group[k].y;
+ secp256k1_fe_normalize(&r_dot_y_normalized);
+ /* Also the recovery id is flipped depending if we hit the low-s branch */
+ if ((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER) {
+ expected_recid |= secp256k1_fe_is_odd(&r_dot_y_normalized);
+ } else {
+ expected_recid |= !secp256k1_fe_is_odd(&r_dot_y_normalized);
+ }
+ CHECK(recid == expected_recid);
+
+ /* Convert to a standard sig then check */
+ secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
+ secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
+ /* Note that we compute expected_r *after* signing -- this is important
+ * because our nonce-computing function function might change k during
+ * signing. */
+ r_from_k(&expected_r, group, k, NULL);
+ CHECK(r == expected_r);
+ CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
+
+ /* Overflow means we've tried every possible nonce */
+ if (k < starting_k) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
+ /* This is essentially a copy of test_exhaustive_verify, with recovery added */
+ int s, r, msg, key;
+ uint64_t iter = 0;
+ for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) {
+ for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) {
+ for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) {
+ for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) {
+ secp256k1_ge nonconst_ge;
+ secp256k1_ecdsa_recoverable_signature rsig;
+ secp256k1_ecdsa_signature sig;
+ secp256k1_pubkey pk;
+ secp256k1_scalar sk_s, msg_s, r_s, s_s;
+ secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
+ int recid = 0;
+ int k, should_verify;
+ unsigned char msg32[32];
+
+ if (skip_section(&iter)) continue;
+
+ secp256k1_scalar_set_int(&s_s, s);
+ secp256k1_scalar_set_int(&r_s, r);
+ secp256k1_scalar_set_int(&msg_s, msg);
+ secp256k1_scalar_set_int(&sk_s, key);
+ secp256k1_scalar_get_b32(msg32, &msg_s);
+
+ /* Verify by hand */
+ /* Run through every k value that gives us this r and check that *one* works.
+ * Note there could be none, there could be multiple, ECDSA is weird. */
+ should_verify = 0;
+ for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
+ secp256k1_scalar check_x_s;
+ r_from_k(&check_x_s, group, k, NULL);
+ if (r_s == check_x_s) {
+ secp256k1_scalar_set_int(&s_times_k_s, k);
+ secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
+ secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
+ secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
+ should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
+ }
+ }
+ /* nb we have a "high s" rule */
+ should_verify &= !secp256k1_scalar_is_high(&s_s);
+
+ /* We would like to try recovering the pubkey and checking that it matches,
+ * but pubkey recovery is impossible in the exhaustive tests (the reason
+ * being that there are 12 nonzero r values, 12 nonzero points, and no
+ * overlap between the sets, so there are no valid signatures). */
+
+ /* Verify by converting to a standard signature and calling verify */
+ secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
+ secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
+ memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
+ secp256k1_pubkey_save(&pk, &nonconst_ge);
+ CHECK(should_verify ==
+ secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
+ }
+ }
+ }
+ }
+}
+
+static void test_exhaustive_recovery(const secp256k1_context *ctx, const secp256k1_ge *group) {
+ test_exhaustive_recovery_sign(ctx, group);
+ test_exhaustive_recovery_verify(ctx, group);
+}
+
+#endif /* SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H */
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 38a533a755..09cae38403 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -25,7 +25,7 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c
}
/* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */
memset(nonce32, 1, 32);
- return secp256k1_rand_bits(1);
+ return secp256k1_testrand_bits(1);
}
void test_ecdsa_recovery_api(void) {
@@ -184,7 +184,7 @@ void test_ecdsa_recovery_end_to_end(void) {
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
- CHECK(memcmp(&signature[4], &signature[0], 64) == 0);
+ CHECK(secp256k1_memcmp_var(&signature[4], &signature[0], 64) == 0);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1);
memset(&rsignature[4], 0, sizeof(rsignature[4]));
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
@@ -193,16 +193,16 @@ void test_ecdsa_recovery_end_to_end(void) {
/* Parse compact (with recovery id) and recover. */
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1);
- CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
/* Serialize/destroy/parse signature and verify again. */
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
- sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
+ sig[secp256k1_testrand_bits(6)] += 1 + secp256k1_testrand_int(255);
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0);
/* Recover again */
CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 ||
- memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
+ secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
}
/* Tests several edge cases. */
diff --git a/src/secp256k1/src/modules/schnorrsig/Makefile.am.include b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include
new file mode 100644
index 0000000000..568bcc3523
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include
@@ -0,0 +1,9 @@
+include_HEADERS += include/secp256k1_schnorrsig.h
+noinst_HEADERS += src/modules/schnorrsig/main_impl.h
+noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
+noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h
+if USE_BENCHMARK
+noinst_PROGRAMS += bench_schnorrsig
+bench_schnorrsig_SOURCES = src/bench_schnorrsig.c
+bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
+endif
diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h
new file mode 100644
index 0000000000..b0d8481f9b
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h
@@ -0,0 +1,239 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_schnorrsig.h"
+#include "hash.h"
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */
+static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x46615b35ul;
+ sha->s[1] = 0xf4bfbff7ul;
+ sha->s[2] = 0x9f8dc671ul;
+ sha->s[3] = 0x83627ab3ul;
+ sha->s[4] = 0x60217180ul;
+ sha->s[5] = 0x57358661ul;
+ sha->s[6] = 0x21a29e54ul;
+ sha->s[7] = 0x68b07b4cul;
+
+ sha->bytes = 64;
+}
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */
+static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x24dd3219ul;
+ sha->s[1] = 0x4eba7e70ul;
+ sha->s[2] = 0xca0fabb9ul;
+ sha->s[3] = 0x0fa3166dul;
+ sha->s[4] = 0x3afbe4b1ul;
+ sha->s[5] = 0x4c44df97ul;
+ sha->s[6] = 0x4aac2739ul;
+ sha->s[7] = 0x249e850aul;
+
+ sha->bytes = 64;
+}
+
+/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
+ * by using the correct tagged hash function. */
+static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0";
+
+static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ secp256k1_sha256 sha;
+ unsigned char masked_key[32];
+ int i;
+
+ if (algo16 == NULL) {
+ return 0;
+ }
+
+ if (data != NULL) {
+ secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha);
+ secp256k1_sha256_write(&sha, data, 32);
+ secp256k1_sha256_finalize(&sha, masked_key);
+ for (i = 0; i < 32; i++) {
+ masked_key[i] ^= key32[i];
+ }
+ }
+
+ /* Tag the hash with algo16 which is important to avoid nonce reuse across
+ * algorithms. If this nonce function is used in BIP-340 signing as defined
+ * in the spec, an optimized tagging implementation is used. */
+ if (secp256k1_memcmp_var(algo16, bip340_algo16, 16) == 0) {
+ secp256k1_nonce_function_bip340_sha256_tagged(&sha);
+ } else {
+ int algo16_len = 16;
+ /* Remove terminating null bytes */
+ while (algo16_len > 0 && !algo16[algo16_len - 1]) {
+ algo16_len--;
+ }
+ secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len);
+ }
+
+ /* Hash (masked-)key||pk||msg using the tagged hash as per the spec */
+ if (data != NULL) {
+ secp256k1_sha256_write(&sha, masked_key, 32);
+ } else {
+ secp256k1_sha256_write(&sha, key32, 32);
+ }
+ secp256k1_sha256_write(&sha, xonly_pk32, 32);
+ secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_finalize(&sha, nonce32);
+ return 1;
+}
+
+const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340;
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */
+static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x9cecba11ul;
+ sha->s[1] = 0x23925381ul;
+ sha->s[2] = 0x11679112ul;
+ sha->s[3] = 0xd1627e0ful;
+ sha->s[4] = 0x97c87550ul;
+ sha->s[5] = 0x003cc765ul;
+ sha->s[6] = 0x90f61164ul;
+ sha->s[7] = 0x33e9b66aul;
+ sha->bytes = 64;
+}
+
+static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg32, const unsigned char *pubkey32)
+{
+ unsigned char buf[32];
+ secp256k1_sha256 sha;
+
+ /* tagged hash(r.x, pk.x, msg32) */
+ secp256k1_schnorrsig_sha256_tagged(&sha);
+ secp256k1_sha256_write(&sha, r32, 32);
+ secp256k1_sha256_write(&sha, pubkey32, 32);
+ secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_finalize(&sha, buf);
+ /* Set scalar e to the challenge hash modulo the curve order as per
+ * BIP340. */
+ secp256k1_scalar_set_b32(e, buf, NULL);
+}
+
+int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
+ secp256k1_scalar sk;
+ secp256k1_scalar e;
+ secp256k1_scalar k;
+ secp256k1_gej rj;
+ secp256k1_ge pk;
+ secp256k1_ge r;
+ unsigned char buf[32] = { 0 };
+ unsigned char pk_buf[32];
+ unsigned char seckey[32];
+ int ret = 1;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(sig64 != NULL);
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(keypair != NULL);
+
+ if (noncefp == NULL) {
+ noncefp = secp256k1_nonce_function_bip340;
+ }
+
+ ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
+ /* Because we are signing for a x-only pubkey, the secret key is negated
+ * before signing if the point corresponding to the secret key does not
+ * have an even Y. */
+ if (secp256k1_fe_is_odd(&pk.y)) {
+ secp256k1_scalar_negate(&sk, &sk);
+ }
+
+ secp256k1_scalar_get_b32(seckey, &sk);
+ secp256k1_fe_get_b32(pk_buf, &pk.x);
+ ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata);
+ secp256k1_scalar_set_b32(&k, buf, NULL);
+ ret &= !secp256k1_scalar_is_zero(&k);
+ secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
+ secp256k1_ge_set_gej(&r, &rj);
+
+ /* We declassify r to allow using it as a branch point. This is fine
+ * because r is not a secret. */
+ secp256k1_declassify(ctx, &r, sizeof(r));
+ secp256k1_fe_normalize_var(&r.y);
+ if (secp256k1_fe_is_odd(&r.y)) {
+ secp256k1_scalar_negate(&k, &k);
+ }
+ secp256k1_fe_normalize_var(&r.x);
+ secp256k1_fe_get_b32(&sig64[0], &r.x);
+
+ secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, pk_buf);
+ secp256k1_scalar_mul(&e, &e, &sk);
+ secp256k1_scalar_add(&e, &e, &k);
+ secp256k1_scalar_get_b32(&sig64[32], &e);
+
+ memczero(sig64, 64, !ret);
+ secp256k1_scalar_clear(&k);
+ secp256k1_scalar_clear(&sk);
+ memset(seckey, 0, sizeof(seckey));
+
+ return ret;
+}
+
+int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
+ secp256k1_scalar s;
+ secp256k1_scalar e;
+ secp256k1_gej rj;
+ secp256k1_ge pk;
+ secp256k1_gej pkj;
+ secp256k1_fe rx;
+ secp256k1_ge r;
+ unsigned char buf[32];
+ int overflow;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(sig64 != NULL);
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
+ return 0;
+ }
+
+ secp256k1_scalar_set_b32(&s, &sig64[32], &overflow);
+ if (overflow) {
+ return 0;
+ }
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+
+ /* Compute e. */
+ secp256k1_fe_get_b32(buf, &pk.x);
+ secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, buf);
+
+ /* Compute rj = s*G + (-e)*pkj */
+ secp256k1_scalar_negate(&e, &e);
+ secp256k1_gej_set_ge(&pkj, &pk);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
+
+ secp256k1_ge_set_gej_var(&r, &rj);
+ if (secp256k1_ge_is_infinity(&r)) {
+ return 0;
+ }
+
+ secp256k1_fe_normalize_var(&r.y);
+ return !secp256k1_fe_is_odd(&r.y) &&
+ secp256k1_fe_equal_var(&rx, &r.x);
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
new file mode 100644
index 0000000000..4bf0bc1680
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
@@ -0,0 +1,206 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
+#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
+
+#include "include/secp256k1_schnorrsig.h"
+#include "src/modules/schnorrsig/main_impl.h"
+
+static const unsigned char invalid_pubkey_bytes[][32] = {
+ /* 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, 0, 0
+ },
+ /* 2 */
+ {
+ 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, 2
+ },
+ /* order */
+ {
+ 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,
+ ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 24) & 0xFF,
+ ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 16) & 0xFF,
+ ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 8) & 0xFF,
+ (EXHAUSTIVE_TEST_ORDER + 0UL) & 0xFF
+ },
+ /* order + 1 */
+ {
+ 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,
+ ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 24) & 0xFF,
+ ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 16) & 0xFF,
+ ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 8) & 0xFF,
+ (EXHAUSTIVE_TEST_ORDER + 1UL) & 0xFF
+ },
+ /* field size */
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F
+ },
+ /* field size + 1 (note that 1 is legal) */
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30
+ },
+ /* 2^256 - 1 */
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ }
+};
+
+#define NUM_INVALID_KEYS (sizeof(invalid_pubkey_bytes) / sizeof(invalid_pubkey_bytes[0]))
+
+static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
+ const unsigned char *key32, const unsigned char *xonly_pk32,
+ const unsigned char *algo16, void* data) {
+ secp256k1_scalar s;
+ int *idata = data;
+ (void)msg32;
+ (void)key32;
+ (void)xonly_pk32;
+ (void)algo16;
+ secp256k1_scalar_set_int(&s, *idata);
+ secp256k1_scalar_get_b32(nonce32, &s);
+ return 1;
+}
+
+static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey* pubkeys, unsigned char (*xonly_pubkey_bytes)[32], const int* parities) {
+ int d;
+ uint64_t iter = 0;
+ /* Iterate over the possible public keys to verify against (through their corresponding DL d). */
+ for (d = 1; d <= EXHAUSTIVE_TEST_ORDER / 2; ++d) {
+ int actual_d;
+ unsigned k;
+ unsigned char pk32[32];
+ memcpy(pk32, xonly_pubkey_bytes[d - 1], 32);
+ actual_d = parities[d - 1] ? EXHAUSTIVE_TEST_ORDER - d : d;
+ /* Iterate over the possible valid first 32 bytes in the signature, through their corresponding DL k.
+ Values above EXHAUSTIVE_TEST_ORDER/2 refer to the entries in invalid_pubkey_bytes. */
+ for (k = 1; k <= EXHAUSTIVE_TEST_ORDER / 2 + NUM_INVALID_KEYS; ++k) {
+ unsigned char sig64[64];
+ int actual_k = -1;
+ int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
+ int e_count_done = 0;
+ if (skip_section(&iter)) continue;
+ if (k <= EXHAUSTIVE_TEST_ORDER / 2) {
+ memcpy(sig64, xonly_pubkey_bytes[k - 1], 32);
+ actual_k = parities[k - 1] ? EXHAUSTIVE_TEST_ORDER - k : k;
+ } else {
+ memcpy(sig64, invalid_pubkey_bytes[k - 1 - EXHAUSTIVE_TEST_ORDER / 2], 32);
+ }
+ /* Randomly generate messages until all challenges have been hit. */
+ while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
+ secp256k1_scalar e;
+ unsigned char msg32[32];
+ secp256k1_testrand256(msg32);
+ secp256k1_schnorrsig_challenge(&e, sig64, msg32, pk32);
+ /* Only do work if we hit a challenge we haven't tried before. */
+ if (!e_done[e]) {
+ /* Iterate over the possible valid last 32 bytes in the signature.
+ 0..order=that s value; order+1=random bytes */
+ int count_valid = 0, s;
+ for (s = 0; s <= EXHAUSTIVE_TEST_ORDER + 1; ++s) {
+ int expect_valid, valid;
+ if (s <= EXHAUSTIVE_TEST_ORDER) {
+ secp256k1_scalar s_s;
+ secp256k1_scalar_set_int(&s_s, s);
+ secp256k1_scalar_get_b32(sig64 + 32, &s_s);
+ expect_valid = actual_k != -1 && s != EXHAUSTIVE_TEST_ORDER &&
+ (s_s == (actual_k + actual_d * e) % EXHAUSTIVE_TEST_ORDER);
+ } else {
+ secp256k1_testrand256(sig64 + 32);
+ expect_valid = 0;
+ }
+ valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, &pubkeys[d - 1]);
+ CHECK(valid == expect_valid);
+ count_valid += valid;
+ }
+ /* Exactly one s value must verify, unless R is illegal. */
+ CHECK(count_valid == (actual_k != -1));
+ /* Don't retry other messages that result in the same challenge. */
+ e_done[e] = 1;
+ ++e_count_done;
+ }
+ }
+ }
+ }
+}
+
+static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
+ int d, k;
+ uint64_t iter = 0;
+ /* Loop over keys. */
+ for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
+ int actual_d = d;
+ if (parities[d - 1]) actual_d = EXHAUSTIVE_TEST_ORDER - d;
+ /* Loop over nonces. */
+ for (k = 1; k < EXHAUSTIVE_TEST_ORDER; ++k) {
+ int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
+ int e_count_done = 0;
+ unsigned char msg32[32];
+ unsigned char sig64[64];
+ int actual_k = k;
+ if (skip_section(&iter)) continue;
+ if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
+ /* Generate random messages until all challenges have been tried. */
+ while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
+ secp256k1_scalar e;
+ secp256k1_testrand256(msg32);
+ secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, xonly_pubkey_bytes[d - 1]);
+ /* Only do work if we hit a challenge we haven't tried before. */
+ if (!e_done[e]) {
+ secp256k1_scalar expected_s = (actual_k + e * actual_d) % EXHAUSTIVE_TEST_ORDER;
+ unsigned char expected_s_bytes[32];
+ secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
+ /* Invoke the real function to construct a signature. */
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig64, msg32, &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
+ /* The first 32 bytes must match the xonly pubkey for the specified k. */
+ CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
+ /* The last 32 bytes must match the expected s value. */
+ CHECK(secp256k1_memcmp_var(sig64 + 32, expected_s_bytes, 32) == 0);
+ /* Don't retry other messages that result in the same challenge. */
+ e_done[e] = 1;
+ ++e_count_done;
+ }
+ }
+ }
+ }
+}
+
+static void test_exhaustive_schnorrsig(const secp256k1_context *ctx) {
+ secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
+ secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
+ int parity[EXHAUSTIVE_TEST_ORDER - 1];
+ unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
+ unsigned i;
+
+ /* Verify that all invalid_pubkey_bytes are actually invalid. */
+ for (i = 0; i < NUM_INVALID_KEYS; ++i) {
+ secp256k1_xonly_pubkey pk;
+ CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk, invalid_pubkey_bytes[i]));
+ }
+
+ /* Construct keypairs and xonly-pubkeys for the entire group. */
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; ++i) {
+ secp256k1_scalar scalar_i;
+ unsigned char buf[32];
+ secp256k1_scalar_set_int(&scalar_i, i);
+ secp256k1_scalar_get_b32(buf, &scalar_i);
+ CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parity[i - 1], &keypair[i - 1]));
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
+ }
+
+ test_exhaustive_schnorrsig_sign(ctx, xonly_pubkey_bytes, keypair, parity);
+ test_exhaustive_schnorrsig_verify(ctx, xonly_pubkey, xonly_pubkey_bytes, parity);
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
new file mode 100644
index 0000000000..f522fcb320
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
@@ -0,0 +1,806 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+
+#include "secp256k1_schnorrsig.h"
+
+/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
+ * bytes) changes the hash function
+ */
+void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
+ unsigned char nonces[2][32];
+ CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1);
+ secp256k1_testrand_flip(args[n_flip], n_bytes);
+ CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1);
+ CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0);
+}
+
+/* Tests for the equality of two sha256 structs. This function only produces a
+ * correct result if an integer multiple of 64 many bytes have been written
+ * into the hash functions. */
+void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) {
+ /* Is buffer fully consumed? */
+ CHECK((sha1->bytes & 0x3F) == 0);
+
+ CHECK(sha1->bytes == sha2->bytes);
+ CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0);
+}
+
+void run_nonce_function_bip340_tests(void) {
+ unsigned char tag[13] = "BIP0340/nonce";
+ unsigned char aux_tag[11] = "BIP0340/aux";
+ unsigned char algo16[16] = "BIP0340/nonce\0\0\0";
+ secp256k1_sha256 sha;
+ secp256k1_sha256 sha_optimized;
+ unsigned char nonce[32];
+ unsigned char msg[32];
+ unsigned char key[32];
+ unsigned char pk[32];
+ unsigned char aux_rand[32];
+ unsigned char *args[5];
+ int i;
+
+ /* Check that hash initialized by
+ * secp256k1_nonce_function_bip340_sha256_tagged has the expected
+ * state. */
+ secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag));
+ secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+
+ /* Check that hash initialized by
+ * secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected
+ * state. */
+ secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag));
+ secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+
+ secp256k1_testrand256(msg);
+ secp256k1_testrand256(key);
+ secp256k1_testrand256(pk);
+ secp256k1_testrand256(aux_rand);
+
+ /* Check that a bitflip in an argument results in different nonces. */
+ args[0] = msg;
+ args[1] = key;
+ args[2] = pk;
+ args[3] = algo16;
+ args[4] = aux_rand;
+ for (i = 0; i < count; i++) {
+ nonce_function_bip340_bitflip(args, 0, 32);
+ nonce_function_bip340_bitflip(args, 1, 32);
+ nonce_function_bip340_bitflip(args, 2, 32);
+ /* Flip algo16 special case "BIP0340/nonce" */
+ nonce_function_bip340_bitflip(args, 3, 16);
+ /* Flip algo16 again */
+ nonce_function_bip340_bitflip(args, 3, 16);
+ nonce_function_bip340_bitflip(args, 4, 32);
+ }
+
+ /* NULL algo16 is disallowed */
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0);
+ /* Empty algo16 is fine */
+ memset(algo16, 0x00, 16);
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ /* algo16 with terminating null bytes is fine */
+ algo16[1] = 65;
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ /* Other algo16 is fine */
+ memset(algo16, 0xFF, 16);
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+
+ /* NULL aux_rand argument is allowed. */
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+}
+
+void test_schnorrsig_api(void) {
+ unsigned char sk1[32];
+ unsigned char sk2[32];
+ unsigned char sk3[32];
+ unsigned char msg[32];
+ secp256k1_keypair keypairs[3];
+ secp256k1_keypair invalid_keypair = { 0 };
+ secp256k1_xonly_pubkey pk[3];
+ secp256k1_xonly_pubkey zero_pk;
+ unsigned char sig[64];
+
+ /** setup **/
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ int ecount;
+
+ secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
+
+ secp256k1_testrand256(sk1);
+ secp256k1_testrand256(sk2);
+ secp256k1_testrand256(sk3);
+ secp256k1_testrand256(msg);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[0], sk1) == 1);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[1], sk2) == 1);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[2], sk3) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[0], NULL, &keypairs[0]) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[1], NULL, &keypairs[1]) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[2], NULL, &keypairs[2]) == 1);
+ memset(&zero_pk, 0, sizeof(zero_pk));
+
+ /** main test body **/
+ ecount = 0;
+ CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL, NULL) == 0);
+ CHECK(ecount == 6);
+
+ ecount = 0;
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(none, sig, msg, &pk[0]) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, &pk[0]) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &pk[0]) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, &pk[0]) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0);
+ CHECK(ecount == 6);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(vrfy);
+ secp256k1_context_destroy(both);
+}
+
+/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the
+ * expected state. */
+void test_schnorrsig_sha256_tagged(void) {
+ char tag[17] = "BIP0340/challenge";
+ secp256k1_sha256 sha;
+ secp256k1_sha256 sha_optimized;
+
+ secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag));
+ secp256k1_schnorrsig_sha256_tagged(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+}
+
+/* Helper function for schnorrsig_bip_vectors
+ * Signs the message and checks that it's the same as expected_sig. */
+void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg, const unsigned char *expected_sig) {
+ unsigned char sig[64];
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey pk, pk_expected;
+
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, aux_rand));
+ CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0);
+
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized));
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
+ CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &pk));
+}
+
+/* Helper function for schnorrsig_bip_vectors
+ * Checks that both verify and verify_batch (TODO) return the same value as expected. */
+void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) {
+ secp256k1_xonly_pubkey pk;
+
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized));
+ CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk));
+}
+
+/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See
+ * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */
+void test_schnorrsig_bip_vectors(void) {
+ {
+ /* Test vector 0 */
+ const unsigned char sk[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+ };
+ const unsigned char pk[32] = {
+ 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10,
+ 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29,
+ 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0,
+ 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9
+ };
+ unsigned char aux_rand[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const unsigned char msg[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const unsigned char sig[64] = {
+ 0xE9, 0x07, 0x83, 0x1F, 0x80, 0x84, 0x8D, 0x10,
+ 0x69, 0xA5, 0x37, 0x1B, 0x40, 0x24, 0x10, 0x36,
+ 0x4B, 0xDF, 0x1C, 0x5F, 0x83, 0x07, 0xB0, 0x08,
+ 0x4C, 0x55, 0xF1, 0xCE, 0x2D, 0xCA, 0x82, 0x15,
+ 0x25, 0xF6, 0x6A, 0x4A, 0x85, 0xEA, 0x8B, 0x71,
+ 0xE4, 0x82, 0xA7, 0x4F, 0x38, 0x2D, 0x2C, 0xE5,
+ 0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47,
+ 0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 1 */
+ const unsigned char sk[32] = {
+ 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A,
+ 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7,
+ 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56,
+ 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF
+ };
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ unsigned char aux_rand[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x68, 0x96, 0xBD, 0x60, 0xEE, 0xAE, 0x29, 0x6D,
+ 0xB4, 0x8A, 0x22, 0x9F, 0xF7, 0x1D, 0xFE, 0x07,
+ 0x1B, 0xDE, 0x41, 0x3E, 0x6D, 0x43, 0xF9, 0x17,
+ 0xDC, 0x8D, 0xCF, 0x8C, 0x78, 0xDE, 0x33, 0x41,
+ 0x89, 0x06, 0xD1, 0x1A, 0xC9, 0x76, 0xAB, 0xCC,
+ 0xB2, 0x0B, 0x09, 0x12, 0x92, 0xBF, 0xF4, 0xEA,
+ 0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C,
+ 0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 2 */
+ const unsigned char sk[32] = {
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9
+ };
+ const unsigned char pk[32] = {
+ 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13,
+ 0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC,
+ 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9,
+ 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8
+ };
+ unsigned char aux_rand[32] = {
+ 0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE,
+ 0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC,
+ 0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C,
+ 0x4B, 0x0B, 0x62, 0xD7, 0x98, 0xE6, 0xD9, 0x06
+ };
+ const unsigned char msg[32] = {
+ 0x7E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A,
+ 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D,
+ 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33,
+ 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C
+ };
+ const unsigned char sig[64] = {
+ 0x58, 0x31, 0xAA, 0xEE, 0xD7, 0xB4, 0x4B, 0xB7,
+ 0x4E, 0x5E, 0xAB, 0x94, 0xBA, 0x9D, 0x42, 0x94,
+ 0xC4, 0x9B, 0xCF, 0x2A, 0x60, 0x72, 0x8D, 0x8B,
+ 0x4C, 0x20, 0x0F, 0x50, 0xDD, 0x31, 0x3C, 0x1B,
+ 0xAB, 0x74, 0x58, 0x79, 0xA5, 0xAD, 0x95, 0x4A,
+ 0x72, 0xC4, 0x5A, 0x91, 0xC3, 0xA5, 0x1D, 0x3C,
+ 0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E,
+ 0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 3 */
+ const unsigned char sk[32] = {
+ 0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81,
+ 0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0,
+ 0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54,
+ 0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10
+ };
+ const unsigned char pk[32] = {
+ 0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25,
+ 0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD,
+ 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A,
+ 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17
+ };
+ unsigned char aux_rand[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ const unsigned char msg[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ const unsigned char sig[64] = {
+ 0x7E, 0xB0, 0x50, 0x97, 0x57, 0xE2, 0x46, 0xF1,
+ 0x94, 0x49, 0x88, 0x56, 0x51, 0x61, 0x1C, 0xB9,
+ 0x65, 0xEC, 0xC1, 0xA1, 0x87, 0xDD, 0x51, 0xB6,
+ 0x4F, 0xDA, 0x1E, 0xDC, 0x96, 0x37, 0xD5, 0xEC,
+ 0x97, 0x58, 0x2B, 0x9C, 0xB1, 0x3D, 0xB3, 0x93,
+ 0x37, 0x05, 0xB3, 0x2B, 0xA9, 0x82, 0xAF, 0x5A,
+ 0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27,
+ 0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 4 */
+ const unsigned char pk[32] = {
+ 0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12,
+ 0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83,
+ 0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA,
+ 0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9
+ };
+ const unsigned char msg[32] = {
+ 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2,
+ 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24,
+ 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B,
+ 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F,
+ 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28,
+ 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63,
+ 0x76, 0xAF, 0xB1, 0x54, 0x8A, 0xF6, 0x03, 0xB3,
+ 0xEB, 0x45, 0xC9, 0xF8, 0x20, 0x7D, 0xEE, 0x10,
+ 0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93,
+ 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 5 */
+ const unsigned char pk[32] = {
+ 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50,
+ 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21,
+ 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87,
+ 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34
+ };
+ secp256k1_xonly_pubkey pk_parsed;
+ /* No need to check the signature of the test vector as parsing the pubkey already fails */
+ CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
+ }
+ {
+ /* Test vector 6 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0xFF, 0xF9, 0x7B, 0xD5, 0x75, 0x5E, 0xEE, 0xA4,
+ 0x20, 0x45, 0x3A, 0x14, 0x35, 0x52, 0x35, 0xD3,
+ 0x82, 0xF6, 0x47, 0x2F, 0x85, 0x68, 0xA1, 0x8B,
+ 0x2F, 0x05, 0x7A, 0x14, 0x60, 0x29, 0x75, 0x56,
+ 0x3C, 0xC2, 0x79, 0x44, 0x64, 0x0A, 0xC6, 0x07,
+ 0xCD, 0x10, 0x7A, 0xE1, 0x09, 0x23, 0xD9, 0xEF,
+ 0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E,
+ 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 7 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x1F, 0xA6, 0x2E, 0x33, 0x1E, 0xDB, 0xC2, 0x1C,
+ 0x39, 0x47, 0x92, 0xD2, 0xAB, 0x11, 0x00, 0xA7,
+ 0xB4, 0x32, 0xB0, 0x13, 0xDF, 0x3F, 0x6F, 0xF4,
+ 0xF9, 0x9F, 0xCB, 0x33, 0xE0, 0xE1, 0x51, 0x5F,
+ 0x28, 0x89, 0x0B, 0x3E, 0xDB, 0x6E, 0x71, 0x89,
+ 0xB6, 0x30, 0x44, 0x8B, 0x51, 0x5C, 0xE4, 0xF8,
+ 0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35,
+ 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 8 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
+ 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
+ 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
+ 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
+ 0x96, 0x17, 0x64, 0xB3, 0xAA, 0x9B, 0x2F, 0xFC,
+ 0xB6, 0xEF, 0x94, 0x7B, 0x68, 0x87, 0xA2, 0x26,
+ 0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C,
+ 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 9 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x3D, 0xDA, 0x83, 0x28, 0xAF, 0x9C, 0x23,
+ 0xA9, 0x4C, 0x1F, 0xEE, 0xCF, 0xD1, 0x23, 0xBA,
+ 0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC,
+ 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 10 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x15, 0xFB, 0xAF, 0x5A, 0xE2, 0x88, 0x64,
+ 0x01, 0x3C, 0x09, 0x97, 0x42, 0xDE, 0xAD, 0xB4,
+ 0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9,
+ 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 11 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A,
+ 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB,
+ 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7,
+ 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D,
+ 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
+ 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
+ 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
+ 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 12 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
+ 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
+ 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
+ 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
+ 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 13 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
+ 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
+ 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
+ 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+ 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
+ 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 14 */
+ const unsigned char pk[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30
+ };
+ secp256k1_xonly_pubkey pk_parsed;
+ /* No need to check the signature of the test vector as parsing the pubkey already fails */
+ CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
+ }
+}
+
+/* Nonce function that returns constant 0 */
+static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+ (void) nonce32;
+ return 0;
+}
+
+/* Nonce function that sets nonce to 0 */
+static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+
+ memset(nonce32, 0, 32);
+ return 1;
+}
+
+/* Nonce function that sets nonce to 0xFF...0xFF */
+static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+
+ memset(nonce32, 0xFF, 32);
+ return 1;
+}
+
+void test_schnorrsig_sign(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ const unsigned char msg[32] = "this is a msg for a schnorrsig..";
+ unsigned char sig[64];
+ unsigned char zeros64[64] = { 0 };
+
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+
+ /* Test different nonce functions */
+ memset(sig, 1, sizeof(sig));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_failing, NULL) == 0);
+ CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
+ memset(&sig, 1, sizeof(sig));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_0, NULL) == 0);
+ CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_overflowing, NULL) == 1);
+ CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) != 0);
+}
+
+#define N_SIGS 3
+/* Creates N_SIGS valid signatures and verifies them with verify and
+ * verify_batch (TODO). Then flips some bits and checks that verification now
+ * fails. */
+void test_schnorrsig_sign_verify(void) {
+ unsigned char sk[32];
+ unsigned char msg[N_SIGS][32];
+ unsigned char sig[N_SIGS][64];
+ size_t i;
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey pk;
+ secp256k1_scalar s;
+
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
+
+ for (i = 0; i < N_SIGS; i++) {
+ secp256k1_testrand256(msg[i]);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], &pk));
+ }
+
+ {
+ /* Flip a few bits in the signature and in the message and check that
+ * verify and verify_batch (TODO) fail */
+ size_t sig_idx = secp256k1_testrand_int(N_SIGS);
+ size_t byte_idx = secp256k1_testrand_int(32);
+ unsigned char xorbyte = secp256k1_testrand_int(254)+1;
+ sig[sig_idx][byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ sig[sig_idx][byte_idx] ^= xorbyte;
+
+ byte_idx = secp256k1_testrand_int(32);
+ sig[sig_idx][32+byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ sig[sig_idx][32+byte_idx] ^= xorbyte;
+
+ byte_idx = secp256k1_testrand_int(32);
+ msg[sig_idx][byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ msg[sig_idx][byte_idx] ^= xorbyte;
+
+ /* Check that above bitflips have been reversed correctly */
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ }
+
+ /* Test overflowing s */
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ memset(&sig[0][32], 0xFF, 32);
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+
+ /* Test negative s */
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
+ secp256k1_scalar_negate(&s, &s);
+ secp256k1_scalar_get_b32(&sig[0][32], &s);
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+}
+#undef N_SIGS
+
+void test_schnorrsig_taproot(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey internal_pk;
+ unsigned char internal_pk_bytes[32];
+ secp256k1_xonly_pubkey output_pk;
+ unsigned char output_pk_bytes[32];
+ unsigned char tweak[32];
+ int pk_parity;
+ unsigned char msg[32];
+ unsigned char sig[64];
+
+ /* Create output key */
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
+ /* In actual taproot the tweak would be hash of internal_pk */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, tweak, &internal_pk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1);
+
+ /* Key spend */
+ secp256k1_testrand256(msg);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+ /* Verify key spend */
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &output_pk) == 1);
+
+ /* Script spend */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1);
+ /* Verify script spend */
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1);
+}
+
+void run_schnorrsig_tests(void) {
+ int i;
+ run_nonce_function_bip340_tests();
+
+ test_schnorrsig_api();
+ test_schnorrsig_sha256_tagged();
+ test_schnorrsig_bip_vectors();
+ for (i = 0; i < count; i++) {
+ test_schnorrsig_sign();
+ test_schnorrsig_sign_verify();
+ }
+ test_schnorrsig_taproot();
+}
+
+#endif
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index 2a74703523..fb3fb187ce 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -8,6 +8,7 @@
#define SECP256K1_SCALAR_H
#include "num.h"
+#include "util.h"
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -15,12 +16,12 @@
#if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low.h"
-#elif defined(USE_SCALAR_4X64)
+#elif defined(SECP256K1_WIDEMUL_INT128)
#include "scalar_4x64.h"
-#elif defined(USE_SCALAR_8X32)
+#elif defined(SECP256K1_WIDEMUL_INT64)
#include "scalar_8x32.h"
#else
-#error "Please select scalar implementation"
+#error "Please select wide multiplication implementation"
#endif
/** Clear a scalar to prevent the leak of sensitive data. */
@@ -101,12 +102,11 @@ static void secp256k1_scalar_order_get_num(secp256k1_num *r);
/** Compare two scalars. */
static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b);
-#ifdef USE_ENDOMORPHISM
-/** Find r1 and r2 such that r1+r2*2^128 = a. */
-static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a);
-/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */
-static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a);
-#endif
+/** Find r1 and r2 such that r1+r2*2^128 = k. */
+static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k);
+/** Find r1 and r2 such that r1+r2*lambda = k,
+ * where r1 and r2 or their negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). */
+static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k);
/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */
static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift);
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 8f539c4bc6..73cbd5e18a 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -192,9 +192,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c1 += th; /* overflow is handled on the next line */ \
- c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
+ c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
}
@@ -207,7 +207,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c1 += th; /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK(c1 >= th); \
}
@@ -221,16 +221,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \
- c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \
- th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
+ th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
}
@@ -238,15 +238,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
#define sumadd(a) { \
unsigned int over; \
c0 += (a); /* overflow is handled on the next line */ \
- over = (c0 < (a)) ? 1 : 0; \
+ over = (c0 < (a)); \
c1 += over; /* overflow is handled on the next line */ \
- c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
+ c2 += (c1 < over); /* never overflows by contract */ \
}
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
#define sumadd_fast(a) { \
c0 += (a); /* overflow is handled on the next line */ \
- c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
VERIFY_CHECK(c2 == 0); \
}
@@ -912,18 +912,16 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
secp256k1_scalar_reduce_512(r, l);
}
-#ifdef USE_ENDOMORPHISM
-static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
- r1->d[0] = a->d[0];
- r1->d[1] = a->d[1];
+static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
+ r1->d[0] = k->d[0];
+ r1->d[1] = k->d[1];
r1->d[2] = 0;
r1->d[3] = 0;
- r2->d[0] = a->d[2];
- r2->d[1] = a->d[3];
+ r2->d[0] = k->d[2];
+ r2->d[1] = k->d[3];
r2->d[2] = 0;
r2->d[3] = 0;
}
-#endif
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0;
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 3c372f34fe..6853f79ecc 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -271,9 +271,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFF */ \
c1 += th; /* overflow is handled on the next line */ \
- c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
+ c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
}
@@ -286,7 +286,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFF */ \
c1 += th; /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK(c1 >= th); \
}
@@ -300,16 +300,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \
- c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \
- th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \
c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
+ th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
}
@@ -317,15 +317,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
#define sumadd(a) { \
unsigned int over; \
c0 += (a); /* overflow is handled on the next line */ \
- over = (c0 < (a)) ? 1 : 0; \
+ over = (c0 < (a)); \
c1 += over; /* overflow is handled on the next line */ \
- c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
+ c2 += (c1 < over); /* never overflows by contract */ \
}
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
#define sumadd_fast(a) { \
c0 += (a); /* overflow is handled on the next line */ \
- c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
VERIFY_CHECK(c2 == 0); \
}
@@ -672,26 +672,24 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
secp256k1_scalar_reduce_512(r, l);
}
-#ifdef USE_ENDOMORPHISM
-static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
- r1->d[0] = a->d[0];
- r1->d[1] = a->d[1];
- r1->d[2] = a->d[2];
- r1->d[3] = a->d[3];
+static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
+ r1->d[0] = k->d[0];
+ r1->d[1] = k->d[1];
+ r1->d[2] = k->d[2];
+ r1->d[3] = k->d[3];
r1->d[4] = 0;
r1->d[5] = 0;
r1->d[6] = 0;
r1->d[7] = 0;
- r2->d[0] = a->d[4];
- r2->d[1] = a->d[5];
- r2->d[2] = a->d[6];
- r2->d[3] = a->d[7];
+ r2->d[0] = k->d[4];
+ r2->d[1] = k->d[5];
+ r2->d[2] = k->d[6];
+ r2->d[3] = k->d[7];
r2->d[4] = 0;
r2->d[5] = 0;
r2->d[6] = 0;
r2->d[7] = 0;
}
-#endif
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0;
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index 70cd73db06..fc75891818 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -7,6 +7,10 @@
#ifndef SECP256K1_SCALAR_IMPL_H
#define SECP256K1_SCALAR_IMPL_H
+#ifdef VERIFY
+#include <string.h>
+#endif
+
#include "scalar.h"
#include "util.h"
@@ -16,12 +20,12 @@
#if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low_impl.h"
-#elif defined(USE_SCALAR_4X64)
+#elif defined(SECP256K1_WIDEMUL_INT128)
#include "scalar_4x64_impl.h"
-#elif defined(USE_SCALAR_8X32)
+#elif defined(SECP256K1_WIDEMUL_INT64)
#include "scalar_8x32_impl.h"
#else
-#error "Please select scalar implementation"
+#error "Please select wide multiplication implementation"
#endif
static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
@@ -252,37 +256,65 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
#endif
}
-#ifdef USE_ENDOMORPHISM
+/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
#if defined(EXHAUSTIVE_TEST_ORDER)
+# if EXHAUSTIVE_TEST_ORDER == 13
+# define EXHAUSTIVE_TEST_LAMBDA 9
+# elif EXHAUSTIVE_TEST_ORDER == 199
+# define EXHAUSTIVE_TEST_LAMBDA 92
+# else
+# error No known lambda for the specified exhaustive test group order.
+# endif
+
/**
- * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
- * full case we don't bother making k1 and k2 be small, we just want them to be
+ * Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the
+ * full case we don't bother making r1 and r2 be small, we just want them to be
* nontrivial to get full test coverage for the exhaustive tests. We therefore
- * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
+ * (arbitrarily) set r2 = k + 5 (mod n) and r1 = k - r2 * lambda (mod n).
*/
-static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
- *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
- *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
+static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
+ *r2 = (*k + 5) % EXHAUSTIVE_TEST_ORDER;
+ *r1 = (*k + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
}
#else
/**
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
- * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
- * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72}
+ * lambda is: */
+static const secp256k1_scalar secp256k1_const_lambda = SECP256K1_SCALAR_CONST(
+ 0x5363AD4CUL, 0xC05C30E0UL, 0xA5261C02UL, 0x8812645AUL,
+ 0x122E22EAUL, 0x20816678UL, 0xDF02967CUL, 0x1B23BD72UL
+);
+
+#ifdef VERIFY
+static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k);
+#endif
+
+/*
+ * Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and
+ * beta^3 == 1 mod p, where n is the curve order and p is the field order.
*
- * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
- * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
- * and k2 have a small size.
- * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are:
+ * Futhermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are
+ * roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p.
+ * (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.)
+ *
+ * Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring
+ * homomorphism phi : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n. The kernel of phi
+ * is a lattice over Z[l] (considering Z[l] as a Z-module). This lattice is generated by a
+ * reduced basis {a1 + b1*l, a2 + b2*l} where
*
* - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
* - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3}
* - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8}
* - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
*
- * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives
+ * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
+ * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
+ * and k2 are small in absolute value.
+ *
+ * The algorithm computes c1 = round(b2 * k / n) and c2 = round((-b1) * k / n), and gives
* k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and
- * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2.
+ * compute r2 = k2 mod n, and r1 = k1 mod n = (k - r2 * lambda) mod n, avoiding the need for
+ * the constants a1 and a2.
*
* g1, g2 are precomputed constants used to replace division with a rounded multiplication
* when decomposing the scalar for an endomorphism-based point multiplication.
@@ -294,21 +326,21 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
* Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez),
* Section 4.3 (here we use a somewhat higher-precision estimate):
* d = a1*b2 - b1*a2
- * g1 = round((2^272)*b2/d)
- * g2 = round((2^272)*b1/d)
+ * g1 = round(2^384 * b2/d)
+ * g2 = round(2^384 * (-b1)/d)
*
- * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found
- * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda').
+ * (Note that d is also equal to the curve order, n, here because [a1,b1] and [a2,b2]
+ * can be found as outputs of the Extended Euclidean Algorithm on inputs n and lambda).
*
- * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order).
+ * The function below splits k into r1 and r2, such that
+ * - r1 + lambda * r2 == k (mod n)
+ * - either r1 < 2^128 or -r1 mod n < 2^128
+ * - either r2 < 2^128 or -r2 mod n < 2^128
+ *
+ * See proof below.
*/
-
-static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
+static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
secp256k1_scalar c1, c2;
- static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST(
- 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL,
- 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL
- );
static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST(
0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL,
0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL
@@ -318,25 +350,167 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL
);
static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST(
- 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL,
- 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL
+ 0x3086D221UL, 0xA7D46BCDUL, 0xE86C90E4UL, 0x9284EB15UL,
+ 0x3DAA8A14UL, 0x71E8CA7FUL, 0xE893209AUL, 0x45DBB031UL
);
static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST(
- 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL,
- 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL
+ 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C4UL,
+ 0x221208ACUL, 0x9DF506C6UL, 0x1571B4AEUL, 0x8AC47F71UL
);
- VERIFY_CHECK(r1 != a);
- VERIFY_CHECK(r2 != a);
+ VERIFY_CHECK(r1 != k);
+ VERIFY_CHECK(r2 != k);
/* these _var calls are constant time since the shift amount is constant */
- secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272);
- secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272);
+ secp256k1_scalar_mul_shift_var(&c1, k, &g1, 384);
+ secp256k1_scalar_mul_shift_var(&c2, k, &g2, 384);
secp256k1_scalar_mul(&c1, &c1, &minus_b1);
secp256k1_scalar_mul(&c2, &c2, &minus_b2);
secp256k1_scalar_add(r2, &c1, &c2);
- secp256k1_scalar_mul(r1, r2, &minus_lambda);
- secp256k1_scalar_add(r1, r1, a);
-}
-#endif
+ secp256k1_scalar_mul(r1, r2, &secp256k1_const_lambda);
+ secp256k1_scalar_negate(r1, r1);
+ secp256k1_scalar_add(r1, r1, k);
+
+#ifdef VERIFY
+ secp256k1_scalar_split_lambda_verify(r1, r2, k);
#endif
+}
+
+#ifdef VERIFY
+/*
+ * Proof for secp256k1_scalar_split_lambda's bounds.
+ *
+ * Let
+ * - epsilon1 = 2^256 * |g1/2^384 - b2/d|
+ * - epsilon2 = 2^256 * |g2/2^384 - (-b1)/d|
+ * - c1 = round(k*g1/2^384)
+ * - c2 = round(k*g2/2^384)
+ *
+ * Lemma 1: |c1 - k*b2/d| < 2^-1 + epsilon1
+ *
+ * |c1 - k*b2/d|
+ * =
+ * |c1 - k*g1/2^384 + k*g1/2^384 - k*b2/d|
+ * <= {triangle inequality}
+ * |c1 - k*g1/2^384| + |k*g1/2^384 - k*b2/d|
+ * =
+ * |c1 - k*g1/2^384| + k*|g1/2^384 - b2/d|
+ * < {rounding in c1 and 0 <= k < 2^256}
+ * 2^-1 + 2^256 * |g1/2^384 - b2/d|
+ * = {definition of epsilon1}
+ * 2^-1 + epsilon1
+ *
+ * Lemma 2: |c2 - k*(-b1)/d| < 2^-1 + epsilon2
+ *
+ * |c2 - k*(-b1)/d|
+ * =
+ * |c2 - k*g2/2^384 + k*g2/2^384 - k*(-b1)/d|
+ * <= {triangle inequality}
+ * |c2 - k*g2/2^384| + |k*g2/2^384 - k*(-b1)/d|
+ * =
+ * |c2 - k*g2/2^384| + k*|g2/2^384 - (-b1)/d|
+ * < {rounding in c2 and 0 <= k < 2^256}
+ * 2^-1 + 2^256 * |g2/2^384 - (-b1)/d|
+ * = {definition of epsilon2}
+ * 2^-1 + epsilon2
+ *
+ * Let
+ * - k1 = k - c1*a1 - c2*a2
+ * - k2 = - c1*b1 - c2*b2
+ *
+ * Lemma 3: |k1| < (a1 + a2 + 1)/2 < 2^128
+ *
+ * |k1|
+ * = {definition of k1}
+ * |k - c1*a1 - c2*a2|
+ * = {(a1*b2 - b1*a2)/n = 1}
+ * |k*(a1*b2 - b1*a2)/n - c1*a1 - c2*a2|
+ * =
+ * |a1*(k*b2/n - c1) + a2*(k*(-b1)/n - c2)|
+ * <= {triangle inequality}
+ * a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2|
+ * < {Lemma 1 and Lemma 2}
+ * a1*(2^-1 + epslion1) + a2*(2^-1 + epsilon2)
+ * < {rounding up to an integer}
+ * (a1 + a2 + 1)/2
+ * < {rounding up to a power of 2}
+ * 2^128
+ *
+ * Lemma 4: |k2| < (-b1 + b2)/2 + 1 < 2^128
+ *
+ * |k2|
+ * = {definition of k2}
+ * |- c1*a1 - c2*a2|
+ * = {(b1*b2 - b1*b2)/n = 0}
+ * |k*(b1*b2 - b1*b2)/n - c1*b1 - c2*b2|
+ * =
+ * |b1*(k*b2/n - c1) + b2*(k*(-b1)/n - c2)|
+ * <= {triangle inequality}
+ * (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2|
+ * < {Lemma 1 and Lemma 2}
+ * (-b1)*(2^-1 + epslion1) + b2*(2^-1 + epsilon2)
+ * < {rounding up to an integer}
+ * (-b1 + b2)/2 + 1
+ * < {rounding up to a power of 2}
+ * 2^128
+ *
+ * Let
+ * - r2 = k2 mod n
+ * - r1 = k - r2*lambda mod n.
+ *
+ * Notice that r1 is defined such that r1 + r2 * lambda == k (mod n).
+ *
+ * Lemma 5: r1 == k1 mod n.
+ *
+ * r1
+ * == {definition of r1 and r2}
+ * k - k2*lambda
+ * == {definition of k2}
+ * k - (- c1*b1 - c2*b2)*lambda
+ * ==
+ * k + c1*b1*lambda + c2*b2*lambda
+ * == {a1 + b1*lambda == 0 mod n and a2 + b2*lambda == 0 mod n}
+ * k - c1*a1 - c2*a2
+ * == {definition of k1}
+ * k1
+ *
+ * From Lemma 3, Lemma 4, Lemma 5 and the definition of r2, we can conclude that
+ *
+ * - either r1 < 2^128 or -r1 mod n < 2^128
+ * - either r2 < 2^128 or -r2 mod n < 2^128.
+ *
+ * Q.E.D.
+ */
+static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k) {
+ secp256k1_scalar s;
+ unsigned char buf1[32];
+ unsigned char buf2[32];
+
+ /* (a1 + a2 + 1)/2 is 0xa2a8918ca85bafe22016d0b917e4dd77 */
+ static const unsigned char k1_bound[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xa2, 0xa8, 0x91, 0x8c, 0xa8, 0x5b, 0xaf, 0xe2, 0x20, 0x16, 0xd0, 0xb9, 0x17, 0xe4, 0xdd, 0x77
+ };
+
+ /* (-b1 + b2)/2 + 1 is 0x8a65287bd47179fb2be08846cea267ed */
+ static const unsigned char k2_bound[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x8a, 0x65, 0x28, 0x7b, 0xd4, 0x71, 0x79, 0xfb, 0x2b, 0xe0, 0x88, 0x46, 0xce, 0xa2, 0x67, 0xed
+ };
+
+ secp256k1_scalar_mul(&s, &secp256k1_const_lambda, r2);
+ secp256k1_scalar_add(&s, &s, r1);
+ VERIFY_CHECK(secp256k1_scalar_eq(&s, k));
+
+ secp256k1_scalar_negate(&s, r1);
+ secp256k1_scalar_get_b32(buf1, r1);
+ secp256k1_scalar_get_b32(buf2, &s);
+ VERIFY_CHECK(secp256k1_memcmp_var(buf1, k1_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k1_bound, 32) < 0);
+
+ secp256k1_scalar_negate(&s, r2);
+ secp256k1_scalar_get_b32(buf1, r2);
+ secp256k1_scalar_get_b32(buf2, &s);
+ VERIFY_CHECK(secp256k1_memcmp_var(buf1, k2_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k2_bound, 32) < 0);
+}
+#endif /* VERIFY */
+#endif /* !defined(EXHAUSTIVE_TEST_ORDER) */
#endif /* SECP256K1_SCALAR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index b79cf1ff6c..a615ec074b 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -48,14 +48,17 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int
}
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
- const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
int i;
+ int over = 0;
*r = 0;
for (i = 0; i < 32; i++) {
- *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
+ *r = (*r * 0x100) + b32[i];
+ if (*r >= EXHAUSTIVE_TEST_ORDER) {
+ over = 1;
+ *r %= EXHAUSTIVE_TEST_ORDER;
+ }
}
- /* just deny overflow, it basically always happens */
- if (overflow) *overflow = 0;
+ if (overflow) *overflow = over;
}
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index 4cee700001..f381e2e322 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -11,7 +11,7 @@
#include "scratch.h"
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) {
- const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ const size_t base_alloc = ROUND_TO_ALIGN(sizeof(secp256k1_scratch));
void *alloc = checked_malloc(error_callback, base_alloc + size);
secp256k1_scratch* ret = (secp256k1_scratch *)alloc;
if (ret != NULL) {
@@ -26,7 +26,7 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err
static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
if (scratch != NULL) {
VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
- if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return;
}
@@ -36,7 +36,7 @@ static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback,
}
static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
- if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return 0;
}
@@ -44,7 +44,7 @@ static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callb
}
static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
- if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return;
}
@@ -56,10 +56,14 @@ static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_c
}
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
- if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return 0;
}
+ /* Ensure that multiplication will not wrap around */
+ if (ALIGNMENT > 1 && objects > SIZE_MAX/(ALIGNMENT - 1)) {
+ return 0;
+ }
if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
return 0;
}
@@ -68,9 +72,16 @@ static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_c
static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
void *ret;
- size = ROUND_TO_ALIGN(size);
+ size_t rounded_size;
+
+ rounded_size = ROUND_TO_ALIGN(size);
+ /* Check that rounding did not wrap around */
+ if (rounded_size < size) {
+ return NULL;
+ }
+ size = rounded_size;
- if (memcmp(scratch->magic, "scratch", 8) != 0) {
+ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return NULL;
}
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index b03a6e6345..dae506d08c 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -7,6 +7,7 @@
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
+#include "assumptions.h"
#include "util.h"
#include "num_impl.h"
#include "field_impl.h"
@@ -19,6 +20,7 @@
#include "eckey_impl.h"
#include "hash_impl.h"
#include "scratch_impl.h"
+#include "selftest.h"
#if defined(VALGRIND)
# include <valgrind/memcheck.h>
@@ -117,6 +119,9 @@ secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigne
size_t prealloc_size;
secp256k1_context* ret;
+ if (!secp256k1_selftest()) {
+ secp256k1_callback_call(&default_error_callback, "self test failed");
+ }
VERIFY_CHECK(prealloc != NULL);
prealloc_size = secp256k1_context_preallocated_size(flags);
ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
@@ -226,7 +231,7 @@ void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scr
* of the software. This is setup for use with valgrind but could be substituted with
* the appropriate instrumentation for other analysis tools.
*/
-static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, void *p, size_t len) {
+static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) {
#if defined(VALGRIND)
if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len);
#else
@@ -279,6 +284,9 @@ int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pu
if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) {
return 0;
}
+ if (!secp256k1_ge_is_in_correct_subgroup(&Q)) {
+ return 0;
+ }
secp256k1_pubkey_save(pubkey, &Q);
secp256k1_ge_clear(&Q);
return 1;
@@ -291,7 +299,7 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(outputlen != NULL);
- ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65));
+ ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33u : 65u));
len = *outputlen;
*outputlen = 0;
ARG_CHECK(output != NULL);
@@ -548,10 +556,21 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char
return ret;
}
-int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) {
+static int secp256k1_ec_pubkey_create_helper(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *seckey_scalar, secp256k1_ge *p, const unsigned char *seckey) {
secp256k1_gej pj;
+ int ret;
+
+ ret = secp256k1_scalar_set_b32_seckey(seckey_scalar, seckey);
+ secp256k1_scalar_cmov(seckey_scalar, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(ecmult_gen_ctx, &pj, seckey_scalar);
+ secp256k1_ge_set_gej(p, &pj);
+ return ret;
+}
+
+int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) {
secp256k1_ge p;
- secp256k1_scalar sec;
+ secp256k1_scalar seckey_scalar;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
@@ -559,15 +578,11 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
- ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !ret);
-
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
- secp256k1_ge_set_gej(&p, &pj);
+ ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey);
secp256k1_pubkey_save(pubkey, &p);
memczero(pubkey, sizeof(*pubkey), !ret);
- secp256k1_scalar_clear(&sec);
+ secp256k1_scalar_clear(&seckey_scalar);
return ret;
}
@@ -605,24 +620,31 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
return ret;
}
-int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+
+static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak) {
secp256k1_scalar term;
+ int overflow = 0;
+ int ret = 0;
+
+ secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term);
+ secp256k1_scalar_clear(&term);
+ return ret;
+}
+
+int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar sec;
int ret = 0;
- int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
-
- ret &= (!overflow) & secp256k1_eckey_privkey_tweak_add(&sec, &term);
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak);
secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
- secp256k1_scalar_clear(&term);
return ret;
}
@@ -630,25 +652,26 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak);
}
+static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak) {
+ secp256k1_scalar term;
+ int overflow = 0;
+ secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term);
+}
+
int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
- secp256k1_scalar term;
int ret = 0;
- int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
ARG_CHECK(tweak != NULL);
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
- ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
+ ret = secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
+ ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak);
if (ret) {
- if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) {
- secp256k1_pubkey_save(pubkey, &p);
- } else {
- ret = 0;
- }
+ secp256k1_pubkey_save(pubkey, &p);
}
return ret;
@@ -741,3 +764,11 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
#ifdef ENABLE_MODULE_RECOVERY
# include "modules/recovery/main_impl.h"
#endif
+
+#ifdef ENABLE_MODULE_EXTRAKEYS
+# include "modules/extrakeys/main_impl.h"
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+# include "modules/schnorrsig/main_impl.h"
+#endif
diff --git a/src/secp256k1/src/selftest.h b/src/secp256k1/src/selftest.h
new file mode 100644
index 0000000000..0e37510c1e
--- /dev/null
+++ b/src/secp256k1/src/selftest.h
@@ -0,0 +1,32 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_SELFTEST_H
+#define SECP256K1_SELFTEST_H
+
+#include "hash.h"
+
+#include <string.h>
+
+static int secp256k1_selftest_sha256(void) {
+ static const char *input63 = "For this sample, this 63-byte string will be used as input data";
+ static const unsigned char output32[32] = {
+ 0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e,
+ 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42,
+ };
+ unsigned char out[32];
+ secp256k1_sha256 hasher;
+ secp256k1_sha256_initialize(&hasher);
+ secp256k1_sha256_write(&hasher, (const unsigned char*)input63, 63);
+ secp256k1_sha256_finalize(&hasher, out);
+ return secp256k1_memcmp_var(out, output32, 32) == 0;
+}
+
+static int secp256k1_selftest(void) {
+ return secp256k1_selftest_sha256();
+}
+
+#endif /* SECP256K1_SELFTEST_H */
diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h
index f1f9be077e..a76003d5b8 100644
--- a/src/secp256k1/src/testrand.h
+++ b/src/secp256k1/src/testrand.h
@@ -14,25 +14,34 @@
/* A non-cryptographic RNG used only for test infrastructure. */
/** Seed the pseudorandom number generator for testing. */
-SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16);
+SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16);
/** Generate a pseudorandom number in the range [0..2**32-1]. */
-static uint32_t secp256k1_rand32(void);
+static uint32_t secp256k1_testrand32(void);
/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or
* more. */
-static uint32_t secp256k1_rand_bits(int bits);
+static uint32_t secp256k1_testrand_bits(int bits);
/** Generate a pseudorandom number in the range [0..range-1]. */
-static uint32_t secp256k1_rand_int(uint32_t range);
+static uint32_t secp256k1_testrand_int(uint32_t range);
/** Generate a pseudorandom 32-byte array. */
-static void secp256k1_rand256(unsigned char *b32);
+static void secp256k1_testrand256(unsigned char *b32);
/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */
-static void secp256k1_rand256_test(unsigned char *b32);
+static void secp256k1_testrand256_test(unsigned char *b32);
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
-static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
+static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len);
+
+/** Flip a single random bit in a byte array */
+static void secp256k1_testrand_flip(unsigned char *b, size_t len);
+
+/** Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL. */
+static void secp256k1_testrand_init(const char* hexseed);
+
+/** Print final test information. */
+static void secp256k1_testrand_finish(void);
#endif /* SECP256K1_TESTRAND_H */
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 30a91e5296..3392566329 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -8,6 +8,7 @@
#define SECP256K1_TESTRAND_IMPL_H
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include "testrand.h"
@@ -19,11 +20,11 @@ static int secp256k1_test_rng_precomputed_used = 8;
static uint64_t secp256k1_test_rng_integer;
static int secp256k1_test_rng_integer_bits_left = 0;
-SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) {
+SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) {
secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16);
}
-SECP256K1_INLINE static uint32_t secp256k1_rand32(void) {
+SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) {
if (secp256k1_test_rng_precomputed_used == 8) {
secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed));
secp256k1_test_rng_precomputed_used = 0;
@@ -31,10 +32,10 @@ SECP256K1_INLINE static uint32_t secp256k1_rand32(void) {
return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++];
}
-static uint32_t secp256k1_rand_bits(int bits) {
+static uint32_t secp256k1_testrand_bits(int bits) {
uint32_t ret;
if (secp256k1_test_rng_integer_bits_left < bits) {
- secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left);
+ secp256k1_test_rng_integer |= (((uint64_t)secp256k1_testrand32()) << secp256k1_test_rng_integer_bits_left);
secp256k1_test_rng_integer_bits_left += 32;
}
ret = secp256k1_test_rng_integer;
@@ -44,7 +45,7 @@ static uint32_t secp256k1_rand_bits(int bits) {
return ret;
}
-static uint32_t secp256k1_rand_int(uint32_t range) {
+static uint32_t secp256k1_testrand_int(uint32_t range) {
/* We want a uniform integer between 0 and range-1, inclusive.
* B is the smallest number such that range <= 2**B.
* two mechanisms implemented here:
@@ -76,25 +77,25 @@ static uint32_t secp256k1_rand_int(uint32_t range) {
mult = 1;
}
while(1) {
- uint32_t x = secp256k1_rand_bits(bits);
+ uint32_t x = secp256k1_testrand_bits(bits);
if (x < trange) {
return (mult == 1) ? x : (x % range);
}
}
}
-static void secp256k1_rand256(unsigned char *b32) {
+static void secp256k1_testrand256(unsigned char *b32) {
secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32);
}
-static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) {
+static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len) {
size_t bits = 0;
memset(bytes, 0, len);
while (bits < len * 8) {
int now;
uint32_t val;
- now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31;
- val = secp256k1_rand_bits(1);
+ now = 1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31;
+ val = secp256k1_testrand_bits(1);
while (now > 0 && bits < len * 8) {
bytes[bits / 8] |= val << (bits % 8);
now--;
@@ -103,8 +104,55 @@ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) {
}
}
-static void secp256k1_rand256_test(unsigned char *b32) {
- secp256k1_rand_bytes_test(b32, 32);
+static void secp256k1_testrand256_test(unsigned char *b32) {
+ secp256k1_testrand_bytes_test(b32, 32);
+}
+
+static void secp256k1_testrand_flip(unsigned char *b, size_t len) {
+ b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_int(8));
+}
+
+static void secp256k1_testrand_init(const char* hexseed) {
+ unsigned char seed16[16] = {0};
+ if (hexseed && strlen(hexseed) != 0) {
+ int pos = 0;
+ while (pos < 16 && hexseed[0] != 0 && hexseed[1] != 0) {
+ unsigned short sh;
+ if ((sscanf(hexseed, "%2hx", &sh)) == 1) {
+ seed16[pos] = sh;
+ } else {
+ break;
+ }
+ hexseed += 2;
+ pos++;
+ }
+ } else {
+ FILE *frand = fopen("/dev/urandom", "r");
+ if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
+ uint64_t t = time(NULL) * (uint64_t)1337;
+ fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
+ seed16[0] ^= t;
+ seed16[1] ^= t >> 8;
+ seed16[2] ^= t >> 16;
+ seed16[3] ^= t >> 24;
+ seed16[4] ^= t >> 32;
+ seed16[5] ^= t >> 40;
+ seed16[6] ^= t >> 48;
+ seed16[7] ^= t >> 56;
+ }
+ if (frand) {
+ fclose(frand);
+ }
+ }
+
+ printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]);
+ secp256k1_testrand_seed(seed16);
+}
+
+static void secp256k1_testrand_finish(void) {
+ unsigned char run32[32];
+ secp256k1_testrand256(run32);
+ printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);
}
#endif /* SECP256K1_TESTRAND_IMPL_H */
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index 374ed7dc12..bb4b5b4c07 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -54,7 +54,7 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) {
void random_field_element_test(secp256k1_fe *fe) {
do {
unsigned char b32[32];
- secp256k1_rand256_test(b32);
+ secp256k1_testrand256_test(b32);
if (secp256k1_fe_set_b32(fe, b32)) {
break;
}
@@ -63,7 +63,7 @@ void random_field_element_test(secp256k1_fe *fe) {
void random_field_element_magnitude(secp256k1_fe *fe) {
secp256k1_fe zero;
- int n = secp256k1_rand_int(9);
+ int n = secp256k1_testrand_int(9);
secp256k1_fe_normalize(fe);
if (n == 0) {
return;
@@ -81,11 +81,12 @@ void random_group_element_test(secp256k1_ge *ge) {
secp256k1_fe fe;
do {
random_field_element_test(&fe);
- if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) {
+ if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_testrand_bits(1))) {
secp256k1_fe_normalize(&ge->y);
break;
}
} while(1);
+ ge->infinity = 0;
}
void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) {
@@ -107,7 +108,7 @@ void random_scalar_order_test(secp256k1_scalar *num) {
do {
unsigned char b32[32];
int overflow = 0;
- secp256k1_rand256_test(b32);
+ secp256k1_testrand256_test(b32);
secp256k1_scalar_set_b32(num, b32, &overflow);
if (overflow || secp256k1_scalar_is_zero(num)) {
continue;
@@ -120,7 +121,7 @@ void random_scalar_order(secp256k1_scalar *num) {
do {
unsigned char b32[32];
int overflow = 0;
- secp256k1_rand256(b32);
+ secp256k1_testrand256(b32);
secp256k1_scalar_set_b32(num, b32, &overflow);
if (overflow || secp256k1_scalar_is_zero(num)) {
continue;
@@ -182,8 +183,10 @@ void run_context_tests(int use_prealloc) {
ecount2 = 10;
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2);
- secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
- CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ /* set error callback (to a function that still aborts in case malloc() fails in secp256k1_context_clone() below) */
+ secp256k1_context_set_error_callback(sign, secp256k1_default_illegal_callback_fn, NULL);
+ CHECK(sign->error_callback.fn != vrfy->error_callback.fn);
+ CHECK(sign->error_callback.fn == secp256k1_default_illegal_callback_fn);
/* check if sizes for cloning are consistent */
CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
@@ -239,7 +242,8 @@ void run_context_tests(int use_prealloc) {
}
/* Verify that the error callback makes it across the clone. */
- CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ CHECK(sign->error_callback.fn != vrfy->error_callback.fn);
+ CHECK(sign->error_callback.fn == secp256k1_default_illegal_callback_fn);
/* And that it resets back to default. */
secp256k1_context_set_error_callback(sign, NULL, NULL);
CHECK(vrfy->error_callback.fn == sign->error_callback.fn);
@@ -361,8 +365,8 @@ void run_scratch_tests(void) {
CHECK(scratch->alloc_size != 0);
CHECK(scratch->alloc_size % ALIGNMENT == 0);
- /* Allocating another 500 bytes fails */
- CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ /* Allocating another 501 bytes fails */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 501) == NULL);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
CHECK(scratch->alloc_size != 0);
@@ -395,6 +399,18 @@ void run_scratch_tests(void) {
secp256k1_scratch_space_destroy(none, scratch);
CHECK(ecount == 5);
+ /* Test that large integers do not wrap around in a bad way */
+ scratch = secp256k1_scratch_space_create(none, 1000);
+ /* Try max allocation with a large number of objects. Only makes sense if
+ * ALIGNMENT is greater than 1 because otherwise the objects take no extra
+ * space. */
+ CHECK(ALIGNMENT <= 1 || !secp256k1_scratch_max_allocation(&none->error_callback, scratch, (SIZE_MAX / (ALIGNMENT - 1)) + 1));
+ /* Try allocating SIZE_MAX to test wrap around which only happens if
+ * ALIGNMENT > 1, otherwise it returns NULL anyway because the scratch
+ * space is too small. */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, SIZE_MAX) == NULL);
+ secp256k1_scratch_space_destroy(none, scratch);
+
/* cleanup */
secp256k1_scratch_space_destroy(none, NULL); /* no-op */
secp256k1_context_destroy(none);
@@ -426,14 +442,14 @@ void run_sha256_tests(void) {
secp256k1_sha256_initialize(&hasher);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
secp256k1_sha256_finalize(&hasher, out);
- CHECK(memcmp(out, outputs[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0);
if (strlen(inputs[i]) > 0) {
- int split = secp256k1_rand_int(strlen(inputs[i]));
+ int split = secp256k1_testrand_int(strlen(inputs[i]));
secp256k1_sha256_initialize(&hasher);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split);
secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split);
secp256k1_sha256_finalize(&hasher, out);
- CHECK(memcmp(out, outputs[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0);
}
}
}
@@ -470,14 +486,14 @@ void run_hmac_sha256_tests(void) {
secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i]));
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i]));
secp256k1_hmac_sha256_finalize(&hasher, out);
- CHECK(memcmp(out, outputs[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0);
if (strlen(inputs[i]) > 0) {
- int split = secp256k1_rand_int(strlen(inputs[i]));
+ int split = secp256k1_testrand_int(strlen(inputs[i]));
secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i]));
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split);
secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split);
secp256k1_hmac_sha256_finalize(&hasher, out);
- CHECK(memcmp(out, outputs[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0);
}
}
}
@@ -504,21 +520,21 @@ void run_rfc6979_hmac_sha256_tests(void) {
secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 64);
for (i = 0; i < 3; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32);
- CHECK(memcmp(out, out1[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, out1[i], 32) == 0);
}
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 65);
for (i = 0; i < 3; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32);
- CHECK(memcmp(out, out1[i], 32) != 0);
+ CHECK(secp256k1_memcmp_var(out, out1[i], 32) != 0);
}
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 64);
for (i = 0; i < 3; i++) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32);
- CHECK(memcmp(out, out2[i], 32) == 0);
+ CHECK(secp256k1_memcmp_var(out, out2[i], 32) == 0);
}
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
}
@@ -542,7 +558,7 @@ void test_rand_bits(int rand32, int bits) {
/* Multiply the output of all rand calls with the odd number m, which
should not change the uniformity of its distribution. */
for (i = 0; i < rounds[usebits]; i++) {
- uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits));
+ uint32_t r = (rand32 ? secp256k1_testrand32() : secp256k1_testrand_bits(bits));
CHECK((((uint64_t)r) >> bits) == 0);
for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) {
uint32_t rm = r * mults[m];
@@ -567,7 +583,7 @@ void test_rand_int(uint32_t range, uint32_t subrange) {
uint64_t x = 0;
CHECK((range % subrange) == 0);
for (i = 0; i < rounds; i++) {
- uint32_t r = secp256k1_rand_int(range);
+ uint32_t r = secp256k1_testrand_int(range);
CHECK(r < range);
r = r % subrange;
x |= (((uint64_t)1) << r);
@@ -599,7 +615,7 @@ void run_rand_int(void) {
#ifndef USE_NUM_NONE
void random_num_negate(secp256k1_num *num) {
- if (secp256k1_rand_bits(1)) {
+ if (secp256k1_testrand_bits(1)) {
secp256k1_num_negate(num);
}
}
@@ -643,11 +659,11 @@ void test_num_add_sub(void) {
secp256k1_num n2;
secp256k1_num n1p2, n2p1, n1m2, n2m1;
random_num_order_test(&n1); /* n1 = R1 */
- if (secp256k1_rand_bits(1)) {
+ if (secp256k1_testrand_bits(1)) {
random_num_negate(&n1);
}
random_num_order_test(&n2); /* n2 = R2 */
- if (secp256k1_rand_bits(1)) {
+ if (secp256k1_testrand_bits(1)) {
random_num_negate(&n2);
}
secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */
@@ -838,7 +854,7 @@ void scalar_test(void) {
while (i < 256) {
secp256k1_scalar t;
int j;
- int now = secp256k1_rand_int(15) + 1;
+ int now = secp256k1_testrand_int(15) + 1;
if (now + i > 256) {
now = 256 - i;
}
@@ -915,7 +931,7 @@ void scalar_test(void) {
secp256k1_num rnum;
secp256k1_num rnum2;
unsigned char cone[1] = {0x01};
- unsigned int shift = 256 + secp256k1_rand_int(257);
+ unsigned int shift = 256 + secp256k1_testrand_int(257);
secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift);
secp256k1_num_mul(&rnum, &s1num, &s2num);
secp256k1_num_shift(&rnum, shift - 1);
@@ -933,7 +949,7 @@ void scalar_test(void) {
random_scalar_order_test(&r);
for (i = 0; i < 100; ++i) {
int low;
- int shift = 1 + secp256k1_rand_int(15);
+ int shift = 1 + secp256k1_testrand_int(15);
int expected = r.d[0] % (1 << shift);
low = secp256k1_scalar_shr_int(&r, shift);
CHECK(expected == low);
@@ -981,7 +997,7 @@ void scalar_test(void) {
secp256k1_scalar b;
int i;
/* Test add_bit. */
- int bit = secp256k1_rand_bits(8);
+ int bit = secp256k1_testrand_bits(8);
secp256k1_scalar_set_int(&b, 1);
CHECK(secp256k1_scalar_is_one(&b));
for (i = 0; i < bit; i++) {
@@ -1142,7 +1158,7 @@ void run_scalar_tests(void) {
secp256k1_scalar_set_b32(&scalar, bin, &overflow);
CHECK(overflow == 0);
secp256k1_scalar_get_b32(bin_tmp, &scalar);
- CHECK(memcmp(bin, bin_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(bin, bin_tmp, 32) == 0);
/* A scalar set to all 1s should overflow. */
memset(bin, 0xFF, 32);
@@ -1752,7 +1768,7 @@ void run_scalar_tests(void) {
void random_fe(secp256k1_fe *x) {
unsigned char bin[32];
do {
- secp256k1_rand256(bin);
+ secp256k1_testrand256(bin);
if (secp256k1_fe_set_b32(x, bin)) {
return;
}
@@ -1762,7 +1778,7 @@ void random_fe(secp256k1_fe *x) {
void random_fe_test(secp256k1_fe *x) {
unsigned char bin[32];
do {
- secp256k1_rand256_test(bin);
+ secp256k1_testrand256_test(bin);
if (secp256k1_fe_set_b32(x, bin)) {
return;
}
@@ -1830,18 +1846,18 @@ void run_field_convert(void) {
CHECK(secp256k1_fe_equal_var(&fe, &fe2));
/* Check conversion from fe. */
secp256k1_fe_get_b32(b322, &fe);
- CHECK(memcmp(b322, b32, 32) == 0);
+ CHECK(secp256k1_memcmp_var(b322, b32, 32) == 0);
secp256k1_fe_to_storage(&fes2, &fe);
- CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0);
+ CHECK(secp256k1_memcmp_var(&fes2, &fes, sizeof(fes)) == 0);
}
-int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) {
+int fe_secp256k1_memcmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
secp256k1_fe t = *b;
#ifdef VERIFY
t.magnitude = a->magnitude;
t.normalized = a->normalized;
#endif
- return memcmp(a, &t, sizeof(secp256k1_fe));
+ return secp256k1_memcmp_var(a, &t, sizeof(secp256k1_fe));
}
void run_field_misc(void) {
@@ -1867,13 +1883,13 @@ void run_field_misc(void) {
CHECK(x.normalized && x.magnitude == 1);
#endif
secp256k1_fe_cmov(&x, &x, 1);
- CHECK(fe_memcmp(&x, &z) != 0);
- CHECK(fe_memcmp(&x, &q) == 0);
+ CHECK(fe_secp256k1_memcmp_var(&x, &z) != 0);
+ CHECK(fe_secp256k1_memcmp_var(&x, &q) == 0);
secp256k1_fe_cmov(&q, &z, 1);
#ifdef VERIFY
CHECK(!q.normalized && q.magnitude == z.magnitude);
#endif
- CHECK(fe_memcmp(&q, &z) == 0);
+ CHECK(fe_secp256k1_memcmp_var(&q, &z) == 0);
secp256k1_fe_normalize_var(&x);
secp256k1_fe_normalize_var(&z);
CHECK(!secp256k1_fe_equal_var(&x, &z));
@@ -1897,9 +1913,9 @@ void run_field_misc(void) {
secp256k1_fe_to_storage(&zs, &z);
secp256k1_fe_storage_cmov(&zs, &xs, 0);
secp256k1_fe_storage_cmov(&zs, &zs, 1);
- CHECK(memcmp(&xs, &zs, sizeof(xs)) != 0);
+ CHECK(secp256k1_memcmp_var(&xs, &zs, sizeof(xs)) != 0);
secp256k1_fe_storage_cmov(&ys, &xs, 1);
- CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0);
+ CHECK(secp256k1_memcmp_var(&xs, &ys, sizeof(xs)) == 0);
secp256k1_fe_from_storage(&x, &xs);
secp256k1_fe_from_storage(&y, &ys);
secp256k1_fe_from_storage(&z, &zs);
@@ -1955,7 +1971,7 @@ void run_field_inv_all_var(void) {
secp256k1_fe_inv_all_var(xi, x, 0);
for (i = 0; i < count; i++) {
size_t j;
- size_t len = secp256k1_rand_int(15) + 1;
+ size_t len = secp256k1_testrand_int(15) + 1;
for (j = 0; j < len; j++) {
random_fe_non_zero(&x[j]);
}
@@ -2086,17 +2102,12 @@ void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
void test_ge(void) {
int i, i1;
-#ifdef USE_ENDOMORPHISM
int runs = 6;
-#else
- int runs = 4;
-#endif
- /* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4).
- * The second in each pair of identical points uses a random Z coordinate in the Jacobian form.
- * All magnitudes are randomized.
- * All 17*17 combinations of points are added to each other, using all applicable methods.
- *
- * When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well.
+ /* 25 points are used:
+ * - infinity
+ * - for each of four random points p1 p2 p3 p4, we add the point, its
+ * negation, and then those two again but with randomized Z coordinate.
+ * - The same is then done for lambda*p1 and lambda^2*p1.
*/
secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs));
secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs));
@@ -2111,14 +2122,12 @@ void test_ge(void) {
int j;
secp256k1_ge g;
random_group_element_test(&g);
-#ifdef USE_ENDOMORPHISM
if (i >= runs - 2) {
secp256k1_ge_mul_lambda(&g, &ge[1]);
}
if (i >= runs - 1) {
secp256k1_ge_mul_lambda(&g, &g);
}
-#endif
ge[1 + 4 * i] = g;
ge[2 + 4 * i] = g;
secp256k1_ge_neg(&ge[3 + 4 * i], &g);
@@ -2215,6 +2224,9 @@ void test_ge(void) {
/* Normal doubling. */
secp256k1_gej_double_var(&resj, &gej[i2], NULL);
ge_equals_gej(&ref, &resj);
+ /* Constant-time doubling. */
+ secp256k1_gej_double(&resj, &gej[i2]);
+ ge_equals_gej(&ref, &resj);
}
/* Test adding opposites. */
@@ -2244,7 +2256,7 @@ void test_ge(void) {
gej_shuffled[i] = gej[i];
}
for (i = 0; i < 4 * runs + 1; i++) {
- int swap = i + secp256k1_rand_int(4 * runs + 1 - i);
+ int swap = i + secp256k1_testrand_int(4 * runs + 1 - i);
if (swap != i) {
secp256k1_gej t = gej_shuffled[i];
gej_shuffled[i] = gej_shuffled[swap];
@@ -2300,6 +2312,39 @@ void test_ge(void) {
free(zinv);
}
+
+void test_intialized_inf(void) {
+ secp256k1_ge p;
+ secp256k1_gej pj, npj, infj1, infj2, infj3;
+ secp256k1_fe zinv;
+
+ /* Test that adding P+(-P) results in a fully initalized infinity*/
+ random_group_element_test(&p);
+ secp256k1_gej_set_ge(&pj, &p);
+ secp256k1_gej_neg(&npj, &pj);
+
+ secp256k1_gej_add_var(&infj1, &pj, &npj, NULL);
+ CHECK(secp256k1_gej_is_infinity(&infj1));
+ CHECK(secp256k1_fe_is_zero(&infj1.x));
+ CHECK(secp256k1_fe_is_zero(&infj1.y));
+ CHECK(secp256k1_fe_is_zero(&infj1.z));
+
+ secp256k1_gej_add_ge_var(&infj2, &npj, &p, NULL);
+ CHECK(secp256k1_gej_is_infinity(&infj2));
+ CHECK(secp256k1_fe_is_zero(&infj2.x));
+ CHECK(secp256k1_fe_is_zero(&infj2.y));
+ CHECK(secp256k1_fe_is_zero(&infj2.z));
+
+ secp256k1_fe_set_int(&zinv, 1);
+ secp256k1_gej_add_zinv_var(&infj3, &npj, &p, &zinv);
+ CHECK(secp256k1_gej_is_infinity(&infj3));
+ CHECK(secp256k1_fe_is_zero(&infj3.x));
+ CHECK(secp256k1_fe_is_zero(&infj3.y));
+ CHECK(secp256k1_fe_is_zero(&infj3.z));
+
+
+}
+
void test_add_neg_y_diff_x(void) {
/* The point of this test is to check that we can add two points
* whose y-coordinates are negatives of each other but whose x
@@ -2373,6 +2418,7 @@ void run_ge(void) {
test_ge();
}
test_add_neg_y_diff_x();
+ test_intialized_inf();
}
void test_ec_combine(void) {
@@ -2396,7 +2442,7 @@ void test_ec_combine(void) {
secp256k1_ge_set_gej(&Q, &Qj);
secp256k1_pubkey_save(&sd, &Q);
CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1);
- CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0);
+ CHECK(secp256k1_memcmp_var(&sd, &sd2, sizeof(sd)) == 0);
}
}
@@ -2562,7 +2608,6 @@ void test_point_times_order(const secp256k1_gej *point) {
secp256k1_ecmult(&ctx->ecmult_ctx, &res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */
secp256k1_gej_add_var(&res1, &res1, &res2, NULL);
CHECK(secp256k1_gej_is_infinity(&res1));
- CHECK(secp256k1_gej_is_valid_var(&res1) == 0);
secp256k1_ge_set_gej(&res3, &res1);
CHECK(secp256k1_ge_is_infinity(&res3));
CHECK(secp256k1_ge_is_valid_var(&res3) == 0);
@@ -2581,6 +2626,87 @@ void test_point_times_order(const secp256k1_gej *point) {
ge_equals_ge(&res3, &secp256k1_ge_const_g);
}
+/* These scalars reach large (in absolute value) outputs when fed to secp256k1_scalar_split_lambda.
+ *
+ * They are computed as:
+ * - For a in [-2, -1, 0, 1, 2]:
+ * - For b in [-3, -1, 1, 3]:
+ * - Output (a*LAMBDA + (ORDER+b)/2) % ORDER
+ */
+static const secp256k1_scalar scalars_near_split_bounds[20] = {
+ SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fc),
+ SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fd),
+ SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fe),
+ SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6ff),
+ SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632d),
+ SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632e),
+ SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632f),
+ SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf76330),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b209f),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a0),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a1),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a2),
+ SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede11),
+ SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede12),
+ SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede13),
+ SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede14),
+ SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a42),
+ SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a43),
+ SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a44),
+ SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a45)
+};
+
+void test_ecmult_target(const secp256k1_scalar* target, int mode) {
+ /* Mode: 0=ecmult_gen, 1=ecmult, 2=ecmult_const */
+ secp256k1_scalar n1, n2;
+ secp256k1_ge p;
+ secp256k1_gej pj, p1j, p2j, ptj;
+ static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Generate random n1,n2 such that n1+n2 = -target. */
+ random_scalar_order_test(&n1);
+ secp256k1_scalar_add(&n2, &n1, target);
+ secp256k1_scalar_negate(&n2, &n2);
+
+ /* Generate a random input point. */
+ if (mode != 0) {
+ random_group_element_test(&p);
+ secp256k1_gej_set_ge(&pj, &p);
+ }
+
+ /* EC multiplications */
+ if (mode == 0) {
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &p1j, &n1);
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &p2j, &n2);
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &ptj, target);
+ } else if (mode == 1) {
+ secp256k1_ecmult(&ctx->ecmult_ctx, &p1j, &pj, &n1, &zero);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &p2j, &pj, &n2, &zero);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &ptj, &pj, target, &zero);
+ } else {
+ secp256k1_ecmult_const(&p1j, &p, &n1, 256);
+ secp256k1_ecmult_const(&p2j, &p, &n2, 256);
+ secp256k1_ecmult_const(&ptj, &p, target, 256);
+ }
+
+ /* Add them all up: n1*P + n2*P + target*P = (n1+n2+target)*P = (n1+n1-n1-n2)*P = 0. */
+ secp256k1_gej_add_var(&ptj, &ptj, &p1j, NULL);
+ secp256k1_gej_add_var(&ptj, &ptj, &p2j, NULL);
+ CHECK(secp256k1_gej_is_infinity(&ptj));
+}
+
+void run_ecmult_near_split_bound(void) {
+ int i;
+ unsigned j;
+ for (i = 0; i < 4*count; ++i) {
+ for (j = 0; j < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++j) {
+ test_ecmult_target(&scalars_near_split_bounds[j], 0);
+ test_ecmult_target(&scalars_near_split_bounds[j], 1);
+ test_ecmult_target(&scalars_near_split_bounds[j], 2);
+ }
+ }
+}
+
void run_point_times_order(void) {
int i;
secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2);
@@ -2594,7 +2720,6 @@ void run_point_times_order(void) {
secp256k1_gej j;
CHECK(secp256k1_ge_is_valid_var(&p));
secp256k1_gej_set_ge(&j, &p);
- CHECK(secp256k1_gej_is_valid_var(&j));
test_point_times_order(&j);
}
secp256k1_fe_sqr(&x, &x);
@@ -2967,14 +3092,16 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
secp256k1_scalar szero;
- secp256k1_scalar sc[32];
- secp256k1_ge pt[32];
+ secp256k1_scalar sc;
+ secp256k1_ge pt;
secp256k1_gej r;
ecmult_multi_data data;
secp256k1_scratch *scratch_empty;
- data.sc = sc;
- data.pt = pt;
+ random_group_element_test(&pt);
+ random_scalar_order(&sc);
+ data.sc = &sc;
+ data.pt = &pt;
secp256k1_scalar_set_int(&szero, 0);
/* Try to multiply 1 point, but scratch space is empty.*/
@@ -2988,12 +3115,10 @@ void test_secp256k1_pippenger_bucket_window_inv(void) {
CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0);
for(i = 1; i <= PIPPENGER_MAX_BUCKET_WINDOW; i++) {
-#ifdef USE_ENDOMORPHISM
/* Bucket_window of 8 is not used with endo */
if (i == 8) {
continue;
}
-#endif
CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)) == i);
if (i != PIPPENGER_MAX_BUCKET_WINDOW) {
CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)+1) > i);
@@ -3006,7 +3131,7 @@ void test_secp256k1_pippenger_bucket_window_inv(void) {
* for a given scratch space.
*/
void test_ecmult_multi_pippenger_max_points(void) {
- size_t scratch_size = secp256k1_rand_int(256);
+ size_t scratch_size = secp256k1_testrand_int(256);
size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12);
secp256k1_scratch *scratch;
size_t n_points_supported;
@@ -3232,16 +3357,14 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
int skew;
int bits = 256;
secp256k1_scalar num = *number;
+ secp256k1_scalar scalar_skew;
secp256k1_scalar_set_int(&x, 0);
secp256k1_scalar_set_int(&shift, 1 << w);
- /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */
-#ifdef USE_ENDOMORPHISM
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
bits = 128;
-#endif
skew = secp256k1_wnaf_const(wnaf, &num, w, bits);
for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
@@ -3262,7 +3385,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar_add(&x, &x, &t);
}
/* Skew num because when encoding numbers as odd we use an offset */
- secp256k1_scalar_cadd_bit(&num, skew == 2, 1);
+ secp256k1_scalar_set_int(&scalar_skew, 1 << (skew == 2));
+ secp256k1_scalar_add(&num, &num, &scalar_skew);
CHECK(secp256k1_scalar_eq(&x, &num));
}
@@ -3275,12 +3399,9 @@ void test_fixed_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar_set_int(&x, 0);
secp256k1_scalar_set_int(&shift, 1 << w);
- /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */
-#ifdef USE_ENDOMORPHISM
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
-#endif
skew = secp256k1_wnaf_fixed(wnaf, &num, w);
for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
@@ -3374,13 +3495,32 @@ void run_wnaf(void) {
int i;
secp256k1_scalar n = {{0}};
+ test_constant_wnaf(&n, 4);
/* Sanity check: 1 and 2 are the smallest odd and even numbers and should
* have easier-to-diagnose failure modes */
n.d[0] = 1;
test_constant_wnaf(&n, 4);
n.d[0] = 2;
test_constant_wnaf(&n, 4);
- /* Test 0 */
+ /* Test -1, because it's a special case in wnaf_const */
+ n = secp256k1_scalar_one;
+ secp256k1_scalar_negate(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ /* Test -2, which may not lead to overflows in wnaf_const */
+ secp256k1_scalar_add(&n, &secp256k1_scalar_one, &secp256k1_scalar_one);
+ secp256k1_scalar_negate(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ /* Test (1/2) - 1 = 1/-2 and 1/2 = (1/-2) + 1
+ as corner cases of negation handling in wnaf_const */
+ secp256k1_scalar_inverse(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ secp256k1_scalar_add(&n, &n, &secp256k1_scalar_one);
+ test_constant_wnaf(&n, 4);
+
+ /* Test 0 for fixed wnaf */
test_fixed_wnaf_small();
/* Random tests */
for (i = 0; i < count; i++) {
@@ -3445,7 +3585,7 @@ void test_ecmult_gen_blind(void) {
secp256k1_ge pge;
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key);
- secp256k1_rand256(seed32);
+ secp256k1_testrand256(seed32);
b = ctx->ecmult_gen_ctx.blind;
i = ctx->ecmult_gen_ctx.initial;
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
@@ -3477,16 +3617,18 @@ void run_ecmult_gen_blind(void) {
}
}
-#ifdef USE_ENDOMORPHISM
/***** ENDOMORPHISH TESTS *****/
-void test_scalar_split(void) {
- secp256k1_scalar full;
- secp256k1_scalar s1, slam;
+void test_scalar_split(const secp256k1_scalar* full) {
+ secp256k1_scalar s, s1, slam;
const unsigned char zero[32] = {0};
unsigned char tmp[32];
- random_scalar_order_test(&full);
- secp256k1_scalar_split_lambda(&s1, &slam, &full);
+ secp256k1_scalar_split_lambda(&s1, &slam, full);
+
+ /* check slam*lambda + s1 == full */
+ secp256k1_scalar_mul(&s, &secp256k1_const_lambda, &slam);
+ secp256k1_scalar_add(&s, &s, &s1);
+ CHECK(secp256k1_scalar_eq(&s, full));
/* check that both are <= 128 bits in size */
if (secp256k1_scalar_is_high(&s1)) {
@@ -3497,15 +3639,32 @@ void test_scalar_split(void) {
}
secp256k1_scalar_get_b32(tmp, &s1);
- CHECK(memcmp(zero, tmp, 16) == 0);
+ CHECK(secp256k1_memcmp_var(zero, tmp, 16) == 0);
secp256k1_scalar_get_b32(tmp, &slam);
- CHECK(memcmp(zero, tmp, 16) == 0);
+ CHECK(secp256k1_memcmp_var(zero, tmp, 16) == 0);
}
+
void run_endomorphism_tests(void) {
- test_scalar_split();
+ unsigned i;
+ static secp256k1_scalar s;
+ test_scalar_split(&secp256k1_scalar_zero);
+ test_scalar_split(&secp256k1_scalar_one);
+ secp256k1_scalar_negate(&s,&secp256k1_scalar_one);
+ test_scalar_split(&s);
+ test_scalar_split(&secp256k1_const_lambda);
+ secp256k1_scalar_add(&s, &secp256k1_const_lambda, &secp256k1_scalar_one);
+ test_scalar_split(&s);
+
+ for (i = 0; i < 100U * count; ++i) {
+ secp256k1_scalar full;
+ random_scalar_order_test(&full);
+ test_scalar_split(&full);
+ }
+ for (i = 0; i < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++i) {
+ test_scalar_split(&scalars_near_split_bounds[i]);
+ }
}
-#endif
void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) {
unsigned char pubkeyc[65];
@@ -3547,7 +3706,7 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
VG_CHECK(pubkeyo, outl);
CHECK(outl == 33);
- CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkeyo[1], &pubkeyc[1], 32) == 0);
CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0]));
if (ypass) {
/* This test isn't always done because we decode with alternative signs, so the y won't match. */
@@ -3563,7 +3722,7 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
VG_CHECK(pubkeyo, outl);
CHECK(outl == 65);
CHECK(pubkeyo[0] == 4);
- CHECK(memcmp(&pubkeyo[1], input, 64) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkeyo[1], input, 64) == 0);
}
CHECK(ecount == 0);
} else {
@@ -3932,7 +4091,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
/* Maximum value is too large, reject. */
memset(ctmp, 255, 32);
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
@@ -3940,7 +4099,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
/* Zero is too small, reject. */
memset(ctmp, 0, 32);
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
@@ -3948,7 +4107,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
/* One must be accepted. */
ctmp[31] = 0x01;
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
@@ -3956,7 +4115,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
pubkey_one = pubkey;
/* Group order + 1 is too large, reject. */
memcpy(ctmp, orderc, 32);
@@ -3966,7 +4125,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
/* -1 must be accepted. */
ctmp[31] = 0x40;
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
@@ -3974,20 +4133,20 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1);
VG_CHECK(&pubkey, sizeof(pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
pubkey_negone = pubkey;
/* Tweak of zero leaves the value unchanged. */
memset(ctmp2, 0, 32);
CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 1);
- CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
+ CHECK(secp256k1_memcmp_var(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40);
memcpy(&pubkey2, &pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
- CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
/* Multiply tweak of zero zeroizes the output. */
CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
- CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0);
- CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
/* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
seckey, the seckey is zeroized. */
@@ -3997,29 +4156,29 @@ void run_eckey_edge_case_test(void) {
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp2) == 1);
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0);
CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, ctmp2) == 0);
- CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, ctmp2) == 0);
- CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0);
/* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing
tweak, the seckey is zeroized. */
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp, orderc) == 0);
- CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
CHECK(secp256k1_ec_seckey_tweak_mul(ctx, ctmp, orderc) == 0);
- CHECK(memcmp(zeros, ctmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0);
memcpy(ctmp, orderc, 32);
ctmp[31] = 0x40;
/* If pubkey_tweak_add or pubkey_tweak_mul are called with an overflowing
tweak, the pubkey is zeroized. */
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0);
- CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0);
- CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
/* If the resulting key in secp256k1_ec_seckey_tweak_add and
* secp256k1_ec_pubkey_tweak_add is 0 the functions fail and in the latter
@@ -4029,25 +4188,25 @@ void run_eckey_edge_case_test(void) {
memset(ctmp2, 0, 32);
ctmp2[31] = 1;
CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 0);
- CHECK(memcmp(zeros, ctmp2, 32) == 0);
+ CHECK(secp256k1_memcmp_var(zeros, ctmp2, 32) == 0);
ctmp2[31] = 1;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
- CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
/* Tweak computation wraps and results in a key of 1. */
ctmp2[31] = 2;
CHECK(secp256k1_ec_seckey_tweak_add(ctx, ctmp2, ctmp) == 1);
- CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1);
+ CHECK(secp256k1_memcmp_var(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1);
ctmp2[31] = 2;
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
ctmp2[31] = 1;
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1);
- CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
/* Tweak mul * 2 = 1+1. */
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1);
ctmp2[31] = 2;
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1);
- CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
/* Test argument errors. */
ecount = 0;
secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
@@ -4056,12 +4215,12 @@ void run_eckey_edge_case_test(void) {
memset(&pubkey, 0, 32);
CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0);
CHECK(ecount == 1);
- CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0);
memcpy(&pubkey, &pubkey2, sizeof(pubkey));
memset(&pubkey2, 0, 32);
CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0);
CHECK(ecount == 2);
- CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey2, zeros, sizeof(pubkey2)) == 0);
/* Plain argument errors. */
ecount = 0;
CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1);
@@ -4101,7 +4260,7 @@ void run_eckey_edge_case_test(void) {
memset(&pubkey, 1, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0);
CHECK(ecount == 2);
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
/* secp256k1_ec_pubkey_combine tests. */
ecount = 0;
pubkeys[0] = &pubkey_one;
@@ -4112,28 +4271,28 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0);
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
CHECK(ecount == 2);
memset(&pubkey, 255, sizeof(secp256k1_pubkey));
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
CHECK(ecount == 3);
pubkeys[0] = &pubkey_negone;
memset(&pubkey, 255, sizeof(secp256k1_pubkey));
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
CHECK(ecount == 3);
len = 33;
CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1);
- CHECK(memcmp(ctmp, ctmp2, 33) == 0);
+ CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0);
/* Result is infinity. */
pubkeys[0] = &pubkey_one;
pubkeys[1] = &pubkey_negone;
@@ -4141,7 +4300,7 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
CHECK(ecount == 3);
/* Passes through infinity but comes out one. */
pubkeys[2] = &pubkey_one;
@@ -4149,19 +4308,19 @@ void run_eckey_edge_case_test(void) {
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
CHECK(ecount == 3);
len = 33;
CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1);
- CHECK(memcmp(ctmp, ctmp2, 33) == 0);
+ CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0);
/* Adds to two. */
pubkeys[1] = &pubkey_one;
memset(&pubkey, 255, sizeof(secp256k1_pubkey));
VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1);
VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
- CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
CHECK(ecount == 3);
secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
}
@@ -4175,21 +4334,21 @@ void run_eckey_negate_test(void) {
/* Verify negation changes the key and changes it back */
CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
- CHECK(memcmp(seckey, seckey_tmp, 32) != 0);
+ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) != 0);
CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
- CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0);
/* Check that privkey alias gives same result */
CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 1);
CHECK(secp256k1_ec_privkey_negate(ctx, seckey_tmp) == 1);
- CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0);
/* Negating all 0s fails */
memset(seckey, 0, 32);
memset(seckey_tmp, 0, 32);
CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
/* Check that seckey is not modified */
- CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0);
/* Negating an overflowing seckey fails and the seckey is zeroed. In this
* test, the seckey has 16 random bytes to ensure that ec_seckey_negate
@@ -4198,7 +4357,7 @@ void run_eckey_negate_test(void) {
memset(seckey, 0xFF, 16);
memset(seckey_tmp, 0, 32);
CHECK(secp256k1_ec_seckey_negate(ctx, seckey) == 0);
- CHECK(memcmp(seckey, seckey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0);
}
void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) {
@@ -4220,7 +4379,7 @@ void test_ecdsa_sign_verify(void) {
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key);
secp256k1_ge_set_gej(&pub, &pubj);
- getrec = secp256k1_rand_bits(1);
+ getrec = secp256k1_testrand_bits(1);
random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL);
if (getrec) {
CHECK(recid >= 0 && recid < 4);
@@ -4287,7 +4446,7 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char
int is_empty_signature(const secp256k1_ecdsa_signature *sig) {
static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0};
- return memcmp(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0;
+ return secp256k1_memcmp_var(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0;
}
void test_ecdsa_end_to_end(void) {
@@ -4320,31 +4479,31 @@ void test_ecdsa_end_to_end(void) {
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
/* Verify exporting and importing public key. */
- CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED));
+ CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_testrand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED));
memset(&pubkey, 0, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1);
/* Verify negation changes the key and changes it back */
memcpy(&pubkey_tmp, &pubkey, sizeof(pubkey));
CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1);
- CHECK(memcmp(&pubkey_tmp, &pubkey, sizeof(pubkey)) != 0);
+ CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) != 0);
CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1);
- CHECK(memcmp(&pubkey_tmp, &pubkey, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) == 0);
/* Verify private key import and export. */
- CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1));
+ CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_testrand_bits(1) == 1));
CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1);
- CHECK(memcmp(privkey, privkey2, 32) == 0);
+ CHECK(secp256k1_memcmp_var(privkey, privkey2, 32) == 0);
/* Optionally tweak the keys using addition. */
- if (secp256k1_rand_int(3) == 0) {
+ if (secp256k1_testrand_int(3) == 0) {
int ret1;
int ret2;
int ret3;
unsigned char rnd[32];
unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
- secp256k1_rand256_test(rnd);
+ secp256k1_testrand256_test(rnd);
memcpy(privkey_tmp, privkey, 32);
ret1 = secp256k1_ec_seckey_tweak_add(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd);
@@ -4355,20 +4514,20 @@ void test_ecdsa_end_to_end(void) {
if (ret1 == 0) {
return;
}
- CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
- CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
/* Optionally tweak the keys using multiplication. */
- if (secp256k1_rand_int(3) == 0) {
+ if (secp256k1_testrand_int(3) == 0) {
int ret1;
int ret2;
int ret3;
unsigned char rnd[32];
unsigned char privkey_tmp[32];
secp256k1_pubkey pubkey2;
- secp256k1_rand256_test(rnd);
+ secp256k1_testrand256_test(rnd);
memcpy(privkey_tmp, privkey, 32);
ret1 = secp256k1_ec_seckey_tweak_mul(ctx, privkey, rnd);
ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd);
@@ -4379,9 +4538,9 @@ void test_ecdsa_end_to_end(void) {
if (ret1 == 0) {
return;
}
- CHECK(memcmp(privkey, privkey_tmp, 32) == 0);
+ CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1);
- CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
+ CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0);
}
/* Sign. */
@@ -4393,13 +4552,13 @@ void test_ecdsa_end_to_end(void) {
extra[31] = 0;
extra[0] = 1;
CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1);
- CHECK(memcmp(&signature[0], &signature[4], sizeof(signature[0])) == 0);
- CHECK(memcmp(&signature[0], &signature[1], sizeof(signature[0])) != 0);
- CHECK(memcmp(&signature[0], &signature[2], sizeof(signature[0])) != 0);
- CHECK(memcmp(&signature[0], &signature[3], sizeof(signature[0])) != 0);
- CHECK(memcmp(&signature[1], &signature[2], sizeof(signature[0])) != 0);
- CHECK(memcmp(&signature[1], &signature[3], sizeof(signature[0])) != 0);
- CHECK(memcmp(&signature[2], &signature[3], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[0], &signature[4], sizeof(signature[0])) == 0);
+ CHECK(secp256k1_memcmp_var(&signature[0], &signature[1], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[0], &signature[2], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[0], &signature[3], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[1], &signature[2], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[1], &signature[3], sizeof(signature[0])) != 0);
+ CHECK(secp256k1_memcmp_var(&signature[2], &signature[3], sizeof(signature[0])) != 0);
/* Verify. */
CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1);
@@ -4420,7 +4579,7 @@ void test_ecdsa_end_to_end(void) {
secp256k1_ecdsa_signature_save(&signature[5], &r, &s);
CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1);
- CHECK(memcmp(&signature[5], &signature[0], 64) == 0);
+ CHECK(secp256k1_memcmp_var(&signature[5], &signature[0], 64) == 0);
/* Serialize/parse DER and verify again */
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1);
@@ -4430,7 +4589,7 @@ void test_ecdsa_end_to_end(void) {
/* Serialize/destroy/parse DER and verify again. */
siglen = 74;
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1);
- sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255);
+ sig[secp256k1_testrand_int(siglen)] += 1 + secp256k1_testrand_int(255);
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 ||
secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0);
}
@@ -4440,23 +4599,23 @@ void test_random_pubkeys(void) {
secp256k1_ge elem2;
unsigned char in[65];
/* Generate some randomly sized pubkeys. */
- size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33;
- if (secp256k1_rand_bits(2) == 0) {
- len = secp256k1_rand_bits(6);
+ size_t len = secp256k1_testrand_bits(2) == 0 ? 65 : 33;
+ if (secp256k1_testrand_bits(2) == 0) {
+ len = secp256k1_testrand_bits(6);
}
if (len == 65) {
- in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7);
+ in[0] = secp256k1_testrand_bits(1) ? 4 : (secp256k1_testrand_bits(1) ? 6 : 7);
} else {
- in[0] = secp256k1_rand_bits(1) ? 2 : 3;
+ in[0] = secp256k1_testrand_bits(1) ? 2 : 3;
}
- if (secp256k1_rand_bits(3) == 0) {
- in[0] = secp256k1_rand_bits(8);
+ if (secp256k1_testrand_bits(3) == 0) {
+ in[0] = secp256k1_testrand_bits(8);
}
if (len > 1) {
- secp256k1_rand256(&in[1]);
+ secp256k1_testrand256(&in[1]);
}
if (len > 33) {
- secp256k1_rand256(&in[33]);
+ secp256k1_testrand256(&in[33]);
}
if (secp256k1_eckey_pubkey_parse(&elem, in, len)) {
unsigned char out[65];
@@ -4467,7 +4626,7 @@ void test_random_pubkeys(void) {
/* If the pubkey can be parsed, it should round-trip... */
CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33));
CHECK(size == len);
- CHECK(memcmp(&in[1], &out[1], len-1) == 0);
+ CHECK(secp256k1_memcmp_var(&in[1], &out[1], len-1) == 0);
/* ... except for the type of hybrid inputs. */
if ((in[0] != 6) && (in[0] != 7)) {
CHECK(in[0] == out[0]);
@@ -4478,7 +4637,7 @@ void test_random_pubkeys(void) {
CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size));
ge_equals_ge(&elem,&elem2);
/* Check that the X9.62 hybrid type is checked. */
- in[0] = secp256k1_rand_bits(1) ? 6 : 7;
+ in[0] = secp256k1_testrand_bits(1) ? 6 : 7;
res = secp256k1_eckey_pubkey_parse(&elem2, in, size);
if (firstb == 2 || firstb == 3) {
if (in[0] == firstb + 4) {
@@ -4490,7 +4649,7 @@ void test_random_pubkeys(void) {
if (res) {
ge_equals_ge(&elem,&elem2);
CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0));
- CHECK(memcmp(&in[1], &out[1], 64) == 0);
+ CHECK(secp256k1_memcmp_var(&in[1], &out[1], 64) == 0);
}
}
}
@@ -4546,21 +4705,21 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen);
if (parsed_der) {
ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0;
- valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0);
+ valid_der = (secp256k1_memcmp_var(compact_der, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der + 32, zeroes, 32) != 0);
}
if (valid_der) {
ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1;
- roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0;
+ roundtrips_der = (len_der == siglen) && secp256k1_memcmp_var(roundtrip_der, sig, siglen) == 0;
}
parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen);
if (parsed_der_lax) {
ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10;
- valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0);
+ valid_der_lax = (secp256k1_memcmp_var(compact_der_lax, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der_lax + 32, zeroes, 32) != 0);
}
if (valid_der_lax) {
ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11;
- roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0;
+ roundtrips_der_lax = (len_der_lax == siglen) && secp256k1_memcmp_var(roundtrip_der_lax, sig, siglen) == 0;
}
if (certainly_der) {
@@ -4576,7 +4735,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
if (valid_der) {
ret |= (!roundtrips_der_lax) << 12;
ret |= (len_der != len_der_lax) << 13;
- ret |= ((len_der != len_der_lax) || (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14;
+ ret |= ((len_der != len_der_lax) || (secp256k1_memcmp_var(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14;
}
ret |= (roundtrips_der != roundtrips_der_lax) << 15;
if (parsed_der) {
@@ -4593,19 +4752,19 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
if (valid_openssl) {
unsigned char tmp[32] = {0};
BN_bn2bin(r, tmp + 32 - BN_num_bytes(r));
- valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
+ valid_openssl = secp256k1_memcmp_var(tmp, max_scalar, 32) < 0;
}
if (valid_openssl) {
unsigned char tmp[32] = {0};
BN_bn2bin(s, tmp + 32 - BN_num_bytes(s));
- valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
+ valid_openssl = secp256k1_memcmp_var(tmp, max_scalar, 32) < 0;
}
}
len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL);
if (len_openssl <= 2048) {
unsigned char *ptr = roundtrip_openssl;
CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl);
- roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0);
+ roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (secp256k1_memcmp_var(roundtrip_openssl, sig, siglen) == 0);
} else {
len_openssl = 0;
}
@@ -4617,7 +4776,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_
ret |= (roundtrips_der != roundtrips_openssl) << 7;
if (roundtrips_openssl) {
ret |= (len_der != (size_t)len_openssl) << 8;
- ret |= ((len_der != (size_t)len_openssl) || (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0)) << 9;
+ ret |= ((len_der != (size_t)len_openssl) || (secp256k1_memcmp_var(roundtrip_der, roundtrip_openssl, len_der) != 0)) << 9;
}
#endif
return ret;
@@ -4637,27 +4796,27 @@ static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) {
static void damage_array(unsigned char *sig, size_t *len) {
int pos;
- int action = secp256k1_rand_bits(3);
+ int action = secp256k1_testrand_bits(3);
if (action < 1 && *len > 3) {
/* Delete a byte. */
- pos = secp256k1_rand_int(*len);
+ pos = secp256k1_testrand_int(*len);
memmove(sig + pos, sig + pos + 1, *len - pos - 1);
(*len)--;
return;
} else if (action < 2 && *len < 2048) {
/* Insert a byte. */
- pos = secp256k1_rand_int(1 + *len);
+ pos = secp256k1_testrand_int(1 + *len);
memmove(sig + pos + 1, sig + pos, *len - pos);
- sig[pos] = secp256k1_rand_bits(8);
+ sig[pos] = secp256k1_testrand_bits(8);
(*len)++;
return;
} else if (action < 4) {
/* Modify a byte. */
- sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255);
+ sig[secp256k1_testrand_int(*len)] += 1 + secp256k1_testrand_int(255);
return;
} else { /* action < 8 */
/* Modify a bit. */
- sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3);
+ sig[secp256k1_testrand_int(*len)] ^= 1 << secp256k1_testrand_bits(3);
return;
}
}
@@ -4670,23 +4829,23 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
int n;
*len = 0;
- der = secp256k1_rand_bits(2) == 0;
+ der = secp256k1_testrand_bits(2) == 0;
*certainly_der = der;
*certainly_not_der = 0;
- indet = der ? 0 : secp256k1_rand_int(10) == 0;
+ indet = der ? 0 : secp256k1_testrand_int(10) == 0;
for (n = 0; n < 2; n++) {
/* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */
- nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0);
+ nlow[n] = der ? 1 : (secp256k1_testrand_bits(3) != 0);
/* The length of the number in bytes (the first byte of which will always be nonzero) */
- nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8;
+ nlen[n] = nlow[n] ? secp256k1_testrand_int(33) : 32 + secp256k1_testrand_int(200) * secp256k1_testrand_int(8) / 8;
CHECK(nlen[n] <= 232);
/* The top bit of the number. */
- nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1));
+ nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_testrand_bits(1));
/* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */
- nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127));
+ nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_testrand_bits(7) : 1 + secp256k1_testrand_int(127));
/* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */
- nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8);
+ nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_testrand_int(3) : secp256k1_testrand_int(300 - nlen[n]) * secp256k1_testrand_int(8) / 8);
if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) {
*certainly_not_der = 1;
}
@@ -4695,7 +4854,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2);
if (!der) {
/* nlenlen[n] max 127 bytes */
- int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
+ int add = secp256k1_testrand_int(127 - nlenlen[n]) * secp256k1_testrand_int(16) * secp256k1_testrand_int(16) / 256;
nlenlen[n] += add;
if (add != 0) {
*certainly_not_der = 1;
@@ -4709,7 +4868,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
CHECK(tlen <= 856);
/* The length of the garbage inside the tuple. */
- elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8;
+ elen = (der || indet) ? 0 : secp256k1_testrand_int(980 - tlen) * secp256k1_testrand_int(8) / 8;
if (elen != 0) {
*certainly_not_der = 1;
}
@@ -4717,7 +4876,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
CHECK(tlen <= 980);
/* The length of the garbage after the end of the tuple. */
- glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8;
+ glen = der ? 0 : secp256k1_testrand_int(990 - tlen) * secp256k1_testrand_int(8) / 8;
if (glen != 0) {
*certainly_not_der = 1;
}
@@ -4732,7 +4891,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
} else {
int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2);
if (!der) {
- int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
+ int add = secp256k1_testrand_int(127 - tlenlen) * secp256k1_testrand_int(16) * secp256k1_testrand_int(16) / 256;
tlenlen += add;
if (add != 0) {
*certainly_not_der = 1;
@@ -4783,13 +4942,13 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
nlen[n]--;
}
/* Generate remaining random bytes of number */
- secp256k1_rand_bytes_test(sig + *len, nlen[n]);
+ secp256k1_testrand_bytes_test(sig + *len, nlen[n]);
*len += nlen[n];
nlen[n] = 0;
}
/* Generate random garbage inside tuple. */
- secp256k1_rand_bytes_test(sig + *len, elen);
+ secp256k1_testrand_bytes_test(sig + *len, elen);
*len += elen;
/* Generate end-of-contents bytes. */
@@ -4801,7 +4960,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
CHECK(tlen + glen <= 1121);
/* Generate random garbage outside tuple. */
- secp256k1_rand_bytes_test(sig + *len, glen);
+ secp256k1_testrand_bytes_test(sig + *len, glen);
*len += glen;
tlen += glen;
CHECK(tlen <= 1121);
@@ -5133,11 +5292,11 @@ void test_ecdsa_edge_cases(void) {
CHECK(!is_empty_signature(&sig));
CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1);
CHECK(!is_empty_signature(&sig2));
- CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0);
+ CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0);
/* The default nonce function is deterministic. */
CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1);
CHECK(!is_empty_signature(&sig2));
- CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0);
+ CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0);
/* The default nonce function changes output with different messages. */
for(i = 0; i < 256; i++) {
int j;
@@ -5184,12 +5343,12 @@ void test_ecdsa_edge_cases(void) {
VG_CHECK(nonce3,32);
CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1);
VG_CHECK(nonce4,32);
- CHECK(memcmp(nonce, nonce2, 32) != 0);
- CHECK(memcmp(nonce, nonce3, 32) != 0);
- CHECK(memcmp(nonce, nonce4, 32) != 0);
- CHECK(memcmp(nonce2, nonce3, 32) != 0);
- CHECK(memcmp(nonce2, nonce4, 32) != 0);
- CHECK(memcmp(nonce3, nonce4, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce, nonce3, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce, nonce4, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce2, nonce3, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce2, nonce4, 32) != 0);
+ CHECK(secp256k1_memcmp_var(nonce3, nonce4, 32) != 0);
}
@@ -5218,7 +5377,7 @@ EC_KEY *get_openssl_key(const unsigned char *key32) {
unsigned char privkey[300];
size_t privkeylen;
const unsigned char* pbegin = privkey;
- int compr = secp256k1_rand_bits(1);
+ int compr = secp256k1_testrand_bits(1);
EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1);
CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr));
CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen));
@@ -5239,7 +5398,7 @@ void test_ecdsa_openssl(void) {
unsigned char message[32];
unsigned char signature[80];
unsigned char key32[32];
- secp256k1_rand256_test(message);
+ secp256k1_testrand256_test(message);
secp256k1_scalar_set_b32(&msg, message, NULL);
random_scalar_order_test(&key);
secp256k1_scalar_get_b32(key32, &key);
@@ -5277,6 +5436,14 @@ void run_ecdsa_openssl(void) {
# include "modules/recovery/tests_impl.h"
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+# include "modules/extrakeys/tests_impl.h"
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+# include "modules/schnorrsig/tests_impl.h"
+#endif
+
void run_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
@@ -5284,12 +5451,12 @@ void run_memczero_test(void) {
/* memczero(..., ..., 0) is a noop. */
memcpy(buf2, buf1, sizeof(buf1));
memczero(buf1, sizeof(buf1), 0);
- CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+ CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
/* memczero(..., ..., 1) zeros the buffer. */
memset(buf2, 0, sizeof(buf2));
memczero(buf1, sizeof(buf1) , 1);
- CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0);
+ CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
}
void int_cmov_test(void) {
@@ -5328,23 +5495,23 @@ void fe_cmov_test(void) {
secp256k1_fe a = zero;
secp256k1_fe_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
r = zero; a = max;
secp256k1_fe_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
a = zero;
secp256k1_fe_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0);
a = one;
secp256k1_fe_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
r = one; a = zero;
secp256k1_fe_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
}
void fe_storage_cmov_test(void) {
@@ -5358,23 +5525,23 @@ void fe_storage_cmov_test(void) {
secp256k1_fe_storage a = zero;
secp256k1_fe_storage_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
r = zero; a = max;
secp256k1_fe_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
a = zero;
secp256k1_fe_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0);
a = one;
secp256k1_fe_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
r = one; a = zero;
secp256k1_fe_storage_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
}
void scalar_cmov_test(void) {
@@ -5388,23 +5555,23 @@ void scalar_cmov_test(void) {
secp256k1_scalar a = zero;
secp256k1_scalar_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
r = zero; a = max;
secp256k1_scalar_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
a = zero;
secp256k1_scalar_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0);
a = one;
secp256k1_scalar_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
r = one; a = zero;
secp256k1_scalar_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
}
void ge_storage_cmov_test(void) {
@@ -5420,23 +5587,23 @@ void ge_storage_cmov_test(void) {
secp256k1_ge_storage a = zero;
secp256k1_ge_storage_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
r = zero; a = max;
secp256k1_ge_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &max, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0);
a = zero;
secp256k1_ge_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &zero, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0);
a = one;
secp256k1_ge_storage_cmov(&r, &a, 1);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
r = one; a = zero;
secp256k1_ge_storage_cmov(&r, &a, 0);
- CHECK(memcmp(&r, &one, sizeof(r)) == 0);
+ CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0);
}
void run_cmov_tests(void) {
@@ -5448,9 +5615,6 @@ void run_cmov_tests(void) {
}
int main(int argc, char **argv) {
- unsigned char seed16[16] = {0};
- unsigned char run32[32] = {0};
-
/* Disable buffering for stdout to improve reliability of getting
* diagnostic information. Happens right at the start of main because
* setbuf must be used before any other operation on the stream. */
@@ -5463,52 +5627,20 @@ int main(int argc, char **argv) {
if (argc > 1) {
count = strtol(argv[1], NULL, 0);
}
+ printf("test count = %i\n", count);
/* find random seed */
- if (argc > 2) {
- int pos = 0;
- const char* ch = argv[2];
- while (pos < 16 && ch[0] != 0 && ch[1] != 0) {
- unsigned short sh;
- if ((sscanf(ch, "%2hx", &sh)) == 1) {
- seed16[pos] = sh;
- } else {
- break;
- }
- ch += 2;
- pos++;
- }
- } else {
- FILE *frand = fopen("/dev/urandom", "r");
- if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
- uint64_t t = time(NULL) * (uint64_t)1337;
- fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
- seed16[0] ^= t;
- seed16[1] ^= t >> 8;
- seed16[2] ^= t >> 16;
- seed16[3] ^= t >> 24;
- seed16[4] ^= t >> 32;
- seed16[5] ^= t >> 40;
- seed16[6] ^= t >> 48;
- seed16[7] ^= t >> 56;
- }
- if (frand) {
- fclose(frand);
- }
- }
- secp256k1_rand_seed(seed16);
-
- printf("test count = %i\n", count);
- printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]);
+ secp256k1_testrand_init(argc > 2 ? argv[2] : NULL);
/* initialize */
run_context_tests(0);
run_context_tests(1);
run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
- if (secp256k1_rand_bits(1)) {
- secp256k1_rand256(run32);
- CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL));
+ if (secp256k1_testrand_bits(1)) {
+ unsigned char rand32[32];
+ secp256k1_testrand256(rand32);
+ CHECK(secp256k1_context_randomize(ctx, secp256k1_testrand_bits(1) ? rand32 : NULL));
}
run_rand_bits();
@@ -5542,6 +5674,7 @@ int main(int argc, char **argv) {
/* ecmult tests */
run_wnaf();
run_point_times_order();
+ run_ecmult_near_split_bound();
run_ecmult_chain();
run_ecmult_constants();
run_ecmult_gen_blind();
@@ -5550,9 +5683,7 @@ int main(int argc, char **argv) {
run_ec_combine();
/* endomorphism tests */
-#ifdef USE_ENDOMORPHISM
run_endomorphism_tests();
-#endif
/* EC point parser test */
run_ec_pubkey_parse_test();
@@ -5583,13 +5714,20 @@ int main(int argc, char **argv) {
run_recovery_tests();
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+ run_extrakeys_tests();
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+ run_schnorrsig_tests();
+#endif
+
/* util tests */
run_memczero_test();
run_cmov_tests();
- secp256k1_rand256(run32);
- printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);
+ secp256k1_testrand_finish();
/* shutdown */
secp256k1_context_destroy(ctx);
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index 8cca1cef21..f4d5b8e176 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -18,18 +18,15 @@
#ifndef EXHAUSTIVE_TEST_ORDER
/* see group_impl.h for allowable values */
#define EXHAUSTIVE_TEST_ORDER 13
-#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */
#endif
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "group.h"
#include "secp256k1.c"
#include "testrand_impl.h"
-#ifdef ENABLE_MODULE_RECOVERY
-#include "src/modules/recovery/main_impl.h"
-#include "include/secp256k1_recovery.h"
-#endif
+static int count = 2;
/** stolen from tests.c */
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
@@ -61,7 +58,7 @@ void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
void random_fe(secp256k1_fe *x) {
unsigned char bin[32];
do {
- secp256k1_rand256(bin);
+ secp256k1_testrand256(bin);
if (secp256k1_fe_set_b32(x, bin)) {
return;
}
@@ -69,6 +66,15 @@ void random_fe(secp256k1_fe *x) {
}
/** END stolen from tests.c */
+static uint32_t num_cores = 1;
+static uint32_t this_core = 0;
+
+SECP256K1_INLINE static int skip_section(uint64_t* iter) {
+ if (num_cores == 1) return 0;
+ *iter += 0xe7037ed1a0b428dbULL;
+ return ((((uint32_t)*iter ^ (*iter >> 32)) * num_cores) >> 32) != this_core;
+}
+
int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, const unsigned char *algo16,
void *data, unsigned int attempt) {
@@ -89,93 +95,93 @@ int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned cha
return 1;
}
-#ifdef USE_ENDOMORPHISM
-void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) {
+void test_exhaustive_endomorphism(const secp256k1_ge *group) {
int i;
- for (i = 0; i < order; i++) {
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_ge res;
secp256k1_ge_mul_lambda(&res, &group[i]);
ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res);
}
}
-#endif
-void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj) {
int i, j;
+ uint64_t iter = 0;
/* Sanity-check (and check infinity functions) */
CHECK(secp256k1_ge_is_infinity(&group[0]));
CHECK(secp256k1_gej_is_infinity(&groupj[0]));
- for (i = 1; i < order; i++) {
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
CHECK(!secp256k1_ge_is_infinity(&group[i]));
CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
}
/* Check all addition formulae */
- for (j = 0; j < order; j++) {
+ for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) {
secp256k1_fe fe_inv;
+ if (skip_section(&iter)) continue;
secp256k1_fe_inv(&fe_inv, &groupj[j].z);
- for (i = 0; i < order; i++) {
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_ge zless_gej;
secp256k1_gej tmp;
/* add_var */
secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
- ge_equals_gej(&group[(i + j) % order], &tmp);
+ ge_equals_gej(&group[(i + j) % EXHAUSTIVE_TEST_ORDER], &tmp);
/* add_ge */
if (j > 0) {
secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
- ge_equals_gej(&group[(i + j) % order], &tmp);
+ ge_equals_gej(&group[(i + j) % EXHAUSTIVE_TEST_ORDER], &tmp);
}
/* add_ge_var */
secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
- ge_equals_gej(&group[(i + j) % order], &tmp);
+ ge_equals_gej(&group[(i + j) % EXHAUSTIVE_TEST_ORDER], &tmp);
/* add_zinv_var */
zless_gej.infinity = groupj[j].infinity;
zless_gej.x = groupj[j].x;
zless_gej.y = groupj[j].y;
secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
- ge_equals_gej(&group[(i + j) % order], &tmp);
+ ge_equals_gej(&group[(i + j) % EXHAUSTIVE_TEST_ORDER], &tmp);
}
}
/* Check doubling */
- for (i = 0; i < order; i++) {
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_gej tmp;
- if (i > 0) {
- secp256k1_gej_double_nonzero(&tmp, &groupj[i]);
- ge_equals_gej(&group[(2 * i) % order], &tmp);
- }
+ secp256k1_gej_double(&tmp, &groupj[i]);
+ ge_equals_gej(&group[(2 * i) % EXHAUSTIVE_TEST_ORDER], &tmp);
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
- ge_equals_gej(&group[(2 * i) % order], &tmp);
+ ge_equals_gej(&group[(2 * i) % EXHAUSTIVE_TEST_ORDER], &tmp);
}
/* Check negation */
- for (i = 1; i < order; i++) {
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_ge tmp;
secp256k1_gej tmpj;
secp256k1_ge_neg(&tmp, &group[i]);
- ge_equals_ge(&group[order - i], &tmp);
+ ge_equals_ge(&group[EXHAUSTIVE_TEST_ORDER - i], &tmp);
secp256k1_gej_neg(&tmpj, &groupj[i]);
- ge_equals_gej(&group[order - i], &tmpj);
+ ge_equals_gej(&group[EXHAUSTIVE_TEST_ORDER - i], &tmpj);
}
}
-void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj) {
int i, j, r_log;
- for (r_log = 1; r_log < order; r_log++) {
- for (j = 0; j < order; j++) {
- for (i = 0; i < order; i++) {
+ uint64_t iter = 0;
+ for (r_log = 1; r_log < EXHAUSTIVE_TEST_ORDER; r_log++) {
+ for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) {
+ if (skip_section(&iter)) continue;
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) {
secp256k1_gej tmp;
secp256k1_scalar na, ng;
secp256k1_scalar_set_int(&na, i);
secp256k1_scalar_set_int(&ng, j);
secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
- ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
+ ge_equals_gej(&group[(i * r_log + j) % EXHAUSTIVE_TEST_ORDER], &tmp);
if (i > 0) {
secp256k1_ecmult_const(&tmp, &group[i], &ng, 256);
- ge_equals_gej(&group[(i * j) % order], &tmp);
+ ge_equals_gej(&group[(i * j) % EXHAUSTIVE_TEST_ORDER], &tmp);
}
}
}
@@ -194,14 +200,16 @@ static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t
return 1;
}
-void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group) {
int i, j, k, x, y;
+ uint64_t iter = 0;
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096);
- for (i = 0; i < order; i++) {
- for (j = 0; j < order; j++) {
- for (k = 0; k < order; k++) {
- for (x = 0; x < order; x++) {
- for (y = 0; y < order; y++) {
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) {
+ for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) {
+ for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
+ for (x = 0; x < EXHAUSTIVE_TEST_ORDER; x++) {
+ if (skip_section(&iter)) continue;
+ for (y = 0; y < EXHAUSTIVE_TEST_ORDER; y++) {
secp256k1_gej tmp;
secp256k1_scalar g_sc;
ecmult_multi_data data;
@@ -213,7 +221,7 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
data.pt[1] = group[y];
secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
- ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
+ ge_equals_gej(&group[(i * x + j * y + k) % EXHAUSTIVE_TEST_ORDER], &tmp);
}
}
}
@@ -222,22 +230,23 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
}
-void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
+void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overflow) {
secp256k1_fe x;
unsigned char x_bin[32];
k %= EXHAUSTIVE_TEST_ORDER;
x = group[k].x;
secp256k1_fe_normalize(&x);
secp256k1_fe_get_b32(x_bin, &x);
- secp256k1_scalar_set_b32(r, x_bin, NULL);
+ secp256k1_scalar_set_b32(r, x_bin, overflow);
}
-void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
int s, r, msg, key;
- for (s = 1; s < order; s++) {
- for (r = 1; r < order; r++) {
- for (msg = 1; msg < order; msg++) {
- for (key = 1; key < order; key++) {
+ uint64_t iter = 0;
+ for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) {
+ for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) {
+ for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) {
+ for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) {
secp256k1_ge nonconst_ge;
secp256k1_ecdsa_signature sig;
secp256k1_pubkey pk;
@@ -246,6 +255,8 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr
int k, should_verify;
unsigned char msg32[32];
+ if (skip_section(&iter)) continue;
+
secp256k1_scalar_set_int(&s_s, s);
secp256k1_scalar_set_int(&r_s, r);
secp256k1_scalar_set_int(&msg_s, msg);
@@ -255,9 +266,9 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr
/* Run through every k value that gives us this r and check that *one* works.
* Note there could be none, there could be multiple, ECDSA is weird. */
should_verify = 0;
- for (k = 0; k < order; k++) {
+ for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
secp256k1_scalar check_x_s;
- r_from_k(&check_x_s, group, k);
+ r_from_k(&check_x_s, group, k, NULL);
if (r_s == check_x_s) {
secp256k1_scalar_set_int(&s_times_k_s, k);
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
@@ -282,13 +293,15 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr
}
}
-void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
+void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
int i, j, k;
+ uint64_t iter = 0;
/* Loop */
- for (i = 1; i < order; i++) { /* message */
- for (j = 1; j < order; j++) { /* key */
- for (k = 1; k < order; k++) { /* nonce */
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */
+ for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */
+ if (skip_section(&iter)) continue;
+ for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
const int starting_k = k;
secp256k1_ecdsa_signature sig;
secp256k1_scalar sk, msg, r, s, expected_r;
@@ -304,10 +317,10 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
/* Note that we compute expected_r *after* signing -- this is important
* because our nonce-computing function function might change k during
* signing. */
- r_from_k(&expected_r, group, k);
+ r_from_k(&expected_r, group, k, NULL);
CHECK(r == expected_r);
- CHECK((k * s) % order == (i + r * j) % order ||
- (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+ CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
+ (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
/* Overflow means we've tried every possible nonce */
if (k < starting_k) {
@@ -328,184 +341,114 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
}
#ifdef ENABLE_MODULE_RECOVERY
-void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
- int i, j, k;
-
- /* Loop */
- for (i = 1; i < order; i++) { /* message */
- for (j = 1; j < order; j++) { /* key */
- for (k = 1; k < order; k++) { /* nonce */
- const int starting_k = k;
- secp256k1_fe r_dot_y_normalized;
- secp256k1_ecdsa_recoverable_signature rsig;
- secp256k1_ecdsa_signature sig;
- secp256k1_scalar sk, msg, r, s, expected_r;
- unsigned char sk32[32], msg32[32];
- int expected_recid;
- int recid;
- secp256k1_scalar_set_int(&msg, i);
- secp256k1_scalar_set_int(&sk, j);
- secp256k1_scalar_get_b32(sk32, &sk);
- secp256k1_scalar_get_b32(msg32, &msg);
-
- secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+#include "src/modules/recovery/tests_exhaustive_impl.h"
+#endif
- /* Check directly */
- secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
- r_from_k(&expected_r, group, k);
- CHECK(r == expected_r);
- CHECK((k * s) % order == (i + r * j) % order ||
- (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
- /* In computing the recid, there is an overflow condition that is disabled in
- * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
- * will exceed the group order, and our signing code always holds out for r
- * values that don't overflow, so with a proper overflow check the tests would
- * loop indefinitely. */
- r_dot_y_normalized = group[k].y;
- secp256k1_fe_normalize(&r_dot_y_normalized);
- /* Also the recovery id is flipped depending if we hit the low-s branch */
- if ((k * s) % order == (i + r * j) % order) {
- expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
- } else {
- expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
- }
- CHECK(recid == expected_recid);
+#ifdef ENABLE_MODULE_EXTRAKEYS
+#include "src/modules/extrakeys/tests_exhaustive_impl.h"
+#endif
- /* Convert to a standard sig then check */
- secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
- secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
- /* Note that we compute expected_r *after* signing -- this is important
- * because our nonce-computing function function might change k during
- * signing. */
- r_from_k(&expected_r, group, k);
- CHECK(r == expected_r);
- CHECK((k * s) % order == (i + r * j) % order ||
- (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
+#ifdef ENABLE_MODULE_SCHNORRSIG
+#include "src/modules/schnorrsig/tests_exhaustive_impl.h"
+#endif
- /* Overflow means we've tried every possible nonce */
- if (k < starting_k) {
- break;
- }
- }
+int main(int argc, char** argv) {
+ int i;
+ secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
+ secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
+ unsigned char rand32[32];
+ secp256k1_context *ctx;
+
+ /* Disable buffering for stdout to improve reliability of getting
+ * diagnostic information. Happens right at the start of main because
+ * setbuf must be used before any other operation on the stream. */
+ setbuf(stdout, NULL);
+ /* Also disable buffering for stderr because it's not guaranteed that it's
+ * unbuffered on all systems. */
+ setbuf(stderr, NULL);
+
+ printf("Exhaustive tests for order %lu\n", (unsigned long)EXHAUSTIVE_TEST_ORDER);
+
+ /* find iteration count */
+ if (argc > 1) {
+ count = strtol(argv[1], NULL, 0);
+ }
+ printf("test count = %i\n", count);
+
+ /* find random seed */
+ secp256k1_testrand_init(argc > 2 ? argv[2] : NULL);
+
+ /* set up split processing */
+ if (argc > 4) {
+ num_cores = strtol(argv[3], NULL, 0);
+ this_core = strtol(argv[4], NULL, 0);
+ if (num_cores < 1 || this_core >= num_cores) {
+ fprintf(stderr, "Usage: %s [count] [seed] [numcores] [thiscore]\n", argv[0]);
+ return 1;
}
+ printf("running tests for core %lu (out of [0..%lu])\n", (unsigned long)this_core, (unsigned long)num_cores - 1);
}
-}
-void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
- /* This is essentially a copy of test_exhaustive_verify, with recovery added */
- int s, r, msg, key;
- for (s = 1; s < order; s++) {
- for (r = 1; r < order; r++) {
- for (msg = 1; msg < order; msg++) {
- for (key = 1; key < order; key++) {
- secp256k1_ge nonconst_ge;
- secp256k1_ecdsa_recoverable_signature rsig;
- secp256k1_ecdsa_signature sig;
- secp256k1_pubkey pk;
- secp256k1_scalar sk_s, msg_s, r_s, s_s;
- secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
- int recid = 0;
- int k, should_verify;
- unsigned char msg32[32];
-
- secp256k1_scalar_set_int(&s_s, s);
- secp256k1_scalar_set_int(&r_s, r);
- secp256k1_scalar_set_int(&msg_s, msg);
- secp256k1_scalar_set_int(&sk_s, key);
- secp256k1_scalar_get_b32(msg32, &msg_s);
+ while (count--) {
+ /* Build context */
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ secp256k1_testrand256(rand32);
+ CHECK(secp256k1_context_randomize(ctx, rand32));
+
+ /* Generate the entire group */
+ secp256k1_gej_set_infinity(&groupj[0]);
+ secp256k1_ge_set_gej(&group[0], &groupj[0]);
+ for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
+ secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
+ secp256k1_ge_set_gej(&group[i], &groupj[i]);
+ if (count != 0) {
+ /* Set a different random z-value for each Jacobian point, except z=1
+ is used in the last iteration. */
+ secp256k1_fe z;
+ random_fe(&z);
+ secp256k1_gej_rescale(&groupj[i], &z);
+ }
- /* Verify by hand */
- /* Run through every k value that gives us this r and check that *one* works.
- * Note there could be none, there could be multiple, ECDSA is weird. */
- should_verify = 0;
- for (k = 0; k < order; k++) {
- secp256k1_scalar check_x_s;
- r_from_k(&check_x_s, group, k);
- if (r_s == check_x_s) {
- secp256k1_scalar_set_int(&s_times_k_s, k);
- secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
- secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
- secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
- should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
- }
- }
- /* nb we have a "high s" rule */
- should_verify &= !secp256k1_scalar_is_high(&s_s);
+ /* Verify against ecmult_gen */
+ {
+ secp256k1_scalar scalar_i;
+ secp256k1_gej generatedj;
+ secp256k1_ge generated;
- /* We would like to try recovering the pubkey and checking that it matches,
- * but pubkey recovery is impossible in the exhaustive tests (the reason
- * being that there are 12 nonzero r values, 12 nonzero points, and no
- * overlap between the sets, so there are no valid signatures). */
+ secp256k1_scalar_set_int(&scalar_i, i);
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
+ secp256k1_ge_set_gej(&generated, &generatedj);
- /* Verify by converting to a standard signature and calling verify */
- secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
- secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
- memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
- secp256k1_pubkey_save(&pk, &nonconst_ge);
- CHECK(should_verify ==
- secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
- }
+ CHECK(group[i].infinity == 0);
+ CHECK(generated.infinity == 0);
+ CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
+ CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
}
}
- }
-}
-#endif
-
-int main(void) {
- int i;
- secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
- secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
- /* Build context */
- secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ /* Run the tests */
+ test_exhaustive_endomorphism(group);
+ test_exhaustive_addition(group, groupj);
+ test_exhaustive_ecmult(ctx, group, groupj);
+ test_exhaustive_ecmult_multi(ctx, group);
+ test_exhaustive_sign(ctx, group);
+ test_exhaustive_verify(ctx, group);
- /* TODO set z = 1, then do num_tests runs with random z values */
+#ifdef ENABLE_MODULE_RECOVERY
+ test_exhaustive_recovery(ctx, group);
+#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+ test_exhaustive_extrakeys(ctx, group);
+#endif
+#ifdef ENABLE_MODULE_SCHNORRSIG
+ test_exhaustive_schnorrsig(ctx);
+#endif
- /* Generate the entire group */
- secp256k1_gej_set_infinity(&groupj[0]);
- secp256k1_ge_set_gej(&group[0], &groupj[0]);
- for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
- /* Set a different random z-value for each Jacobian point */
- secp256k1_fe z;
- random_fe(&z);
-
- secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
- secp256k1_ge_set_gej(&group[i], &groupj[i]);
- secp256k1_gej_rescale(&groupj[i], &z);
-
- /* Verify against ecmult_gen */
- {
- secp256k1_scalar scalar_i;
- secp256k1_gej generatedj;
- secp256k1_ge generated;
-
- secp256k1_scalar_set_int(&scalar_i, i);
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
- secp256k1_ge_set_gej(&generated, &generatedj);
-
- CHECK(group[i].infinity == 0);
- CHECK(generated.infinity == 0);
- CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
- CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
- }
+ secp256k1_context_destroy(ctx);
}
- /* Run the tests */
-#ifdef USE_ENDOMORPHISM
- test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER);
-#endif
- test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
- test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
- test_exhaustive_ecmult_multi(ctx, group, EXHAUSTIVE_TEST_ORDER);
- test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
- test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
+ secp256k1_testrand_finish();
-#ifdef ENABLE_MODULE_RECOVERY
- test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
- test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
-#endif
-
- secp256k1_context_destroy(ctx);
+ printf("no problems found\n");
return 0;
}
-
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 8289e23e0c..3a88a41bc6 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -170,13 +170,35 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
# define I64uFORMAT "llu"
#endif
-#if defined(HAVE___INT128)
-# if defined(__GNUC__)
-# define SECP256K1_GNUC_EXT __extension__
-# else
-# define SECP256K1_GNUC_EXT
+#if defined(__GNUC__)
+# define SECP256K1_GNUC_EXT __extension__
+#else
+# define SECP256K1_GNUC_EXT
+#endif
+
+/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */
+#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN)
+/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */
+# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
+ defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \
+ defined(__i486__) || defined(__i586__) || defined(__i686__) || \
+ defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \
+ defined(__ARMEL__) || defined(__AARCH64EL__) || \
+ (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \
+ (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \
+ defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */
+# define SECP256K1_LITTLE_ENDIAN
+# endif
+# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
+ defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \
+ defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \
+ (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \
+ (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1)
+# define SECP256K1_BIG_ENDIAN
# endif
-SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
+#endif
+#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN)
+# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h.
#endif
/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */
@@ -194,13 +216,36 @@ static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
}
}
+/** Semantics like memcmp. Variable-time.
+ *
+ * We use this to avoid possible compiler bugs with memcmp, e.g.
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
+ */
+static SECP256K1_INLINE int secp256k1_memcmp_var(const void *s1, const void *s2, size_t n) {
+ const unsigned char *p1 = s1, *p2 = s2;
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ int diff = p1[i] - p2[i];
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
+}
+
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/
static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) {
unsigned int mask0, mask1, r_masked, a_masked;
+ /* Access flag with a volatile-qualified lvalue.
+ This prevents clang from figuring out (after inlining) that flag can
+ take only be 0 or 1, which leads to variable time code. */
+ volatile int vflag = flag;
+
/* Casting a negative int to unsigned and back to int is implementation defined behavior */
VERIFY_CHECK(*r >= 0 && *a >= 0);
- mask0 = (unsigned int)flag + ~0u;
+ mask0 = (unsigned int)vflag + ~0u;
mask1 = ~mask0;
r_masked = ((unsigned int)*r & mask0);
a_masked = ((unsigned int)*a & mask1);
@@ -208,4 +253,21 @@ static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag)
*r = (int)(r_masked | a_masked);
}
+/* If USE_FORCE_WIDEMUL_{INT128,INT64} is set, use that wide multiplication implementation.
+ * Otherwise use the presence of __SIZEOF_INT128__ to decide.
+ */
+#if defined(USE_FORCE_WIDEMUL_INT128)
+# define SECP256K1_WIDEMUL_INT128 1
+#elif defined(USE_FORCE_WIDEMUL_INT64)
+# define SECP256K1_WIDEMUL_INT64 1
+#elif defined(__SIZEOF_INT128__)
+# define SECP256K1_WIDEMUL_INT128 1
+#else
+# define SECP256K1_WIDEMUL_INT64 1
+#endif
+#if defined(SECP256K1_WIDEMUL_INT128)
+SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
+SECP256K1_GNUC_EXT typedef __int128 int128_t;
+#endif
+
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
index 60a82d599e..3169e3651c 100644
--- a/src/secp256k1/src/valgrind_ctime_test.c
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -6,16 +6,25 @@
#include <valgrind/memcheck.h>
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
-#if ENABLE_MODULE_ECDH
+#ifdef ENABLE_MODULE_ECDH
# include "include/secp256k1_ecdh.h"
#endif
-#if ENABLE_MODULE_RECOVERY
+#ifdef ENABLE_MODULE_RECOVERY
# include "include/secp256k1_recovery.h"
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+# include "include/secp256k1_extrakeys.h"
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+#include "include/secp256k1_schnorrsig.h"
+#endif
+
int main(void) {
secp256k1_context* ctx;
secp256k1_ecdsa_signature signature;
@@ -28,10 +37,13 @@ int main(void) {
unsigned char key[32];
unsigned char sig[74];
unsigned char spubkey[33];
-#if ENABLE_MODULE_RECOVERY
+#ifdef ENABLE_MODULE_RECOVERY
secp256k1_ecdsa_recoverable_signature recoverable_signature;
int recid;
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+ secp256k1_keypair keypair;
+#endif
if (!RUNNING_ON_VALGRIND) {
fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
@@ -49,7 +61,9 @@ int main(void) {
msg[i] = i + 1;
}
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
+ | SECP256K1_CONTEXT_VERIFY
+ | SECP256K1_CONTEXT_DECLASSIFY);
/* Test keygen. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
@@ -67,7 +81,7 @@ int main(void) {
CHECK(ret);
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
-#if ENABLE_MODULE_ECDH
+#ifdef ENABLE_MODULE_ECDH
/* Test ECDH. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
@@ -75,7 +89,7 @@ int main(void) {
CHECK(ret == 1);
#endif
-#if ENABLE_MODULE_RECOVERY
+#ifdef ENABLE_MODULE_RECOVERY
/* Test signing a recoverable signature. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL);
@@ -114,6 +128,30 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret);
+ /* Test keypair_create and keypair_xonly_tweak_add. */
+#ifdef ENABLE_MODULE_EXTRAKEYS
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_keypair_create(ctx, &keypair, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ /* The tweak is not treated as a secret in keypair_tweak_add */
+ VALGRIND_MAKE_MEM_DEFINED(msg, 32);
+ ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_keypair_create(ctx, &keypair, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+ ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
secp256k1_context_destroy(ctx);
return 0;
}
diff --git a/src/serialize.h b/src/serialize.h
index 7a94e704b2..d9ca984f9c 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -24,7 +24,11 @@
#include <prevector.h>
#include <span.h>
-static const unsigned int MAX_SIZE = 0x02000000;
+/**
+ * The maximum size of a serialized object in bytes or number of elements
+ * (for eg vectors) when the size is encoded as CompactSize.
+ */
+static constexpr uint64_t MAX_SIZE = 0x02000000;
/** Maximum amount of memory (in bytes) to allocate at once when deserializing vectors. */
static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
@@ -304,8 +308,14 @@ void WriteCompactSize(Stream& os, uint64_t nSize)
return;
}
+/**
+ * Decode a CompactSize-encoded variable-length integer.
+ *
+ * As these are primarily used to encode the size of vector-like serializations, by default a range
+ * check is performed. When used as a generic number encoding, range_check should be set to false.
+ */
template<typename Stream>
-uint64_t ReadCompactSize(Stream& is)
+uint64_t ReadCompactSize(Stream& is, bool range_check = true)
{
uint8_t chSize = ser_readdata8(is);
uint64_t nSizeRet = 0;
@@ -331,8 +341,9 @@ uint64_t ReadCompactSize(Stream& is)
if (nSizeRet < 0x100000000ULL)
throw std::ios_base::failure("non-canonical ReadCompactSize()");
}
- if (nSizeRet > (uint64_t)MAX_SIZE)
+ if (range_check && nSizeRet > MAX_SIZE) {
throw std::ios_base::failure("ReadCompactSize(): size too large");
+ }
return nSizeRet;
}
@@ -466,7 +477,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&
#define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
-#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj)
+#define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj)
#define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
/** Serialization wrapper class for integers in VarInt format. */
@@ -529,12 +540,13 @@ struct CustomUintFormatter
template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
/** Formatter for integers in CompactSize format. */
+template<bool RangeCheck>
struct CompactSizeFormatter
{
template<typename Stream, typename I>
void Unser(Stream& s, I& v)
{
- uint64_t n = ReadCompactSize<Stream>(s);
+ uint64_t n = ReadCompactSize<Stream>(s, RangeCheck);
if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) {
throw std::ios_base::failure("CompactSize exceeds limit of type");
}
diff --git a/src/signet.cpp b/src/signet.cpp
new file mode 100644
index 0000000000..e68f031aa4
--- /dev/null
+++ b/src/signet.cpp
@@ -0,0 +1,149 @@
+// 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 <signet.h>
+
+#include <array>
+#include <cstdint>
+#include <vector>
+
+#include <consensus/merkle.h>
+#include <consensus/params.h>
+#include <consensus/validation.h>
+#include <core_io.h>
+#include <hash.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <span.h>
+#include <script/interpreter.h>
+#include <script/standard.h>
+#include <streams.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <uint256.h>
+
+static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
+
+static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
+
+static bool FetchAndClearCommitmentSection(const Span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
+{
+ CScript replacement;
+ bool found_header = false;
+ result.clear();
+
+ opcodetype opcode;
+ CScript::const_iterator pc = witness_commitment.begin();
+ std::vector<uint8_t> pushdata;
+ while (witness_commitment.GetOp(pc, opcode, pushdata)) {
+ if (pushdata.size() > 0) {
+ if (!found_header && pushdata.size() > (size_t) header.size() && Span<const uint8_t>(pushdata.data(), header.size()) == header) {
+ // pushdata only counts if it has the header _and_ some data
+ result.insert(result.end(), pushdata.begin() + header.size(), pushdata.end());
+ pushdata.erase(pushdata.begin() + header.size(), pushdata.end());
+ found_header = true;
+ }
+ replacement << pushdata;
+ } else {
+ replacement << opcode;
+ }
+ }
+
+ if (found_header) witness_commitment = replacement;
+ return found_header;
+}
+
+static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block)
+{
+ std::vector<uint256> leaves;
+ leaves.resize(block.vtx.size());
+ leaves[0] = cb.GetHash();
+ for (size_t s = 1; s < block.vtx.size(); ++s) {
+ leaves[s] = block.vtx[s]->GetHash();
+ }
+ return ComputeMerkleRoot(std::move(leaves));
+}
+
+Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge)
+{
+ CMutableTransaction tx_to_spend;
+ tx_to_spend.nVersion = 0;
+ tx_to_spend.nLockTime = 0;
+ tx_to_spend.vin.emplace_back(COutPoint(), CScript(OP_0), 0);
+ tx_to_spend.vout.emplace_back(0, challenge);
+
+ CMutableTransaction tx_spending;
+ tx_spending.nVersion = 0;
+ tx_spending.nLockTime = 0;
+ tx_spending.vin.emplace_back(COutPoint(), CScript(), 0);
+ tx_spending.vout.emplace_back(0, CScript(OP_RETURN));
+
+ // can't fill any other fields before extracting signet
+ // responses from block coinbase tx
+
+ // find and delete signet signature
+ if (block.vtx.empty()) return nullopt; // no coinbase tx in block; invalid
+ CMutableTransaction modified_cb(*block.vtx.at(0));
+
+ const int cidx = GetWitnessCommitmentIndex(block);
+ if (cidx == NO_WITNESS_COMMITMENT) {
+ return nullopt; // require a witness commitment
+ }
+
+ CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey;
+
+ std::vector<uint8_t> signet_solution;
+ if (!FetchAndClearCommitmentSection(SIGNET_HEADER, witness_commitment, signet_solution)) {
+ // no signet solution -- allow this to support OP_TRUE as trivial block challenge
+ } else {
+ try {
+ VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0);
+ v >> tx_spending.vin[0].scriptSig;
+ v >> tx_spending.vin[0].scriptWitness.stack;
+ if (!v.empty()) return nullopt; // extraneous data encountered
+ } catch (const std::exception&) {
+ return nullopt; // parsing error
+ }
+ }
+ uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block);
+
+ std::vector<uint8_t> block_data;
+ CVectorWriter writer(SER_NETWORK, INIT_PROTO_VERSION, block_data, 0);
+ writer << block.nVersion;
+ writer << block.hashPrevBlock;
+ writer << signet_merkle;
+ writer << block.nTime;
+ tx_to_spend.vin[0].scriptSig << block_data;
+ tx_spending.vin[0].prevout = COutPoint(tx_to_spend.GetHash(), 0);
+
+ return SignetTxs{tx_to_spend, tx_spending};
+}
+
+// Signet block solution checker
+bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams)
+{
+ if (block.GetHash() == consensusParams.hashGenesisBlock) {
+ // genesis block solution is always valid
+ return true;
+ }
+
+ const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end());
+ const Optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge);
+
+ if (!signet_txs) {
+ LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n");
+ return false;
+ }
+
+ 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);
+
+ 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");
+ return false;
+ }
+ return true;
+}
diff --git a/src/signet.h b/src/signet.h
new file mode 100644
index 0000000000..23563a83c4
--- /dev/null
+++ b/src/signet.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2019-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_SIGNET_H
+#define BITCOIN_SIGNET_H
+
+#include <consensus/params.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+
+#include <optional.h>
+
+/**
+ * Extract signature and check whether a block has a valid solution
+ */
+bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams);
+
+/**
+ * Generate the signet tx corresponding to the given block
+ *
+ * The signet tx commits to everything in the block except:
+ * 1. It hashes a modified merkle root with the signet signature removed.
+ * 2. It skips the nonce.
+ */
+class SignetTxs {
+ template<class T1, class T2>
+ SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign} { }
+
+public:
+ static Optional<SignetTxs> Create(const CBlock& block, const CScript& challenge);
+
+ const CTransaction m_to_spend;
+ const CTransaction m_to_sign;
+};
+
+#endif // BITCOIN_SIGNET_H
diff --git a/src/streams.h b/src/streams.h
index 6ce8065da8..c22f5936fd 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -60,6 +60,7 @@ public:
int GetVersion() const { return nVersion; }
int GetType() const { return nType; }
size_t size() const { return stream->size(); }
+ void ignore(size_t size) { return stream->ignore(size); }
};
/* Minimal stream for overwriting and/or appending to an existing byte vector
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index b4f392116c..26de780f29 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -10,7 +10,6 @@
#endif
#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
diff --git a/src/sync.cpp b/src/sync.cpp
index 4be13a3c48..322198a852 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -238,12 +238,15 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
if (!LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
+template void AssertLockNotHeldInternal(const char*, const char*, int, Mutex*);
+template void AssertLockNotHeldInternal(const char*, const char*, int, RecursiveMutex*);
void DeleteLock(void* cs)
{
diff --git a/src/sync.h b/src/sync.h
index 05ff2ee8a9..41f4e43bdd 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -53,8 +53,9 @@ void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
template <typename MutexType>
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
+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 DeleteLock(void* cs);
bool LockStackEmpty();
@@ -69,8 +70,9 @@ inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, v
inline void LeaveCritical() {}
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
template <typename MutexType>
-inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
-inline void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
+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) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
@@ -350,18 +352,4 @@ public:
}
};
-// Utility class for indicating to compiler thread analysis that a mutex is
-// locked (when it couldn't be determined otherwise).
-struct SCOPED_LOCKABLE LockAssertion
-{
- template <typename Mutex>
- explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
- {
-#ifdef DEBUG_LOCKORDER
- AssertLockHeld(mutex);
-#endif
- }
- ~LockAssertion() UNLOCK_FUNCTION() {}
-};
-
#endif // BITCOIN_SYNC_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index bc6b38c682..25fdd64568 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -392,7 +392,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();
+ std::vector<CAddress> vAddr1 = addrman.GetAddr(/* max_addresses */ 0, /* max_pct */0);
BOOST_CHECK_EQUAL(vAddr1.size(), 0U);
CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE);
@@ -415,13 +415,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
BOOST_CHECK(addrman.Add(addr4, source2));
BOOST_CHECK(addrman.Add(addr5, source1));
- // GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down.
- BOOST_CHECK_EQUAL(addrman.GetAddr().size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.GetAddr(/* max_addresses */ 0, /* max_pct */ 0).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);
// 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().size(), 1U);
+ 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);
// Test: Ensure GetAddr still returns 23% when addrman has many addrs.
for (unsigned int i = 1; i < (8 * 256); i++) {
@@ -436,7 +438,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
if (i % 8 == 0)
addrman.Good(addr);
}
- std::vector<CAddress> vAddr = addrman.GetAddr();
+ std::vector<CAddress> vAddr = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23);
size_t percent23 = (addrman.size() * 23) / 100;
BOOST_CHECK_EQUAL(vAddr.size(), percent23);
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index eedab30576..d519eca859 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -13,10 +13,13 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
{
static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"};
+ static const std::string vstrOutNoPadding[] = {"","my","mzxq","mzxw6","mzxw6yq","mzxw6ytb","mzxw6ytboi"};
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{
std::string strEnc = EncodeBase32(vstrIn[i]);
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
+ strEnc = EncodeBase32(vstrIn[i], false);
+ BOOST_CHECK_EQUAL(strEnc, vstrOutNoPadding[i]);
std::string strDec = DecodeBase32(vstrOut[i]);
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
}
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index 57559fa687..6a636f2574 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
std::string base58string = test[1].get_str();
BOOST_CHECK_MESSAGE(
- EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
+ EncodeBase58(sourcedata) == base58string,
strTest);
}
}
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index b3cc8cefd9..0ad5066603 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -12,6 +12,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <random.h>
#include <test/util/setup_common.h>
@@ -750,4 +751,110 @@ BOOST_AUTO_TEST_CASE(sha256d64)
}
}
+static void TestSHA3_256(const std::string& input, const std::string& output)
+{
+ const auto in_bytes = ParseHex(input);
+ const auto out_bytes = ParseHex(output);
+
+ SHA3_256 sha;
+ // Hash the whole thing.
+ unsigned char out[SHA3_256::OUTPUT_SIZE];
+ sha.Write(in_bytes).Finalize(out);
+ assert(out_bytes.size() == sizeof(out));
+ BOOST_CHECK(std::equal(std::begin(out_bytes), std::end(out_bytes), out));
+
+ // Reset and split randomly in 3
+ sha.Reset();
+ int s1 = InsecureRandRange(in_bytes.size() + 1);
+ int s2 = InsecureRandRange(in_bytes.size() + 1 - s1);
+ int s3 = in_bytes.size() - s1 - s2;
+ sha.Write(MakeSpan(in_bytes).first(s1)).Write(MakeSpan(in_bytes).subspan(s1, s2));
+ sha.Write(MakeSpan(in_bytes).last(s3)).Finalize(out);
+ BOOST_CHECK(std::equal(std::begin(out_bytes), std::end(out_bytes), out));
+}
+
+BOOST_AUTO_TEST_CASE(keccak_tests)
+{
+ // Start with the zero state.
+ uint64_t state[25] = {0};
+ CSHA256 tester;
+ for (int i = 0; i < 262144; ++i) {
+ KeccakF(state);
+ for (int j = 0; j < 25; ++j) {
+ unsigned char buf[8];
+ WriteLE64(buf, state[j]);
+ tester.Write(buf, 8);
+ }
+ }
+ uint256 out;
+ tester.Finalize(out.begin());
+ // Expected hash of the concatenated serialized states after 1...262144 iterations of KeccakF.
+ // Verified against an independent implementation.
+ BOOST_CHECK_EQUAL(out.ToString(), "5f4a7f2eca7d57740ef9f1a077b4fc67328092ec62620447fe27ad8ed5f7e34f");
+}
+
+BOOST_AUTO_TEST_CASE(sha3_256_tests)
+{
+ // Test vectors from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/sha3/sha-3bytetestvectors.zip
+
+ // SHA3-256 Short test vectors (SHA3_256ShortMsg.rsp)
+ TestSHA3_256("", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a");
+ TestSHA3_256("e9", "f0d04dd1e6cfc29a4460d521796852f25d9ef8d28b44ee91ff5b759d72c1e6d6");
+ TestSHA3_256("d477", "94279e8f5ccdf6e17f292b59698ab4e614dfe696a46c46da78305fc6a3146ab7");
+ TestSHA3_256("b053fa", "9d0ff086cd0ec06a682c51c094dc73abdc492004292344bd41b82a60498ccfdb");
+ TestSHA3_256("e7372105", "3a42b68ab079f28c4ca3c752296f279006c4fe78b1eb79d989777f051e4046ae");
+ TestSHA3_256("0296f2c40a", "53a018937221081d09ed0497377e32a1fa724025dfdc1871fa503d545df4b40d");
+ TestSHA3_256("e6fd42037f80", "2294f8d3834f24aa9037c431f8c233a66a57b23fa3de10530bbb6911f6e1850f");
+ TestSHA3_256("37b442385e0538", "cfa55031e716bbd7a83f2157513099e229a88891bb899d9ccd317191819998f8");
+ TestSHA3_256("8bca931c8a132d2f", "dbb8be5dec1d715bd117b24566dc3f24f2cc0c799795d0638d9537481ef1e03e");
+ TestSHA3_256("fb8dfa3a132f9813ac", "fd09b3501888445ffc8c3bb95d106440ceee469415fce1474743273094306e2e");
+ TestSHA3_256("71fbacdbf8541779c24a", "cc4e5a216b01f987f24ab9cad5eb196e89d32ed4aac85acb727e18e40ceef00e");
+ TestSHA3_256("7e8f1fd1882e4a7c49e674", "79bef78c78aa71e11a3375394c2562037cd0f82a033b48a6cc932cc43358fd9e");
+ TestSHA3_256("5c56a6b18c39e66e1b7a993a", "b697556cb30d6df448ee38b973cb6942559de4c2567b1556240188c55ec0841c");
+ TestSHA3_256("9c76ca5b6f8d1212d8e6896ad8", "69dfc3a25865f3535f18b4a7bd9c0c69d78455f1fc1f4bf4e29fc82bf32818ec");
+ TestSHA3_256("687ff7485b7eb51fe208f6ff9a1b", "fe7e68ae3e1a91944e4d1d2146d9360e5333c099a256f3711edc372bc6eeb226");
+ TestSHA3_256("4149f41be1d265e668c536b85dde41", "229a7702448c640f55dafed08a52aa0b1139657ba9fc4c5eb8587e174ecd9b92");
+ TestSHA3_256("d83c721ee51b060c5a41438a8221e040", "b87d9e4722edd3918729ded9a6d03af8256998ee088a1ae662ef4bcaff142a96");
+ TestSHA3_256("266e8cbd3e73d80df2a49cfdaf0dc39cd1", "6c2de3c95900a1bcec6bd4ca780056af4acf3aa36ee640474b6e870187f59361");
+ TestSHA3_256("a1d7ce5104eb25d6131bb8f66e1fb13f3523", "ee9062f39720b821b88be5e64621d7e0ca026a9fe7248d78150b14bdbaa40bed");
+ TestSHA3_256("d751ccd2cd65f27db539176920a70057a08a6b", "7aaca80dbeb8dc3677d18b84795985463650d72f2543e0ec709c9e70b8cd7b79");
+ TestSHA3_256("b32dec58865ab74614ea982efb93c08d9acb1bb0", "6a12e535dbfddab6d374058d92338e760b1a211451a6c09be9b61ee22f3bb467");
+ TestSHA3_256("4e0cc4f5c6dcf0e2efca1f9f129372e2dcbca57ea6", "d2b7717864e9438dd02a4f8bb0203b77e2d3cd8f8ffcf9dc684e63de5ef39f0d");
+ TestSHA3_256("d16d978dfbaecf2c8a04090f6eebdb421a5a711137a6", "7f497913318defdc60c924b3704b65ada7ca3ba203f23fb918c6fb03d4b0c0da");
+ TestSHA3_256("47249c7cb85d8f0242ab240efd164b9c8b0bd3104bba3b", "435e276f06ae73aa5d5d6018f58e0f009be351eada47b677c2f7c06455f384e7");
+ TestSHA3_256("cf549a383c0ac31eae870c40867eeb94fa1b6f3cac4473f2", "cdfd1afa793e48fd0ee5b34dfc53fbcee43e9d2ac21515e4746475453ab3831f");
+ TestSHA3_256("9b3fdf8d448680840d6284f2997d3af55ffd85f6f4b33d7f8d", "25005d10e84ff97c74a589013be42fb37f68db64bdfc7626efc0dd628077493a");
+ TestSHA3_256("6b22fe94be2d0b2528d9847e127eb6c7d6967e7ec8b9660e77cc", "157a52b0477639b3bc179667b35c1cdfbb3eef845e4486f0f84a526e940b518c");
+ TestSHA3_256("d8decafdad377904a2789551135e782e302aed8450a42cfb89600c", "3ddecf5bba51643cd77ebde2141c8545f862067b209990d4cb65bfa65f4fa0c0");
+ TestSHA3_256("938fe6afdbf14d1229e03576e532f078898769e20620ae2164f5abfa", "9511abd13c756772b852114578ef9b96f9dc7d0f2b8dcde6ea7d1bd14c518890");
+ TestSHA3_256("66eb5e7396f5b451a02f39699da4dbc50538fb10678ec39a5e28baa3c0", "540acf81810a199996a612e885781308802fe460e9c638cc022e17076be8597a");
+ TestSHA3_256("de98968c8bd9408bd562ac6efbca2b10f5769aacaa01365763e1b2ce8048", "6b2f2547781449d4fa158180a178ef68d7056121bf8a2f2f49891afc24978521");
+ TestSHA3_256("94464e8fafd82f630e6aab9aa339d981db0a372dc5c1efb177305995ae2dc0", "ea7952ad759653cd47a18004ac2dbb9cf4a1e7bba8a530cf070570c711a634ea");
+ TestSHA3_256("c178ce0f720a6d73c6cf1caa905ee724d5ba941c2e2628136e3aad7d853733ba", "64537b87892835ff0963ef9ad5145ab4cfce5d303a0cb0415b3b03f9d16e7d6b");
+ TestSHA3_256("14365d3301150d7c5ba6bb8c1fc26e9dab218fc5d01c9ed528b72482aadee9c27bef667907797d55514468f68791f053daa2df598d7db7d54beea493bdcbb0c75c7b36ad84b9996dca96354190bd96d9d7fbe8ff54ffaf77c55eb92985da50825ee3b4179f5ec88b6fa60bb361d0caf9493494fe4d28ef843f0f498a2a9331b82a", "9b690531dee948a9c559a2e0efab2ec824151a9175f2730a030b748d07cbaa7f");
+ TestSHA3_256("4a757db93f6d4c6529211d70d5f8491799c0f73ae7f24bbd2138db2eaf2c63a85063b9f7adaa03fc348f275323248334e3ffdf9798859f9cf6693d29566ff7d50976c505ecb58e543c459b39acdf4ce4b5e80a682eaa7c1f1ce5fe4acb864ff91eb6892b23165735ea49626898b40ceeb78161f5d0ea4a103cb404d937f9d1dc362b", "1ac7cc7e2e8ea14fb1b90096f41265100712c5dd41519d78b2786cfb6355af72");
+ TestSHA3_256("da11c39c77250f6264dda4b096341ff9c4cc2c900633b20ea1664bf32193f790a923112488f882450cf334819bbaca46ffb88eff0265aa803bc79ca42739e4347c6bff0bb9aa99780261ffe42be0d3b5135d03723338fb2776841a0b4bc26360f9ef769b34c2bec5ed2feb216e2fa30fa5c37430c0360ecbfba3af6fb6b8dedacbb95c", "c163cd43de224ac5c262ae39db746cfcad66074ebaec4a6da23d86b310520f21");
+ TestSHA3_256("3341ca020d4835838b0d6c8f93aaaebb7af60730d208c85283f6369f1ee27fd96d38f2674f316ef9c29c1b6b42dd59ec5236f65f5845a401adceaa4cf5bbd91cac61c21102052634e99faedd6cdddcd4426b42b6a372f29a5a5f35f51ce580bb1845a3c7cfcd447d269e8caeb9b320bb731f53fe5c969a65b12f40603a685afed86bfe53", "6c3e93f2b49f493344cc3eb1e9454f79363032beee2f7ea65b3d994b5cae438f");
+ TestSHA3_256("989fc49594afc73405bacee4dbbe7135804f800368de39e2ea3bbec04e59c6c52752927ee3aa233ba0d8aab5410240f4c109d770c8c570777c928fce9a0bec9bc5156c821e204f0f14a9ab547e0319d3e758ae9e28eb2dbc3d9f7acf51bd52f41bf23aeb6d97b5780a35ba08b94965989744edd3b1d6d67ad26c68099af85f98d0f0e4fff9", "b10adeb6a9395a48788931d45a7b4e4f69300a76d8b716c40c614c3113a0f051");
+ TestSHA3_256("e5022f4c7dfe2dbd207105e2f27aaedd5a765c27c0bc60de958b49609440501848ccf398cf66dfe8dd7d131e04f1432f32827a057b8904d218e68ba3b0398038d755bd13d5f168cfa8a11ab34c0540873940c2a62eace3552dcd6953c683fdb29983d4e417078f1988c560c9521e6f8c78997c32618fc510db282a985f868f2d973f82351d11", "3293a4b9aeb8a65e1014d3847500ffc8241594e9c4564cbd7ce978bfa50767fe");
+ TestSHA3_256("b1f6076509938432145bb15dbe1a7b2e007934be5f753908b50fd24333455970a7429f2ffbd28bd6fe1804c4688311f318fe3fcd9f6744410243e115bcb00d7e039a4fee4c326c2d119c42abd2e8f4155a44472643704cc0bc72403b8a8ab0fd4d68e04a059d6e5ed45033b906326abb4eb4147052779bad6a03b55ca5bd8b140e131bed2dfada", "f82d9602b231d332d902cb6436b15aef89acc591cb8626233ced20c0a6e80d7a");
+ TestSHA3_256("56ea14d7fcb0db748ff649aaa5d0afdc2357528a9aad6076d73b2805b53d89e73681abfad26bee6c0f3d20215295f354f538ae80990d2281be6de0f6919aa9eb048c26b524f4d91ca87b54c0c54aa9b54ad02171e8bf31e8d158a9f586e92ffce994ecce9a5185cc80364d50a6f7b94849a914242fcb73f33a86ecc83c3403630d20650ddb8cd9c4", "4beae3515ba35ec8cbd1d94567e22b0d7809c466abfbafe9610349597ba15b45");
+
+ // SHA3-256 Long test vectors (SHA3_256LongMsg.rsp)
+ TestSHA3_256("b1caa396771a09a1db9bc20543e988e359d47c2a616417bbca1b62cb02796a888fc6eeff5c0b5c3d5062fcb4256f6ae1782f492c1cf03610b4a1fb7b814c057878e1190b9835425c7a4a0e182ad1f91535ed2a35033a5d8c670e21c575ff43c194a58a82d4a1a44881dd61f9f8161fc6b998860cbe4975780be93b6f87980bad0a99aa2cb7556b478ca35d1f3746c33e2bb7c47af426641cc7bbb3425e2144820345e1d0ea5b7da2c3236a52906acdc3b4d34e474dd714c0c40bf006a3a1d889a632983814bbc4a14fe5f159aa89249e7c738b3b73666bac2a615a83fd21ae0a1ce7352ade7b278b587158fd2fabb217aa1fe31d0bda53272045598015a8ae4d8cec226fefa58daa05500906c4d85e7567", "cb5648a1d61c6c5bdacd96f81c9591debc3950dcf658145b8d996570ba881a05");
+ TestSHA3_256("712b03d9ebe78d3a032a612939c518a6166ca9a161183a7596aa35b294d19d1f962da3ff64b57494cb5656e24adcf3b50e16f4e52135d2d9de76e94aa801cf49db10e384035329c54c9455bb3a9725fd9a44f44cb9078d18d3783d46ce372c31281aecef2f8b53d5702b863d71bc5786a33dd15d9256103b5ff7572f703d5cde6695e6c84f239acd1d6512ef581330590f4ab2a114ea064a693d5f8df5d908587bc7f998cde4a8b43d8821595566597dc8b3bf9ea78b154bd8907ee6c5d4d8a851f94be510962292b7ddda04d17b79fab4c022deb400e5489639dbc448f573d5cf72073a8001b36f73ac6677351b39d9bdb900e9a1121f488a7fa0aee60682e7dc7c531c85ec0154593ded3ae70e4121cae58445d8896b549cacf22d07cdace7625d57158721b44851d796d6511c38dac28dd37cbf2d7073b407fbc813149adc485e3dacee66755443c389d2d90dc70d8ff91816c0c5d7adbad7e30772a1f3ce76c72a6a2284ec7f174aefb6e9a895c118717999421b470a9665d2728c3c60c6d3e048d58b43c0d1b5b2f00be8b64bfe453d1e8fadf5699331f9", "095dcd0bc55206d2e1e715fb7173fc16a81979f278495dfc69a6d8f3174eba5a");
+ TestSHA3_256("2a459282195123ebc6cf5782ab611a11b9487706f7795e236df3a476404f4b8c1e9904e2dc5ef29c5e06b179b8649707928c3913d1e53164747f1fa9bba6eeaf8fb759d71e32adc8c611d061345882f1cdeee3ab4cab3554adb2e43f4b01c37b4546994b25f4dcd6c497bc206865643930157cb5b2f4f25be235fa223688535907efcc253bcd083021407ea09cb1c34684aa0c1849e7efe2d9af6938c46525af9e5afb4da6e5b83da4b61dc718672a8090549cbe5aadb44f5bc93a6b3fbdc2e6d32e2eaaae637465179ea17f23ad1e4f1ebc328e2c6dc90c302b74a1edbbb0676c136b269d70c41040a313af06ab291bf489d9700950b77f207c1fc41884799931b3bca8b93331a6e96b7a3f0a8bd24cdb64964c377e0512f36444bb0643a4e3ecb328194cd5428fd89ede167472a14a9bf5730aff1e3b2c708de96eff1ebaaf63beb75f9c7d8034d6e5471e8f8a1f7efce37793a958e134619c19c54d3d42645f7a7263f25471fbaae8be3ea2fbd34ec6d7aacd7d5680948c3cd9a837c9c469a88f600d95829f4d1e4e4a5ef4ed4623c07815a1c33d9fb3b91333ff04eac92806a68a46cf2e9293f8bff466ce87fe66b46fbff7c238c7f9b2c92eb2fdc7d8084167f6f4e680d03301e5c33f78f1857d6863b1b8c36c7fce3e07d2a96a8979712079ae0023a1e3970165bfcf3a5463d2a4fdf1ca0e044f9a247528cd935734cb6d85ba53ceb95325c0eaf0ff5cd81ecb32e58917eb26bfc52dba3704bf5a927fee3220", "cb1c691c87244c0caf733aacd427f83412cd48820b358c1b15dd9fadee54e5af");
+ TestSHA3_256("32659902674c94473a283be00835eb86339d394a189a87da41dad500db27da6b6a4753b2bb219c961a227d88c6df466ba2fc1e9a2d4c982db4398778c76714d5e9940da48bc3808f3c9989131a07683b8c29d6af336e9aee1dfa57d83c48a86f17146edec07869bb06550689ebf4788159ed0a921048b4a6e3e3ec272413bec15d8e1f6a40897fa0e11d9df223ef9fc270106249ae220fdc6ebdef6d6611805421ccc850f53ee9c836baf657a94005883b5a85def344d218264f07b2ea8714afcc941096c6ded0bb6bf5b8bf652fd15a21931c58c9f526e27363ddff98c0a25bc7af9f469ab35bffea948b333f042cc18a82cec0177f33c3bdbf185b580353de79e51e675b03b31e195f19ba1f063d44def0441dc52820426c2c61cf12974ec249fd3502f017ffa06220075ced7e2d6b86a52677ba3916e8e8726062aec5bc8ea1c18b1e4137680b2c9d002191b423bee8691bd7e0f93c3b9959bc1c14d5c5cbe8f7c9c336aa16e9de9faa12f3f048c66d04cb441eb2bbc5e8a91e052c0f9000856896f9b7ba30c1e2eead36fc7ac30a7d3ddfc65caaba0e3b292d26dfba46b5e2dc9bc9acadde1c9f52b2969299bd1281ddff65822b629cfba2928613200e73661b803afdcc4a817d9361389e975e67dfadd22a797bdaf991ddf42db18711c079ecec55925f9978e478612609bacd900172011c27e24bad639ffc24a23877278318872153aef6893ccb5b68b94b33154df7334375aadd3edbb35272cc7b672dec68faa62900873ded52f6049891b77f2d0311a84b19b73660e09d1f1998095c1da1edecfa9f741b5fd6db048dd68255085d43529279021d59ed853470d6863b7c8e07fcb0d1e6acfb1eb16f7f60bb1f46ce70493010e57930a3b4b8b87e065272f6f1dd31df057627f4214e58798b664e1e40960f2789d44ccacfb3dbd8b02a68a053976711f8034c1ed3a8", "5ac9275e02543410359a3f364b2ae3b85763321fd6d374d13fe54314e5561b01");
+ TestSHA3_256("a65da8277a3b3738432bca9822d43b3d810cdad3b0ed2468d02bd269f1a416cd77392190c2dde8630eeb28a297bda786017abe9cf82f14751422ac9fff6322d5d9a33173db49792d3bc37fff501af667f7ca3dd335d028551e04039ef5a9d42a9443e1b80ea872fd945ad8999514ae4a29a35f60b0f7e971b67ae04d1ba1b53470c03847a3225c3ddf593a57aed3599661ae2d2bb1cddd2fa62c4a94b8704c5c35c33e08e2debe54e567ae21e27e7eb36593ae1c807a8ef8b5c1495b15412108aaf3fce4130520aa6e2d3bdf7b3ea609fdf9ea1c64258435aae2e58a7b3abda198f979c17dbe0aa74253e979bf3a5800f388ea11a7f7454c4e36270a3083a790c77cbe89693205b32880c0d8f79b1c000ee9b5e58f175ba7696616c17c45673cff25d1221f899836e95cc9e26a887a7115c4537e65ad4eacc319ba98a9a8860c089cbc76e7ea4c984d900b80622afbbbd1c0cdc670e3a4c523f81c77fed38b6aa988876b097da8411cc48e9b25a826460a862aa3fadfe75952aa4347c2effebdac9138ebcc6c34991e9f5b19fc2b847a87be72ff49c99ecf19d837ee3e23686cd760d9dd7adc78091bca79e42fdb9bc0120faec1a6ca52913e2a0156ba9850e1f39d712859f7fdf7daedf0e206dff67e7121e5d1590a8a068947a8657d753e83c7f009b6b2e54acc24afc9fdc9601a1d6d9d1f17aab0ce96c4d83405d1e3baba1dffa86ecccee7f1c1b80b1bbf859106ce2b647ae1e4a6a9b584ae1dfc0a4deebb755638f1d95dcc79b1be263177e2a05c72bde545d09ba726f41d9547117e876af81bfc672e33c71442eb05675d9552df1b313d1f9934f9ddd08955fa21d6edf23000a277f6f149591299a0a96032861ecdc96bb76afa05a2bffb445d61dc891bc70c13695920b911cad0df3fa842a3e2318c57556974343f69794cb8fa18c1ad624835857e4781041198aa705c4d11f3ef82e941be2aee7a770e54521312fe6facbaf1138eee08fa90fae986a5d93719aeb30ac292a49c1d91bf4574d553a92a4a6c305ab09db6bbeffd84c7aa707f1c1628a0220d6ba4ee5e960566686228a6e766d8a30dddf30ed5aa637c949950c3d0e894a7560670b6879a7d70f3c7e5ab29aed236cc3527bdea076fec8add12d784fbcf9a", "68f62c418a6b97026cc70f6abf8419b671ee373709fa13074e37bd39f0a50fcb");
+ TestSHA3_256("460f8c7aac921fa9a55800b1d04cf981717c78217cd43f98f02c5c0e66865c2eea90bcce0971a0d22bc1c74d24d9bfea054e558b38b8502fccb85f190d394f2f58f581a02d3b9cc986f07f5a67d57ab4b707bd964ecc10f94f8cc538b81eeb743746c537407b7b575ced0e1ec4c691a72eb0978be798e8be22b278b390be99c730896fdc69b6a44456be5ee261366e8b1351cbb22aa53e45ec325ed2bca0bfeeebc867d7d07681581b6d56ed66ac78280df04053407a7b57561261dd644cc7b20f0a95709e42795b5402dd89fcb11746c597e0b650a008bc085c681bb24b17db4458e1effba3f414a883ddfc4bccb3ace24d9223839d4b3ca9185ad5cc24193134b9339b0e205a4cc0fa3d8f7a85b4230d1b3ee101fbae9ee14c2153da5f337c853573bd004114cb436ee58ab1648373ee07cc39f14198ac5a02a4dd0585cf83dfd4899df88e8859dae8bc351af286642c1c25737bf8712cb941cbbb741d540feb9f5d831f901fbe2d6facd7dab626bd705f2fd7c9a7a0e7a9127e3451af2ae8509dd7b79dce41c1e30b9dba1c38cb4861dad3ac00d68fa5d07ba591c1c3b9d6b7d6e08099d0572ca4c475240601decba894fa3c4b0ea52ed687281beee268a1c8535e283b1fc7c51aa31d5ec098c50fec958acdd0d54a49643bef170093a1102a1b3bf5ad42fb55ebaf7db07385eadcd6e66da8b7b6e6c022a1e3d01f5fccec86365d3014c159a3bff17d614751b3fa0e8e89152936e159b7c0ea8d71cd4ffd83adae209b254b793f6f06bb63838a303b95c85b4edfa4ddcca0ed952165930bca87140f67f5389d1233fe04f0a3d647050410c44d389513084ad53155af00de02cc7943a3b988d8e1454f85153aff0816e24b964ec91dc514c588a93634ff3dd485c40575faa2f254abdf86fbcf6d381337601a7b1ba5b99719f045eb7bf6f2e8b9dd9d053ef0b3126f984fc9ea87a2a70b3798fab593b83a4ff44d9c0c4ec3e570ac537c10d9e3c4996027a813b70d7867b858f31f508aa56e7b087370707974b2186f02f5c549112f2158c0d365402e52cba18fe245f77f7e6fbf952ec2dc3c880b38be771caea23bc22838b1f70472d558bdf585d9c77088b7ba2dceaeb3e6f96df7d91d47da1ec42be03936d621ecf747f24f9073c122923b4161d99bc8190e24f57b6fac952ed344c7eae86a5f43c08089c28c7daf3aa7e39c59d6f1e17ece1977caf6b4a77a6ff52774521b861f38ebc978005e5763cc97123e4d17c7bc4134c8f139c7d7a9a02646fef9525d2a6871fc99747e81430b3fec38c677427c6f5e2f16c14eee646ebf6eb16775ad0957f8684c7045f7826bc3736eca", "7d495ddf961cbff060f80b509f2b9e20bed95319eef61c7adb5edeec18e64713");
+ TestSHA3_256("c8a2a26587d0126abe9ba8031f37d8a7d18219c41fe639bc7281f32d7c83c376b7d8f9770e080d98d95b320c0f402d57b7ef680da04e42dd5211aacf4426ecca5050ca596312cfae79cee0e8c92e14913cc3c66b24ece86c2bfa99078991faad7b513e94f0b601b7853ddb1eb3c9345f47445a651389d070e482ea5db48d962820257daf1cbe4bb8e5f04a3637d836c8c1bc4d83d6eda5f165f2c2592be268412712ae324ef054bb812f56b8bc25c1d59071c64dd3e00df896924c84575817027861faa5f016c5c74142272daa767e8c9dacee4c732ab08b5fa9ad65a0b74c73fb5a889169f645e50d70e41d689415f7d0b4ec071e9238b5a88110856fc6ae9b9944817e21597d1ccd03b60e60472d1e11d3e9063de24a7b59609b6a2a4ee68238690cf2800614746941c48af9566e07494f0dd236e091e75a8f769e3b179b30c10f5277eec7b3f5c97337189b8b82bc5e717ff27355b2009356caa908e976ae1d7f7a94d36202a8d5e03641aeac0e453a8168ee5a0858ceecfcbf11fb8c1f033201add297a0a89476d2ea8b9a82bda8c3c7ef4f55c3295a4ecb7c607ac73d37eadc13b7a2494ec1928f7a80c8d534efe38a3d9ccb4ccdab9f092a1def6478532c5ad3cd5c259b3812600fa89e6d1e228114795d246cedc9c9fff0d1c1297a5ddfc1169c2efb3800df8dd18a8511214785abcc1bc7eb31bdb2f5f70358dfe860ed5a03ab7e95cc21df5ee7aee68be568d6985e5c1e91408e4432663b1c4e6d613d6dc382b5b900a4fc1b7a9c27a1138c5e2356ab9026c34465006602753daf6ab7427da93c307c901d0bb1ddb21c53bc0493dd8d857161e8ffa51fdecb75568243205aa979c2e7ed2a77b5f8edc34cffb0321a8c653bc381f96ab85a86bf0bb2c9518208d636eac40aa7ad754260a75d4a46362f994c90173b975afb0ee17601311b1c51ba562c1ca7e3c2dd18b90bdebb1858fe876c71b3ad742c4bcba33e7763c750098de856fde8731cb6d698218be9f0a98298630e5b374957d126cf0b1c489c48bab6b50f6fb59ee28be6c3916bbd16514234f80e1ac15d0215852b87f9c6e429eb9f85007bf6ae3de1af0202861fd177c7c4f51af533f956a051815815c6e51e25af20d02893e95442991f1de5f86a4397ae20d9f675657bf9f397267831e94cef4e4d287f759850350ce0898f2e29de3c5c41f4246fe998a8d1359a2bed36ded1e4d6b08682025843700fee8cab56703e342212870acdd53655255b35e414fa53d9810f47a37195f22d72f6e555392023a08adc282c585b2ae62e129efccdc9fe9617eecac12b2ecdabd247a1161a17750740f90ebed3520ceb17676f1fa87259815ff415c2794c5953f689c8d5407dbbd10d1241a986e265cea901af34ec1ded0323ca3290a317208ba865637af4797e65b9cfcad3b931bbf6ac896623e2f4408529172911f1b6a9bcae8279ec7e33452d0cd7b026b46a99cbe8a69cd4d21cdc6d3a84002fab527c4fd18a121526d49890ced3fb89beb384b524015a2e03c049241eb9", "b8d4b29b086ef6d6f73802b9e7a4f2001e384c8258e7046e6779662fd958517e");
+ TestSHA3_256("3a86a182b54704a3af811e3e660abcfbaef2fb8f39bab09115c1068976ff694bb6f5a3839ae44590d73e4996d45af5ceb26b03218ab3fef6f5f4ef48d22839fb4371c270f9535357b22142c4ffb54e854b64cab41932fe888d41ca702e908c63eae244715bfbf69f481250f16f848dc881c6996e6f9d76f0e491de2c129f2a2ab22e72b04644f610a2fabc45aa2d7b3e5d77b87a135d2fd502ca74a207bddaf9a43e945245961a53c7bfcfe73a1ae090e6606ffe8ddbf1e0f0d6d4fa94526578c6faf282dd592b10bf4bce00a7b1846625690623667e83b9b59b465d42c6944e224ad36698f5f2ee938404b7775c2e66207bc41025adaf07590312f398812d24c0178126fdd334964a54b8353482a83be17cf2ee52d23b72e5f57fe31eebf8a1a64742eb9459bcb0eca231a1658ab88b7056d8e47554f0a46058d6565c6cbf6edec45fdde6f051e38255b82493de27ffd3efbe1b179b9642d2166073db6d4832707420237a00bad7125795e645e5bc3e1431ecbabf0ff5f74416626322545c966241cce6d8f2c035a78f100e030741f13b02a9eaf618d468bc40274db98bc342be12ad4d892c2ba546e571c556ac7cbf4e4c3fd3431efd40457cf65a297845dd8cce09811418c3cef941ff32c43c375157f6f49c2e893625e4b216b1f985aa0fd25f29a9011d4f59c78b037ed71f384e5de8116e3fc148c0a3cad07cb119b9829aac55eed9a299edb9abc5d017be485f690add70ff2efbb889ac6ce0da9b3bdbeb9dd47823116733d58a8d510b7f2e2c8244a2cbf53816b59e413207fb75f9c5ce1af06e67d182d3250ea3283bcbb45cb07ea6a6aa486361eb6f69199c0eb8e6490beff82e4ab274b1204e7f2f0ba097fba0332aa4c4a861771f5b3d45ce43e667581a40fee4bebe7fa9d87b70a5bb876c928f7e6d16ae604b3a4e9c7f1d616e2deab96b6207705b9a8f87468503cdd20a3c02cc8da43d046da68b5ed163d926a5a714a4df1b8ef007bca408f68b9e20de86d6398ad81df5e74d5aaac40874b5d6787211ff88e128cf1676e84ca7f51aee5951efee1915dcc11502a8df74fac4c8451dda49b631a8fb87470f0ebe9b67449bbd1640ceee6101e8cd82aa1033fa84f75b28450e461b93f65da5c43759b0e83660d50961702bb1ad015dad42e600117475237cf6e7279d4a02d1f67cf59de0108355d03963e3d84ce7647173dd7d77a6b3f275d7de74236d7bbb2df437d536136dbe1dbe8f307facc7bc7d0cde1abf745cbeb81af1ab2c46138cf007e901f22668377958bcbbadb7e9905973b27ff0c5baaece25e974c1bd116cc81dd1c81a30bae86a6fb12c6a5494068e122153128313eb3e628d76e9babc823c9eb9d3b81bacfa7a6b372abe6b1246a350f23e2e95b09c9037a75aac255ef7d4f267cad3ce869531b4165db2e5a9792094efea4ae3d9ea4d0efdc712e63df21882a353743190e016b2166e4da8a2c78e48defc7155d5fdfc4e596624e6a19c91b43719a22c1204b1cefe05989d455773d3881fa8d3eefc255f81dfe90bd41dc6f1e9c265a753298a6e98c999acd9525a9db5f9f9456a0f51a93dd9693e1d9c3fa283f7c58a9c752afcaa635abea8dfc80e2c326b939260069457fdad68c341852dcb5fcbbd351318defd7ae3b9f827478eb77306a5ae14cf8895f2bc6f0f361ffc8aa37e286629dc7e59b73a8712525e851c64d363065631edc1609f3d49a09575876a", "b71ec00c0fcc4f8663312711540df1cd236eb52f237409415b749ff9436dc331");
+ TestSHA3_256("c041e23b6d55998681802114abc73d2776967cab715572698d3d497ec66a790b0531d32f45b3c432f5b2d8039ea47de5c6060a6514f3ff8fb5f58e61fd1b5b80524c812a46dad56c035a6e95ecb465ea8176d99b836e36f65977b7dbb3932a706d3af415b6f2549b7120ecb0db1e7d9e6f8df23607eda006436bccd32ef96d431fa434d9de22ca2608ab593eb50b4d6a57f45c1ce698c3283a77d330b876ad6030324a5c0693be7790a4bd26c0a25eb403531f37689829c20546d6dc97327131688b3d88766db8f5d1b22050450c37e53951446dd7155a3e6d7edcbe1354411d8f58154475d74008937e8ba48b706066c296d1a87936dd023ac8eebe7605a58c6c40da774cf9df189db0050adcf7629e66cbd1cf9824397834cb13c4066c26e6c8ec950b44fc1c8db8ef976a7ec8c4f4ec9849ca7a07f906223053b80db24b946b034ee7a30880d0ace348acba0d0ed21ea443816706a216ce9eb682d1fe9dfc1d2e0bf3b1449247413520b8d8ebc99fc298c6dca949be0ffebe450b9b79a387a615d617b8d9da5b3e8d2776208c7cc2a11bdbc387f9d4597b380739b24ae59dcd5fb63bfeefe0746d9266cfda18afa583d6891e483e6d5c0db305f5609beba75bb5b447ccac2dfb94ede4a94db6eaaf3070d8d5353f107f7bd74528eb913e0b19bed6236a3b48567c46a9eec28fb6486f92d0d09625452d8f4dd1b89c566533cc2326b820c2b9efed43be8481cb9ad809e47af7b31795cb0fbdb18fbb12e8853f8bacec366a092daf8f2a55d2911fc7c70ddd33d33e86c2c4ceeb9390ec506b399f6fa8f35abf7789d0f547fd09cb7e6fb6016a3fc2a27a762989ae620d234c810777d5a1bb633744af2844495d2963c986ef8540ca715bed7692c77b9dec90e06acc5986b47dd4a8d3ca3300b2bedf9f26ae6d1c7e7acef05c0fc521c3309e1e70771eea6e96b67de5e3fb6833145bb73d46081b074539498307929da779e003c27f0a171035458b8c7c86c905b23dda74c040878d5a05be94821537724ebd5608ec0754c3e3e99a719bbb6d5320eed07323fca637429b18378936364c389de1e9c6fce8af270a713b4b829b43e7d761e17724c22e84611e1322dde45cbee86a0e16d01cfb8910d99391c39afd8e5f5567c59f219aa8c19ad158f287cb6807ba1fe46d38d091639a217766b3aa9ded73ac14570ba236225218305d68c0be6099c336ba8455c86b7c8c04542e729ceb84596c33ca6eb7ec1091b406cf64495ccfa2169f47b3b590477d4073537c14c05015d51ba527b3869ae4ebd603df906323658b04cb11e13bc29b34ac69f18dd49f8958f7e3f5b05ab8b8ddb34e581bde5eb49dd15698d2d2b68fe7e8baf88d8f395cfcafcdff38cf34b59386f6f77333483655ee316f12bfeb00610d8cba9e59e637ca2cab6ed24dd584143844e61fcca994ba44a4c029682997ab04285f479a6dc2c854c569073c62cd68af804aa70f4976d5b9f6b09d3738fcccb6d60e11ba97a4001062195d05a43798d5f24e9466f082ac367169f892dfd6cc0adeef82212c867a49cba65e0e636bab91e2176d3865634aa45b13c1e3e7cdb4e7872b2437f40f3de5493792c06611a9ca97d0baed71bfb4e9fdd58191198a8b371aea7f65b6e851ce22f4808377d09b6a5a9f04eddc3ff4ef9fd8bf043bb559e1df5319113cb8beea9e06b0c05c50885873acd19f6e8a109c894403a415f627cd1e8f7ca54c288c230795aaddde3a787c2a20ac6dee4913da0240d6d971f3fce31fc53087afe0c45fa3c8f744c53673bec6231abe2623029053f4be0b1557e00b291ebb212d876e88bcc81e5bd9eb820691dca5fbdcc1e7a6c58945a2cac8db2d86c2a7d98dc5908598bda78ce202ac3cd174d48ad9cac9039e27f30658eef6317cd87c199944343e7fce1b3ea7", "ad635385a289163fbaf04b5850285bfe3759774aee7fd0211d770f63985e1b44");
+ TestSHA3_256("01ec0bfc6cc56e4964808e2f1e516416717dad133061e30cb6b66b1dc213103b86b3b017fa7935457631c79e801941e3e3a0e1a3016d435e69a390eaac64f3166d944c8eb8df29fe95fdf27adc34631e4a1f3ff1d5af430f3d6f5908e40c0f83df1447274dfe30bbe76b758bd9abb40ed18331c7552dcc6959a1303e11134ec904bd0aab62de33c39703b99920851afd9d531eeb28f1c4b2e6c17c55db8296320316fbe19e881b5fcb4d266c58ca7f31d9176e26f70315330b58a516ec60d10404a78393aa03ced7acd225cb2a83caf3ab5888406a69a534f1ed1346e9b5e68831f90b872d57367361191c803eb7e38b3b9cd601282d5efdbf082db07d89bd06b093f986d08d3a7b12aa74513b6eb241b26ebf31da5726d59e315d1b4ee53ec6a9fdb6583bacc136e90e9607cab01e5d3853ab9727ede706b6f10b4e04d0510f45c0abc515bcb5ed0bcce86a92861126f4d502fcb8f988d62ecf9d124853de2bab633f9506c6fde8a36cd4413cf773e50f7b2d283482f18e2f547c2fc275cd60056ed98fb8d0816fd777c1566f0c2ae3b1cd92e344910a75e006106d193e06f7786ae37dd0e529cacf74176fd4cc1f6500549af5902dbbd56a70c194f5b671372edec425f90add40b4eb3d55123f3ab62797ad25bf5eecf4f417f86b00e6f76a4f52e44fd949851aae649dd0d26d641d4c1f343c7a2c851ca7851bbbdfd57ed6024eabc518a909a1e4689ea7bc5f83e19872950368a06e93ab41944c3d8befc5705b814e5f33511a7f7ea8a4771c804b321a3a3f32c18fa127d3c9e6c011337dc100ceb156ed45d0a62f238dacac44a3429f89bb7f98d09043c42451106e30471cc6fab7a4e1ce0a8202772b0218b631f287ec3ef82b1aa6299a0b54d6aad06aa9346d28f117d20f3b7f0d462267bd3c685cca8f4584532dfee0e8b9bacefa3092d28fcce7953a28f82e4ba6b3a1430ecca58b770dab656bed1b224663e196dffc28c96a2c65ef9de1989a125ecf2fed47eb96bef8a636a91bd521c47aeb8bc011bf81cc688fd8b620446353cbf7692201b5552cb07fb02eb3954dfaa6f5c31bf91e20b84419dcbbdaba0c31a124d8f4218b2f88da3eba44dbe40eb290052538dccd0ff7670de5f33a83ff74895b66adcff58c9c21e93b31bb49ccb2e026995ee155b5517b72daa76526a2e42aa6fa94357cd42e2a8a1d3e7d4cefc33d5d07d6303d798d2551a21f862b5f492d0c7cf078a77007a02847b34675dfad4fb457e9f20dc5750fb127a3c31b9d6a3996d50ac3ffc6ef29cca1d8414d0438bf3271dc4f4e00cfe19a507b447dc310f74aeb2a3c0b3fae6d7d13f4935bc72c35df3efa6e879164421505ee32d93b030e32a7970b53430b1643855167278e5058c4a48a7840e2fcdb282e45b5b86c0b2756f19b595f3bcfc926df35e33ac26dd1e88cd394015a5f54deb4c9f4a0bef0eabcb27c4eb88dc2302f09e92f1bcc4b4754df1eeb536154543c7dbf181c9979fe6ed08311e5a3acf365ebb5745212b2630e83b3a5bd5fa4834c727248b165700c7435f8cb6ee455bad16ee0da68fe6acd2062dae9c8bc178b157b29ade98a9bbbd4c723a3dcb7852c7978b488e4f73a2c9163dbdffae175119f812b6f4b70c2b498704bc2b58603f167f277a74e64ec296a6dfdb0de3486c0f36ac1b55f80af9fc817ba4f84b898b2a3c5725e2faf466bb26a8a84f91e123d182033a7ae2029236aa4b673ceb50c1733d7edd60e3f119b7141c882d508e0331689c96fbfb9f7e888fe88561de427c721123036737c1460b0da00d3f958b948f68fcb321ab4e297290f781ff8afb06b755d82a7e6ce1963761d799eed786524bf19801b4877b2d856becdf7e87d71aa359f2d51f09de64bcbf27d0c3aceac70790e314fd06c2f5216f3d10574b7302d6bc2775b185145c1b741524567c456d42c5826f93afa20ae7196ca7224c3b69b1eada9eee752fb6d43f24170fcc02af7e1dea73f0f884f936f900165800acb9d57480a31e409d3f676ed92b6812cf182a088fc49d68082aa19c7be0711f436db1d7be44d97dc9405591a8d3e7f6f731c6f3e6c401749829b7624497f5eeac1fc782e7d6988340541f2617a317e", "2a6283b1c02c6aaf74c4155091ff54a904bb700077f96a9c4bd84e8e51b54d01");
+ TestSHA3_256("9271fd111dcf260c04cf4b748f269ac80f7485c41f7724352a7ed40b2e2125b0bf30f3984ee9d21aab6eb07ec976b557c2426e131ad32bd0485aa57172f0e4f1798760f8352067ac023fbeca7b9c8bf5851c724e90ffff44195b44ae73c9c317c85e8e585bddac6d0f2abf812d02e44b62eadb9d0765683aa56af8e9b91588c7b49dc3e146866a02dc18f9ca680f88006094ef29096c2d5af5700b4aca3dfcab462c48bb8085691671efb5ceb22b3ebd8702f71a1d7c184b1053c3fa30a7e76b85f3650d9140714fd4993bb496becf2ae01d3a98ccfdefb6fefd692173bd11af7adb61ffff214a550ffcd3a5993004ee72cb02ca9c577b42c85444e619e6411e2bca86bb548ebbd12a02c5c945eaa3b246f595d817f3849875429e72ac894160a2a91a6617f18e6b2b9258472152741d62843cebc537d25f0daebdedb410d71ee761662bd1b189ca1c93d648b5d141d8d05e3f2b2d8c2c40997fea7eb7e2cc0000d8b2300936759704ef85f38ad0d08a986de6bfd75b5be3209f6d4d3f67e7adf7f8469d47e81979ec8dae7127b5eadcc09779cf4b0a28efaaf58e83d307f2ced4a8699b142f3f19db5598e914e9577652c63f851203580d40699548fc2ab30a9dcf6452f673ad1ed92f8d84dad5dfff55e18107b3acb6e4e8e3c9c34038f40a5c577fe9771c2c31ef03d36a00e04a20d2d0877db66f091dac4b741d2a997b75182702881f9284fa23b9b3c20e715f80d07b9910a4b3185f9489dc7d3fb510f4da273559753d7d207f3975b48df2e7c857caffe703dfac53a786490c09f57d2fa93f60810186df4c0b6b616a04caab9f70a5002c5e5d8da0ed2805f20fbf89cd8d57ca2b4bd37125ce38bf09fb6170ae21f4e6043a9483ef6e585756d97cfb778e57bc7ddc8dfc54d086d6bcfa1f019c749ff79921ec56e833ff8660f0959cd4b5277e9f3b1d4880193fefa98a6c2512718e7c139acdcd324303db3adb70348d09b058baf0e91d52b24952f832b0a3b81fa9bc9a2e9fb276a64e9e0922778b4992d892f6845b4372a28e47d27b53443586d9015463cacb5b65c617f84e1168b15988737a7eda8187f1f4165fecbdd032ae04916cc4b6e18a87558d2ce6a5946c65a9446f66cda139a76506c60d560f56a013b508d6ccbbaa14e24ad0729dd823bf214efcc59e6932cdc860306687c84a63efb551237223641554940a7a60fa7e6ddad64a21b4a2176b046dc480b6c5b5ff7ed96e3211df609195b4028756c22479ba278105771493870372abe24dcc407daa69878b12b845908cf2e220e7fabeeaab88c8f64f864c2bacba0c14b2a693e45aacc6b7db76bc1a2195cfce7b68f3c99440477ea4c1ea5ee78c109f4f1b553c76eb513dd6e16c383ce7f3187ad66c1d5c982724de8e16299c2fde0a8af22e8de56e50a56ac0fef1c52e76864c0ad1eeedd8907065b37892b3eca0ddcdf5c8e0917dec78fedd194ea4b380a059ccc9452e48a9eba2f8b7a4150b7ba17feac83c61604c3cfcfe6655c2be37ef0ae6fc29072f9b1cfb277b64a8d499dd079ad9aa3d5e9a7ccbec8c100596c6fac51e13a260d78d8cd9066edc558e2219cfcda1310dc1fbbdd36f348756855349f33eb6b82186a8c1a55f361305833edd3e4ac8d9b9cf99897c4e06c19ed10765fd0c8c7433851445c5f87b119ef913b2bcdbf7aa2ad19c672e53a9c6c3c309d549513edd7c1cf8a0a399e6df0939cc1fb146d6ad460e2ce05144c69eafa3822141d473fbe5927c58a50c1e842f8b8fad85540ce9f6d06f7b4dea045248b999d24c5fd4d75631caf73518cc08f73684e2a1cd4266235d90c08a0d0ce8784c776fd1b80978b83f0705ba8498744884d5496b791f2db3ffb5377175856b25a643803aa8b9e7f1055e089c1929cf0cbba7674c204c4590fb076968e918e0390d268eeef78c2aebcbf58a429f28212a2425c6ad8970b6a09cadddd8336d519bca4820556d2c4b8cd9f41216de3c728a0774edf47d3489cd29cf1b2a192bc53325d0bed7d23e51be7684297f9d0ecb14acbf648bc440c5fde997acc464fb45e965e6f0dced6d4568ebcd55e5a64633b05a2cb4d8263b721a252b1710dc84d8a5d4b43fcc875e2e7281f621b0bf8bb3465be364456bcd990b26b3e474486f864fb85f320f68bc14c37d271249b18552bef50dfc385a9f41b831589c5a716357cf5a12520d582d00452a8ab21643dd180071d2041bbc5972099141c6292009540d02f3252f1f59f8dfcf4488803f3b0df41759055559a334e68c98ea491b0984f2f82a35db84ea0779b3801cf06b463a832e", "4e75bf3c580474575c96ec7faa03feb732379f95660b77149974133644f5d2a0");
+ TestSHA3_256("075997f09ab1980a3179d4da78c2e914a1ff48f34e5d3c2ab157281ef1841052d0b45a228c3cd6b5028efd2d190d76205e1fdf4cec83c9868fe504f429af1e7c5423267c48a7b5bc005f30a1980147a3fae5c100b95c7cb23d43af9f21d87311d9cc826598993e077015f59ebc476383bb7a78787d915c97039ab188a2a618f7a8d7f64542ba787e9dd7d48c4c87d2aaea068c1b00c9711b2812901673c11418096d0a850fb36b0acece56d311689dfeceb0835009adc427f6d2d6b05ed26f5a43b6478bc72c1f914a2202dbd393cb69b1a1e78162e55ca4b3030ac0298131a7a0d934c032cc9dfc5afa600c59b064d2d9013f15d1184278a8ccb5ad9d7563e666fe5a8c173cec34467ef9cf6d6671208ff714741fee7c8d1d565edf82570dffde4f3f584024142056d8548ad55df83d1babed06141114c95ac88dbea0ce35d950f16d8a732a1ea7d22dfaa75a3e0410c546523277261116a64bcbb2be83e55e040f6c8c79f911b301a8718cc4b19a81d5f0cb6312d87c5b4b079e23a61d247541cfc2c41a37f52b2c6e43a3db5dc47892d0e1feabcc5c808f2391791e45fb065159f99c1d8dd2f69baaf75267eb89dd460f1b6c0badb96cbbc8291cefa370fa7ad6997a4ca2b1fe968216032f02f29837d40215fa219c09161df074e1de8e37056e28c86d1f992a651e271dfc4b0592ad481c613fd00c3eea4b6deabb9f5aa63a4830ed49ab93624fa7b208966eccb1f293f4b9a46411f37d7928e4478dde2f608d3851a8efa68e9d45402bc5124fde4ddc0f83ef82b31019d0aacb4b5121bbc064c95c5292da97981f58f051df9502054bf728e9d4fb7e04787a0890922b30a3f66a760e3d3763855e82be017fa603630a33115a02f02386982001def905784f6ba307a598c6dbaf2946fe9e978acbaf3e4ba50ab49ae8e9582520fc2eb6790deafc77e04a8ee75da92d16f0d249403112c74bc09102b573e110ccb4d8461d249bfe2e85fc9770d606be6fbfd5ec4c30ac306d46412f736e5b696ccc9fbe4adea730955c55ea5c63678271d34b7bd6f6340e72626d290820eeb96a0d2d25ea81361a122ffe8e954cf4ff84f4dafcc5c9d3e7c2ddbdf95ed2c0862d3f2783e4566f450ec49e8b01d9d7bf11e92a7903f2b045c57ed8a65ccbfc5b1d2a38e020a57b38f2e4deea8a52354a7e7be4f977b8f5afe30f6738e955c8bda295064586b6827b245766b217fe39263572b0850965c7ae845611b8efb64c36244a39b9fed0ab970ee5ddeb8f2608dd9c963524a14050c9101d7f2d5537b24d0b0f7a45703c1e131656ec9edc12cdf71dae1cde2790b888ef2a589f03201f8bbfad71f0c4430477a6713ad2e50aaefa1f840cbb839e277389454517e0b9bd76a8ecc5c2e22b854c25ff708f9256d3700adeaec49eb2c4134638ee9bd649b4982f931ec3b23cc819fbc835ddcb3d65e04585aa005e13b7ef8fcafa36cc1a2c79ba6c26fc1dc0f6668f9432c578088cd33a41a778ac0b298fcac212edab724c9fb33d827409fd36bc4b2b0e4e81006fd050d94d3271e0427c61e9ddca599a3c9480cfdd33603cb1a196557281ce6a375fef17463893db293dba0704d4bfda25e08beadd4208c58ea0d8d9066448910b087fc13792fc44075a3fe42e13c5792f093a552aa8ebe0f63e7a807102d5bc145468a0cb469263035c5647049054c18199f7da6d6defd51105e2125c605e327aca137ca85e3f7f46ca69f92d5252f84418293f4e9afeeb067c79576e88cc3c64f3e61d76e1e9e2f72cdfc35261a9679f0c374d7436ff6cfe2ba71650810522fa554a4aded87ad23f0b206b1bc63f56bbff8bcc8849d99e209bd519a953f32c667aa8cd874ad99846ed94b92f88fe0dbf788c8431dc76ca9553692622077da2cdea666c1b3fee7c335da37737afccd3d400a23d18f5bd3784dbcd0663a38acb5a2beef03fc0a1c52ee0b56bda4493f2221e35bee59f962f16bc6781133204f032c7a6209dd3dabd6100325ec14e3ab0d05aadd03fdfe9f8737da15edab9d2598046f8c6dd8381aaf244821994d5a956073c733bcebf9edbc2a6e2676242dc4e6a2e4ba8a7d57ed509340d61fae2c82bee4dedc73b469e202cc0916250d40a1718090690a1d3b986cf593b019b7b7f79ae14843b2e7ccf0fd85218184f7844fbb35e934476841b056b3a75bf20abb6866e19a0614e6a1af0eee4de510535724363b6598cccf08a99066021653177559c57e5aaff4417670a98fe4bd41a137c384f98c0324c20ef8bc851a9b975e9440191ff08deb78c9fa6fc29c76b371a4a1fa08c30fc9d1b3323d897738495086bfd43ef24c650cfa80c42ecbadc0453c4437d1a11b467e93ca95fbae98d38dcb2da953e657fb7ea6c8493d08cf028c5d3eb0fcbcb205493f4658440719e076e02deb07332d093e4d256175ca56f4c785d5e7e26c6090a20429f70b3757daac54153bc16f5828dc6c1c9f5186e2117754be5f1b46b3631980d9e4a9a5c", "2e07737d271b9a0162eb2f4be1be54887118c462317eb6bd9f9baf1e24111848");
+ TestSHA3_256("119a356f8c0790bbd5e9f3b4c5c4a70e97f462364c88cad04d5435645342b35484e94e12df61908fd95546f74859849b817ee92fbd242435c210b7b9bfbffb3f77f965faa1a9073e8feb5a380f673add8fde32208402fa680c8b3e41d187a15131f1028f9d86feaf3fd4b6e0e094d2ba0839c67267c9535173ec51645343ad74fcfaae389aa17cca3137e2588488531c36ba2b8e2f2238d8415c798a0b9a258f1e3cef605fa18977ad3d6707c3ecc5ea5f86ebdaa4b4b0e5bc023d1bc335138ae0de506cb52f2d9efa0ecc546468310cccc88ec08d28c3602e07257f41bb7e4d8a0956c564f3712761d199a931a39e69c5a69aa7b3257931dd92b91e4ed56fbf64e48bd334945cfa2aaf576df04614eb914899f7df54db4012cc8261b12bedcab69876feedbbf7009dcf8d076af89b797ad71217d75cf07514dc07ae34640055c74c9faf560f491f015ac3e167623cfbc67b8e7163e7c1b92debd06e9d28b049e0298f4c38395a40a0778162af2cfe5abe5b946c4d9a54f2a321660ab521068c4957cd3f5be0324cc04f50f209fdea7caaa0ac705c1fb30abfa550e844f509074afde1ee87adda29aa09b7f93e7d064ad2715ee5571ee6e7c9a01672124cc2a22b4354c3844759c1a6ce3fdf17555cac7df73334073ef3730939410fe6cf37463352ad241958b7fafbc66e0d592df48bf55ab2c33428e494c6995826892572d9ab52747b1085fcbef318cfe9cfacd4cd80c164fba584c1344ae7e321c4f77b44db6b322f2f7831f4d4ede7dc407b065c6754cf4424f9903adb4c6b675ded58700bc36da83fd95e84e76c404f7342921ef23d7d07772f5b8ec2f077601cae13448385b04e074f895574be61a831a87efd68a1f6aa67cf291847fa8f74cbe3b4a78ad780895183bc51c30ad2514255d4e013abc097bc8103c0b1933b0b303341836ae167c1e31dfe5f1b791cb06ef29cae398065343eecf06e4ae2048d1547c4bf69ccec5e86c45867c633c62f7d27dc51234b6debb5b9f80a5810716240c64443d0c098c80220d0520a5f5834369b9eb019325e23e88f237c24440bf27959caf7e7e4f1671fda710630255a2962f7e9b3625dc243a0177aacf6a758a68aa85dc3f56181a4a59a406c7fae5575c9e2c64248f520b5a5f904821661e2e43a5a058f445fd0e55b07476c4122d18033053b45112201e0bfdcc9e7cb9931155018ca431a0564930aca8defbca954b2680753a4060bec2cb668d2c15e77cba29589b5c7c07bc7177a8b1adb3a6968732f9213476fd96901514626fa17243af1d156cd037eea81d773f1f71a018d942b524b851794b300c7591ecd783ec8066ccb261bdf9b7a183dbda42b92593b614297dcb0fabcc23ae69797d0251b8ab57a4da2a544615216b01f4dbe2d8c9b5520c7ed9cd9312e9ec6d05a36e7f693d1821d727518169b03976394b9d1e1d7fa2daa25529d391eb5d0cf0f07a8160be2ee043d9345037c655c4f2023689f14d8d2072dd92c1dba056a5b5d4c4fc4196e25caab05b1701ec666ac9a04d90f7d7575a7ac3970252c18fd3bec0cc448e5ff8f3765d546a4a8ad1d41a9640c79375b80534b7b50989976f238654fefea981c9413130beae943a3e9d8f64ce9256d1259d1b2a6b3c02ca5af1a701db8f25a4e9c255dad8785172f323728c3585a45206ae988c283e30a2f9ea9b47f07a7521b0f36e9c504c14bd96027e8d24161e70f196576d8a74a5e9c26acda7cc452a90e550e625a49e50829db70de808c827c67d00c23ee073d4e72aeed891dd73b86acd6756e753e3975a80cdab1d521052caef6a5380f8b03023ba0326a6928aa127ffb33b51dcb05bbdd592d0ad9e8321e6ef2f95c401be6a37e634425689fe7750e2a0fe05ad89001502b309095ca517b2e2ed0388b9f2c59c45feb61222539d6e1ccd397344d23708aebacec10ada96a7711f173f7ff1e4b94fceec6a0a0ea5d814a4581b412063012ff6ac5527b8314d00326b68c2304a276a217fde9fa4034750a2e47e10f816870d12fc4641a27a1c16c35a953f32685f2b92cae0519848045765591c42ddc402dc7c6914d74dd38d2b5e7f35358cb1d91a9f681fde7fd6c7af5840663525ee1d04bf6d3156fed018c44043d95383d92dada3d1cd84af51d9bee814ec8675073e1c48632c5f59e257682542e1f7dc20b56b5c92a9e2cb2be30cb1512fb55fa1de99a3f5864ed3acc19d79e6ffb0da3b08ba0615157747d75c1f308fa0202a4086f34e9eafa3e071dfbacaca731d228aa0304cf390c0a9e6ad7ce22ade758965cfbfc4c9390d24e41a667447fc7b29821464ad98bc5d65dc7f9c42bd4b23e174015592ff92c905660a2722f9fc7973d3cdad848ef88bf02b1b03dea16699b71dc46b35bc4d96069a0753335ae38685d244918e30c5fb0d45283a1281c1659ea591573999d9c2acd2ca9141d55230d41011b70748b518e1cd2fa58ad8dc05fcbdf0bffaf2c7fd6cb2ac67bb13b8f6d31fad64ac113664223599dca411270955c95aec06518894dabc352d2b70984727437040d944da7b42e0ef560ac532de3e4a4891e8509c275b51ed780f8660b0354e12c21b3e11bcc88198980b5f7ff31ad342182d5a933373164dced3cfb2a081720d7eee676cb7378a3e19326a7ee67fd6c00521f9de37c66bcea814b6feb6a061b8cdcf7b4bd8f45d48602c5", "c26d0b064e409df64819cd7c1a3b8076f19815b9823adac4e3ce0b4d3a29de18");
+ TestSHA3_256("72c57c359e10684d0517e46653a02d18d29eff803eb009e4d5eb9e95add9ad1a4ac1f38a70296f3a369a16985ca3c957de2084cdc9bdd8994eb59b8815e0debad4ec1f001feac089820db8becdaf896aaf95721e8674e5d476b43bd2b873a7d135cd685f545b438210f9319e4dcd55986c85303c1ddf18dc746fe63a409df0a998ed376eb683e16c09e6e9018504152b3e7628ef350659fb716e058a5263a18823d2f2f6ee6a8091945a48ae1c5cb1694cf2c1fe76ef9177953afe8899cfa2b7fe0603bfa3180937dadfb66fbbdd119bbf8063338aa4a699075a3bfdbae8db7e5211d0917e9665a702fc9b0a0a901d08bea97654162d82a9f05622b060b634244779c33427eb7a29353a5f48b07cbefa72f3622ac5900bef77b71d6b314296f304c8426f451f32049b1f6af156a9dab702e8907d3cd72bb2c50493f4d593e731b285b70c803b74825b3524cda3205a8897106615260ac93c01c5ec14f5b11127783989d1824527e99e04f6a340e827b559f24db9292fcdd354838f9339a5fa1d7f6b2087f04835828b13463dd40927866f16ae33ed501ec0e6c4e63948768c5aeea3e4f6754985954bea7d61088c44430204ef491b74a64bde1358cecb2cad28ee6a3de5b752ff6a051104d88478653339457ac45ba44cbb65f54d1969d047cda746931d5e6a8b48e211416aefd5729f3d60b56b54e7f85aa2f42de3cb69419240c24e67139a11790a709edef2ac52cf35dd0a08af45926ebe9761f498ff83bfe263d6897ee97943a4b982fe3404ef0b4a45e06113c60340e0664f14799bf59cb4b3934b465fabefd87155905ee5309ba41e9e402973311831ea600b16437f71df39ee77130490c4d0227e5d1757fdc66af3ae6b9953053ed9aafca0160209858a7d4dd38fe10e0cb153672d08633ed6c54977aa0a6e67f9ff2f8c9d22dd7b21de08192960fd0e0da68d77c8d810db11dcaa61c725cd4092cbff76c8e1debd8d0361bb3f2e607911d45716f53067bdc0d89dd4889177765166a424e9fc0cb711201099dda213355e6639ac7eb86eca2ae0ab38b7f674f37ef8a6fcca1a6f52f55d9e1dcd631d2c3c82bba129172feb991d5af51afecd9d61a88b6832e4107480e392aed61a8644f551665ebff6b20953b635737a4f895e429fddcfe801f606fbda74b3bf6f5767d0fac14907fcfd0aa1d4c11b9e91b01d68052399b51a29f1ae6acd965109977c14a555cbcbd21ad8cb9f8853506d4bc21c01e62d61d7b21be1b923be54914e6b0a7ca84dd11f1159193e1184568a6134a6bbadf5b4df986edcf2019390ae841cfaa44435e28ce877d3dae4177992fa5d4e5c005876dbe3d1e63bec7dcc0942762b48b1ecc6c1a918409a8a72812a1e245c0c67be6e729c2b49bc6ee4d24a8f63e78e75db45655c26a9a78aff36fcd67117f26b8f654dca664b9f0e30681874cb749e1a692720078856286c2560b0292cc837933423147569350955c9571bf8941ba128fd339cb4268f46b94bc6ee203eb7026813706ea51c4f24c91866fc23a724bf2501327e6ae89c29f8db315dc28d2c7c719514036367e018f4835f63fdecd71f9bdced7132b6c4f8b13c69a517026fcd3622d67cb632320d5e7308f78f4b7cea11f6291b137851dc6cd6366f2785c71c3f237f81a7658b2a8d512b61e0ad5a4710b7b124151689fcb2116063fbff7e9115fed7b93de834970b838e49f8f8ba5f1f874c354078b5810a55ae289a56da563f1da6cd80a3757d6073fa55e016e45ac6cec1f69d871c92fd0ae9670c74249045e6b464787f9504128736309fed205f8df4d90e332908581298d9c75a3fa36ab0c3c9272e62de53ab290c803d67b696fd615c260a47bffad16746f18ba1a10a061bacbea9369693b3c042eec36bed289d7d12e52bca8aa1c2dff88ca7816498d25626d0f1e106ebb0b4a12138e00f3df5b1c2f49d98b1756e69b641b7c6353d99dbff050f4d76842c6cf1c2a4b062fc8e6336fa689b7c9d5c6b4ab8c15a5c20e514ff070a602d85ae52fa7810c22f8eeffd34a095b93342144f7a98d024216b3d68ed7bea047517bfcd83ec83febd1ba0e5858e2bdc1d8b1f7b0f89e90ccc432a3f930cb8209462e64556c5054c56ca2a85f16b32eb83a10459d13516faa4d23302b7607b9bd38dab2239ac9e9440c314433fdfb3ceadab4b4f87415ed6f240e017221f3b5f7ac196cdf54957bec42fe6893994b46de3d27dc7fb58ca88feb5b9e79cf20053d12530ac524337b22a3629bea52f40b06d3e2128f32060f9105847daed81d35f20e2002817434659baff64494c5b5c7f9216bfda38412a0f70511159dc73bb6bae1f8eaa0ef08d99bcb31f94f6be12c29c83df45926430b366c99fca3270c15fc4056398fdf3135b7779e3066a006961d1ac0ad1c83179ce39e87a96b722ec23aabc065badf3e188347a360772ca6a447abac7e6a44f0d4632d52926332e44a0a86bff5ce699fd063bdda3ffd4c41b53ded49fecec67f40599b934e16e3fd1bc063ad7026f8d71bfd4cbaf56599586774723194b692036f1b6bb242e2ffb9c600b5215b412764599476ce475c9e5b396fbcebd6be323dcf4d0048077400aac7500db41dc95fc7f7edbe7c9c2ec5ea89943fe13b42217eef530bbd023671509e12dfce4e1c1c82955d965e6a68aa66f6967dba48feda572db1f099d9a6dc4bc8edade852b5e824a06890dc48a6a6510ecaf8cf7620d757290e3166d431abecc624fa9ac2234d2eb783308ead45544910c633a94964b2ef5fbc409cb8835ac4147d384e12e0a5e13951f7de0ee13eafcb0ca0c04946d7804040c0a3cd088352424b097adb7aad1ca4495952f3e6c0158c02d2bcec33bfda69301434a84d9027ce02c0b9725dad118", "d894b86261436362e64241e61f6b3e6589daf64dc641f60570c4c0bf3b1f2ca3");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index b1a635d9da..6743dc0070 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -47,7 +47,6 @@ struct CConnmanTest : public CConnman {
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
-extern void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
struct COrphanTx {
CTransactionRef tx;
@@ -79,16 +78,16 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
+ const CChainParams& chainparams = Params();
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", /*fInboundIn=*/ false);
- dummyNode1.SetSendVersion(PROTOCOL_VERSION);
+ CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY);
+ dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
// This test requires that we have a chain with non-zero work.
@@ -133,15 +132,14 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
- vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY));
CNode &node = *vNodes.back();
- node.SetSendVersion(PROTOCOL_VERSION);
+ node.SetCommonVersion(PROTOCOL_VERSION);
peerLogic.InitializeNode(&node);
- node.nVersion = 1;
node.fSuccessfullyConnected = true;
connman->AddNode(node);
@@ -149,10 +147,10 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
+ const CChainParams& chainparams = Params();
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
- const Consensus::Params& consensusParams = Params().GetConsensus();
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
CConnman::Options options;
options.nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
@@ -167,18 +165,18 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
}
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ peerLogic->CheckForStaleTipAndEvictPeers();
// No nodes should be marked for disconnection while we have no extra peers
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}
- SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1);
+ SetMockTime(GetTime() + 3 * chainparams.GetConsensus().nPowTargetSpacing + 1);
// Now tip should definitely be stale, and we should look for an extra
// outbound peer
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ peerLogic->CheckForStaleTipAndEvictPeers();
BOOST_CHECK(connman->GetTryNewOutboundPeer());
// Still no peers should be marked for disconnection
@@ -191,8 +189,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// required time connected check should be satisfied).
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<max_outbound_full_relay; ++i) {
+ peerLogic->CheckForStaleTipAndEvictPeers();
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
// Last added node should get marked for eviction
@@ -204,8 +202,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// peer, and check that the next newest node gets evicted.
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<max_outbound_full_relay-1; ++i) {
+ peerLogic->CheckForStaleTipAndEvictPeers();
+ for (int i = 0; i < max_outbound_full_relay - 1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
@@ -221,21 +219,18 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
+ const CChainParams& chainparams = Params();
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true);
- dummyNode1.SetSendVersion(PROTOCOL_VERSION);
+ CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND);
+ dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
- }
+ peerLogic->Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
@@ -244,25 +239,18 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
- CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true);
- dummyNode2.SetSendVersion(PROTOCOL_VERSION);
+ CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND);
+ dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode2);
- dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
- }
+ peerLogic->Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
- }
+ peerLogic->Misbehaving(dummyNode2.GetId(), 1, /* message */ ""); // 2 reaches discouragement threshold
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
@@ -277,25 +265,22 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
+ const CChainParams& chainparams = Params();
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
int64_t nStartTime = GetTime();
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true);
- dummyNode.SetSendVersion(PROTOCOL_VERSION);
+ CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", ConnectionType::INBOUND);
+ dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode);
- dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
- }
+ peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(dummyNode.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
index 40ca01bd9f..e3aefa18a3 100644
--- a/src/test/fuzz/asmap.cpp
+++ b/src/test/fuzz/asmap.cpp
@@ -33,7 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
if (buffer.size() < 1 + 3 + 4) return;
int asmap_size = 3 + (buffer[0] & 127);
bool ipv6 = buffer[0] & 128;
- int addr_size = ipv6 ? 16 : 4;
+ const size_t addr_size = ipv6 ? ADDR_IPV6_SIZE : ADDR_IPV4_SIZE;
if (buffer.size() < size_t(1 + asmap_size + addr_size)) return;
std::vector<bool> asmap = ipv6 ? IPV6_PREFIX_ASMAP : IPV4_PREFIX_ASMAP;
asmap.reserve(asmap.size() + 8 * asmap_size);
@@ -43,7 +43,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
}
if (!SanityCheckASMap(asmap)) return;
+
+ const uint8_t* addr_data = buffer.data() + 1 + asmap_size;
CNetAddr net_addr;
- net_addr.SetRaw(ipv6 ? NET_IPV6 : NET_IPV4, buffer.data() + 1 + asmap_size);
+ if (ipv6) {
+ assert(addr_size == ADDR_IPV6_SIZE);
+ net_addr.SetLegacyIPv6(Span<const uint8_t>(addr_data, addr_size));
+ } else {
+ assert(addr_size == ADDR_IPV4_SIZE);
+ in_addr ipv4;
+ memcpy(&ipv4, addr_data, addr_size);
+ net_addr.SetIP(CNetAddr{ipv4});
+ }
(void)net_addr.GetMappedAS(asmap);
}
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 3edcf96495..664e65accc 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -7,6 +7,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <hash.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -32,6 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CSHA1 sha1;
CSHA256 sha256;
CSHA512 sha512;
+ SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
while (fuzzed_data_provider.ConsumeBool()) {
@@ -51,6 +53,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)ripemd160.Write(data.data(), data.size());
(void)sha1.Write(data.data(), data.size());
(void)sha256.Write(data.data(), data.size());
+ (void)sha3.Write(data);
(void)sha512.Write(data.data(), data.size());
(void)sip_hasher.Write(data.data(), data.size());
@@ -65,11 +68,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)ripemd160.Reset();
(void)sha1.Reset();
(void)sha256.Reset();
+ (void)sha3.Reset();
(void)sha512.Reset();
break;
}
case 2: {
- switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 8)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
case 0: {
data.resize(CHash160::OUTPUT_SIZE);
hash160.Finalize(data);
@@ -115,9 +119,21 @@ void test_one_input(const std::vector<uint8_t>& buffer)
data[0] = sip_hasher.Finalize() % 256;
break;
}
+ case 9: {
+ data.resize(SHA3_256::OUTPUT_SIZE);
+ sha3.Finalize(data);
+ break;
+ }
}
break;
}
}
}
+ if (fuzzed_data_provider.ConsumeBool()) {
+ uint64_t state[25];
+ for (size_t i = 0; i < 25; ++i) {
+ state[i] = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ }
+ KeccakF(state);
+ }
}
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 54793c890f..26c56fbadf 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -13,6 +13,7 @@
#include <key.h>
#include <merkleblock.h>
#include <net.h>
+#include <netbase.h>
#include <node/utxo_snapshot.h>
#include <primitives/block.h>
#include <protocol.h>
@@ -44,9 +45,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- CDataStream ds(SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(SER_NETWORK, version);
ds << obj;
return ds;
}
@@ -79,9 +80,9 @@ void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj)
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- assert(Deserialize<T>(Serialize(obj)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version)) == obj);
}
} // namespace
@@ -183,16 +184,18 @@ void test_one_input(const std::vector<uint8_t>& buffer)
#elif NETADDR_DESERIALIZE
CNetAddr na;
DeserializeFromFuzzingInput(buffer, na);
- AssertEqualAfterSerializeDeserialize(na);
+ if (na.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(na);
+ }
+ AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
#elif SERVICE_DESERIALIZE
CService s;
DeserializeFromFuzzingInput(buffer, s);
AssertEqualAfterSerializeDeserialize(s);
#elif MESSAGEHEADER_DESERIALIZE
- const CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
- CMessageHeader mh(pchMessageStart);
+ CMessageHeader mh;
DeserializeFromFuzzingInput(buffer, mh);
- (void)mh.IsValid(pchMessageStart);
+ (void)mh.IsCommandValid();
#elif ADDRESS_DESERIALIZE
CAddress a;
DeserializeFromFuzzingInput(buffer, a);
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 1e1807d734..753cfffdcb 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -12,15 +12,6 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-// Decide if main(...) should be provided:
-// * AFL needs main(...) regardless of platform.
-// * macOS handles __attribute__((weak)) main(...) poorly when linking
-// against libFuzzer. See https://github.com/bitcoin/bitcoin/pull/18008
-// for details.
-#if defined(__AFL_COMPILER) || !defined(MAC_OSX)
-#define PROVIDE_MAIN_FUNCTION
-#endif
-
#if defined(PROVIDE_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp
index 3597f51e51..2b181c6da1 100644
--- a/src/test/fuzz/locale.cpp
+++ b/src/test/fuzz/locale.cpp
@@ -52,7 +52,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale);
const int64_t atoi64_without_locale = atoi64(random_string);
const int atoi_without_locale = atoi(random_string);
- const int64_t atoi64c_without_locale = atoi64(random_string.c_str());
const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
const std::string tostring_without_locale = ToString(random_int64);
// The variable `random_int32` is no longer used, but the harness still needs to
@@ -80,8 +79,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
const int64_t atoi64_with_locale = atoi64(random_string);
assert(atoi64_without_locale == atoi64_with_locale);
- const int64_t atoi64c_with_locale = atoi64(random_string.c_str());
- assert(atoi64c_without_locale == atoi64c_with_locale);
const int atoi_with_locale = atoi(random_string);
assert(atoi_without_locale == atoi_with_locale);
const std::string tostring_with_locale = ToString(random_int64);
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
new file mode 100644
index 0000000000..3818838765
--- /dev/null
+++ b/src/test/fuzz/net.cpp
@@ -0,0 +1,153 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <net.h>
+#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
+#include <protocol.h>
+#include <random.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const BasicTestingSetup basic_testing_setup;
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const std::optional<CAddress> address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address) {
+ return;
+ }
+ const std::optional<CAddress> address_bind = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address_bind) {
+ return;
+ }
+
+ CNode node{fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
+ fuzzed_data_provider.ConsumeIntegral<int>(),
+ INVALID_SOCKET,
+ *address,
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ *address_bind,
+ fuzzed_data_provider.ConsumeRandomLengthString(32),
+ fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}),
+ fuzzed_data_provider.ConsumeBool()};
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
+ case 0: {
+ node.CloseSocketDisconnect();
+ break;
+ }
+ case 1: {
+ node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32));
+ break;
+ }
+ case 2: {
+ node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ }
+ case 3: {
+ const std::vector<bool> asmap = ConsumeRandomLengthIntegralVector<bool>(fuzzed_data_provider, 128);
+ if (!SanityCheckASMap(asmap)) {
+ break;
+ }
+ CNodeStats stats;
+ node.copyStats(stats, asmap);
+ break;
+ }
+ case 4: {
+ const CNode* add_ref_node = node.AddRef();
+ assert(add_ref_node == &node);
+ break;
+ }
+ case 5: {
+ if (node.GetRefCount() > 0) {
+ node.Release();
+ }
+ break;
+ }
+ case 6: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ node.AddAddressKnown(*addr_opt);
+ break;
+ }
+ case 7: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
+ node.PushAddress(*addr_opt, fast_random_context);
+ break;
+ }
+ case 8: {
+ const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+ if (!inv_opt) {
+ break;
+ }
+ node.AddKnownTx(inv_opt->hash);
+ break;
+ }
+ case 9: {
+ node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider));
+ break;
+ }
+ case 10: {
+ const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (!service_opt) {
+ break;
+ }
+ node.SetAddrLocal(*service_opt);
+ break;
+ }
+ case 11: {
+ const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ bool complete;
+ node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete);
+ break;
+ }
+ }
+ }
+
+ (void)node.GetAddrLocal();
+ (void)node.GetAddrName();
+ (void)node.GetId();
+ (void)node.GetLocalNonce();
+ (void)node.GetLocalServices();
+ (void)node.GetMyStartingHeight();
+ const int ref_count = node.GetRefCount();
+ assert(ref_count >= 0);
+ (void)node.GetCommonVersion();
+ (void)node.RelayAddrsWithConn();
+
+ const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ?
+ fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({NetPermissionFlags::PF_NONE, NetPermissionFlags::PF_BLOOMFILTER, NetPermissionFlags::PF_RELAY, NetPermissionFlags::PF_FORCERELAY, NetPermissionFlags::PF_NOBAN, NetPermissionFlags::PF_MEMPOOL, NetPermissionFlags::PF_ISIMPLICIT, NetPermissionFlags::PF_ALL}) :
+ static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ (void)node.HasPermission(net_permission_flags);
+ (void)node.ConnectedThroughNetwork();
+}
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index 2901c704f6..8252f38726 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -17,9 +17,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider);
- for (int i = 0; i < 15; ++i) {
- (void)net_addr.GetByte(i);
- }
(void)net_addr.GetHash();
(void)net_addr.GetNetClass();
if (net_addr.GetNetwork() == Network::NET_IPV4) {
@@ -78,7 +75,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)net_addr.ToString();
(void)net_addr.ToStringIP();
- const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ const CSubNet sub_net{net_addr, fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
(void)sub_net.IsValid();
(void)sub_net.ToString();
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
index 6fba2bfaba..7e216e16fe 100644
--- a/src/test/fuzz/p2p_transport_deserializer.cpp
+++ b/src/test/fuzz/p2p_transport_deserializer.cpp
@@ -19,7 +19,8 @@ void initialize()
void test_one_input(const std::vector<uint8_t>& buffer)
{
- V1TransportDeserializer deserializer{Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION};
+ // Construct deserializer, with a dummy NodeId
+ V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION};
const char* pch = (const char*)buffer.data();
size_t n_bytes = buffer.size();
while (n_bytes > 0) {
@@ -31,16 +32,13 @@ void test_one_input(const std::vector<uint8_t>& buffer)
n_bytes -= handled;
if (deserializer.Complete()) {
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
- const CNetMessage msg = deserializer.GetMessage(Params().MessageStart(), m_time);
- assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
- assert(msg.m_raw_message_size <= buffer.size());
- assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
- assert(msg.m_time == m_time);
- if (msg.m_valid_header) {
- assert(msg.m_valid_netmagic);
- }
- if (!msg.m_valid_netmagic) {
- assert(!msg.m_valid_header);
+ uint32_t out_err_raw_size{0};
+ 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/process_message.cpp b/src/test/fuzz/process_message.cpp
index 9e40d5cd55..3ef03137ec 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -30,18 +30,6 @@
#include <string>
#include <vector>
-void ProcessMessage(
- CNode& pfrom,
- const std::string& msg_type,
- CDataStream& vRecv,
- const std::chrono::microseconds time_received,
- const CChainParams& chainparams,
- ChainstateManager& chainman,
- CTxMemPool& mempool,
- CConnman& connman,
- BanMan* banman,
- const std::atomic<bool>& interruptMsgProc);
-
namespace {
#ifdef MESSAGE_TYPE
@@ -80,17 +68,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
return;
}
CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
- CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, false).release();
+ CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY).release();
p2p_node.fSuccessfullyConnected = true;
p2p_node.nVersion = PROTOCOL_VERSION;
- p2p_node.SetSendVersion(PROTOCOL_VERSION);
+ p2p_node.SetCommonVersion(PROTOCOL_VERSION);
connman.AddTestNode(p2p_node);
- g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+ g_setup->m_node.peerman->InitializeNode(&p2p_node);
try {
- ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream, GetTime<std::chrono::microseconds>(),
- Params(), *g_setup->m_node.chainman, *g_setup->m_node.mempool,
- *g_setup->m_node.connman, g_setup->m_node.banman.get(),
- std::atomic<bool>{false});
+ g_setup->m_node.peerman->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream,
+ GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 91ebf9fb1b..f722eeac3a 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -44,16 +44,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
const ServiceFlags service_flags = ServiceFlags(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
- const bool inbound{fuzzed_data_provider.ConsumeBool()};
- const bool block_relay_only{fuzzed_data_provider.ConsumeBool()};
- peers.push_back(MakeUnique<CNode>(i, service_flags, 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, inbound, block_relay_only).release());
+ const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
+ peers.push_back(MakeUnique<CNode>(i, service_flags, 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, conn_type).release());
CNode& p2p_node = *peers.back();
p2p_node.fSuccessfullyConnected = true;
p2p_node.fPauseSend = false;
p2p_node.nVersion = PROTOCOL_VERSION;
- p2p_node.SetSendVersion(PROTOCOL_VERSION);
- g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+ p2p_node.SetCommonVersion(PROTOCOL_VERSION);
+ g_setup->m_node.peerman->InitializeNode(&p2p_node);
connman.AddTestNode(p2p_node);
}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 85aac6ac7a..4274fa4351 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -63,8 +63,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
int required_ret;
(void)ExtractDestinations(script, type_ret, addresses, required_ret);
- (void)GetScriptForWitness(script);
-
const FlatSigningProvider signing_provider;
(void)InferDescriptor(script, signing_provider);
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
new file mode 100644
index 0000000000..d20fa43d68
--- /dev/null
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -0,0 +1,200 @@
+// 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 <test/fuzz/fuzz.h>
+
+#include <primitives/transaction.h>
+#include <pubkey.h>
+#include <script/interpreter.h>
+#include <serialize.h>
+#include <streams.h>
+#include <univalue.h>
+#include <util/strencodings.h>
+
+#include <boost/algorithm/string.hpp>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+// This fuzz "test" can be used to minimize test cases for script_assets_test in
+// src/test/script_tests.cpp. While it written as a fuzz test, and can be used as such,
+// fuzzing the inputs is unlikely to construct useful test cases.
+//
+// Instead, it is primarily intended to be run on a test set that was generated
+// externally, for example using test/functional/feature_taproot.py's --dumptests mode.
+// The minimized set can then be concatenated together, surrounded by '[' and ']',
+// and used as the script_assets_test.json input to the script_assets_test unit test:
+//
+// (normal build)
+// $ mkdir dump
+// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
+// $ ...
+//
+// (fuzz test build)
+// $ mkdir dump-min
+// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
+// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
+
+namespace {
+
+std::vector<unsigned char> CheckedParseHex(const std::string& str)
+{
+ if (str.size() && !IsHex(str)) throw std::runtime_error("Non-hex input '" + str + "'");
+ return ParseHex(str);
+}
+
+CScript ScriptFromHex(const std::string& str)
+{
+ std::vector<unsigned char> data = CheckedParseHex(str);
+ return CScript(data.begin(), data.end());
+}
+
+CMutableTransaction TxFromHex(const std::string& str)
+{
+ CMutableTransaction tx;
+ try {
+ VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx;
+ } catch (const std::ios_base::failure&) {
+ throw std::runtime_error("Tx deserialization failure");
+ }
+ return tx;
+}
+
+std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
+{
+ if (!univalue.isArray()) throw std::runtime_error("Prevouts must be array");
+ std::vector<CTxOut> prevouts;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ CTxOut txout;
+ try {
+ VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout;
+ } catch (const std::ios_base::failure&) {
+ throw std::runtime_error("Prevout invalid format");
+ }
+ prevouts.push_back(std::move(txout));
+ }
+ return prevouts;
+}
+
+CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
+{
+ if (!univalue.isArray()) throw std::runtime_error("Script witness is not array");
+ CScriptWitness scriptwitness;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ auto bytes = CheckedParseHex(univalue[i].get_str());
+ scriptwitness.stack.push_back(std::move(bytes));
+ }
+ return scriptwitness;
+}
+
+const std::map<std::string, unsigned int> FLAG_NAMES = {
+ {std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
+ {std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
+ {std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
+ {std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
+ {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
+ {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
+ {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
+};
+
+std::vector<unsigned int> AllFlags()
+{
+ std::vector<unsigned int> ret;
+
+ for (unsigned int i = 0; i < 128; ++i) {
+ unsigned int flag = 0;
+ if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
+ if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
+ if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
+ if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
+ if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
+
+ // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
+ if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
+ // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
+ if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
+
+ ret.push_back(flag);
+ }
+
+ return ret;
+}
+
+const std::vector<unsigned int> ALL_FLAGS = AllFlags();
+
+unsigned int ParseScriptFlags(const std::string& str)
+{
+ if (str.empty()) return 0;
+
+ unsigned int flags = 0;
+ std::vector<std::string> words;
+ boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
+
+ 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;
+ }
+
+ return flags;
+}
+
+void Test(const std::string& str)
+{
+ UniValue test;
+ if (!test.read(str) || !test.isObject()) throw std::runtime_error("Non-object test input");
+
+ CMutableTransaction tx = TxFromHex(test["tx"].get_str());
+ const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
+ if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
+ size_t idx = test["index"].get_int64();
+ if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
+ unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
+ bool final = test.exists("final") && test["final"].get_bool();
+
+ if (test.exists("success")) {
+ tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_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);
+ 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.
+ if (final || ((flags & test_flags) == flags)) {
+ (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ }
+ }
+ }
+
+ if (test.exists("failure")) {
+ tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_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);
+ 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) {
+ (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ }
+ }
+ }
+}
+
+ECCVerifyHandle handle;
+
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ 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&) {}
+}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 434a47b702..87af71897b 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -35,11 +35,19 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
- const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
- if (pub_key) {
- const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- if (!random_bytes.empty()) {
- (void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
+ const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
+ if (random_bytes.size() == 64) {
+ (void)caching_transaction_signature_checker.VerifySchnorrSignature(random_bytes, pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
+ } else {
+ const auto random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const auto pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (pub_key) {
+ if (!random_bytes.empty()) {
+ (void)caching_transaction_signature_checker.VerifyECDSASignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
}
}
}
diff --git a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
new file mode 100644
index 0000000000..d4f302a8d3
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
@@ -0,0 +1,38 @@
+// 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 <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char* out32, const unsigned char* seckey, size_t seckeylen);
+int ec_seckey_export_der(const secp256k1_context* ctx, unsigned char* seckey, size_t* seckeylen, const unsigned char* key32, bool compressed);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ {
+ std::vector<uint8_t> out32(32);
+ (void)ec_seckey_import_der(secp256k1_context_sign, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE);
+ }
+ {
+ std::vector<uint8_t> seckey(CKey::SIZE);
+ const std::vector<uint8_t> key32 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
+ size_t seckeylen = CKey::SIZE;
+ const bool compressed = fuzzed_data_provider.ConsumeBool();
+ const bool exported = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, key32.data(), compressed);
+ if (exported) {
+ std::vector<uint8_t> out32(32);
+ const bool imported = ec_seckey_import_der(secp256k1_context_sign, out32.data(), seckey.data(), seckey.size()) == 1;
+ assert(imported && key32 == out32);
+ }
+ }
+ secp256k1_context_destroy(secp256k1_context_sign);
+}
diff --git a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
new file mode 100644
index 0000000000..ed8c7aba89
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
@@ -0,0 +1,33 @@
+// 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 <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+bool SigHasLowR(const secp256k1_ecdsa_signature* sig);
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* input, size_t inputlen);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> signature_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (signature_bytes.data() == nullptr) {
+ return;
+ }
+ secp256k1_context* secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_ecdsa_signature sig_der_lax;
+ const bool parsed_der_lax = ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig_der_lax, signature_bytes.data(), signature_bytes.size()) == 1;
+ if (parsed_der_lax) {
+ ECC_Start();
+ (void)SigHasLowR(&sig_der_lax);
+ ECC_Stop();
+ }
+ secp256k1_context_destroy(secp256k1_context_verify);
+}
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index 3aaeb66649..e121c89665 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -28,7 +28,12 @@ public:
{
}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ {
+ return m_fuzzed_data_provider.ConsumeBool();
+ }
+
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_fuzzed_data_provider.ConsumeBool();
}
diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp
new file mode 100644
index 0000000000..786f1a83fe
--- /dev/null
+++ b/src/test/fuzz/signet.cpp
@@ -0,0 +1,32 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <primitives/block.h>
+#include <signet.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+void initialize()
+{
+ InitializeFuzzingContext(CBaseChainParams::SIGNET);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::optional<CBlock> block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
+ if (!block) {
+ return;
+ }
+ (void)CheckSignetBlockSolution(*block, Params().GetConsensus());
+ (void)SignetTxs::Create(*block, ConsumeScript(fuzzed_data_provider));
+}
diff --git a/src/test/fuzz/txrequest.cpp b/src/test/fuzz/txrequest.cpp
new file mode 100644
index 0000000000..9529ad3274
--- /dev/null
+++ b/src/test/fuzz/txrequest.cpp
@@ -0,0 +1,374 @@
+// 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 <crypto/common.h>
+#include <crypto/sha256.h>
+#include <crypto/siphash.h>
+#include <primitives/transaction.h>
+#include <test/fuzz/fuzz.h>
+#include <txrequest.h>
+
+#include <bitset>
+#include <cstdint>
+#include <queue>
+#include <vector>
+
+namespace {
+
+constexpr int MAX_TXHASHES = 16;
+constexpr int MAX_PEERS = 16;
+
+//! Randomly generated GenTxids used in this test (length is MAX_TXHASHES).
+uint256 TXHASHES[MAX_TXHASHES];
+
+//! Precomputed random durations (positive and negative, each ~exponentially distributed).
+std::chrono::microseconds DELAYS[256];
+
+struct Initializer
+{
+ Initializer()
+ {
+ for (uint8_t txhash = 0; txhash < MAX_TXHASHES; txhash += 1) {
+ CSHA256().Write(&txhash, 1).Finalize(TXHASHES[txhash].begin());
+ }
+ int i = 0;
+ // DELAYS[N] for N=0..15 is just N microseconds.
+ for (; i < 16; ++i) {
+ DELAYS[i] = std::chrono::microseconds{i};
+ }
+ // DELAYS[N] for N=16..127 has randomly-looking but roughly exponentially increasing values up to
+ // 198.416453 seconds.
+ for (; i < 128; ++i) {
+ int diff_bits = ((i - 10) * 2) / 9;
+ uint64_t diff = 1 + (CSipHasher(0, 0).Write(i).Finalize() >> (64 - diff_bits));
+ DELAYS[i] = DELAYS[i - 1] + std::chrono::microseconds{diff};
+ }
+ // DELAYS[N] for N=128..255 are negative delays with the same magnitude as N=0..127.
+ for (; i < 256; ++i) {
+ DELAYS[i] = -DELAYS[255 - i];
+ }
+ }
+} g_initializer;
+
+/** Tester class for TxRequestTracker
+ *
+ * It includes a naive reimplementation of its behavior, for a limited set
+ * of MAX_TXHASHES distinct txids, and MAX_PEERS peer identifiers.
+ *
+ * All of the public member functions perform the same operation on
+ * an actual TxRequestTracker and on the state of the reimplementation.
+ * The output of GetRequestable is compared with the expected value
+ * as well.
+ *
+ * Check() calls the TxRequestTracker's sanity check, plus compares the
+ * output of the constant accessors (Size(), CountLoad(), CountTracked())
+ * with expected values.
+ */
+class Tester
+{
+ //! TxRequestTracker object being tested.
+ TxRequestTracker m_tracker;
+
+ //! States for txid/peer combinations in the naive data structure.
+ enum class State {
+ NOTHING, //!< Absence of this txid/peer combination
+
+ // Note that this implementation does not distinguish between DELAYED/READY/BEST variants of CANDIDATE.
+ CANDIDATE,
+ REQUESTED,
+ COMPLETED,
+ };
+
+ //! Sequence numbers, incremented whenever a new CANDIDATE is added.
+ uint64_t m_current_sequence{0};
+
+ //! List of future 'events' (all inserted reqtimes/exptimes). This is used to implement AdvanceToEvent.
+ std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,
+ std::greater<std::chrono::microseconds>> m_events;
+
+ //! Information about a txhash/peer combination.
+ struct Announcement
+ {
+ std::chrono::microseconds m_time;
+ uint64_t m_sequence;
+ State m_state{State::NOTHING};
+ bool m_preferred;
+ bool m_is_wtxid;
+ uint64_t m_priority; //!< Precomputed priority.
+ };
+
+ //! Information about all txhash/peer combination.
+ Announcement m_announcements[MAX_TXHASHES][MAX_PEERS];
+
+ //! The current time; can move forward and backward.
+ std::chrono::microseconds m_now{244466666};
+
+ //! Delete txhashes whose only announcements are COMPLETED.
+ void Cleanup(int txhash)
+ {
+ bool all_nothing = true;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ const Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state != State::NOTHING) {
+ if (ann.m_state != State::COMPLETED) return;
+ all_nothing = false;
+ }
+ }
+ if (all_nothing) return;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ }
+ }
+
+ //! Find the current best peer to request from for a txhash (or -1 if none).
+ int GetSelected(int txhash) const
+ {
+ int ret = -1;
+ uint64_t ret_priority = 0;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ const Announcement& ann = m_announcements[txhash][peer];
+ // Return -1 if there already is a (non-expired) in-flight request.
+ if (ann.m_state == State::REQUESTED) return -1;
+ // If it's a viable candidate, see if it has lower priority than the best one so far.
+ if (ann.m_state == State::CANDIDATE && ann.m_time <= m_now) {
+ if (ret == -1 || ann.m_priority > ret_priority) {
+ std::tie(ret, ret_priority) = std::tie(peer, ann.m_priority);
+ }
+ }
+ }
+ return ret;
+ }
+
+public:
+ Tester() : m_tracker(true) {}
+
+ std::chrono::microseconds Now() const { return m_now; }
+
+ void AdvanceTime(std::chrono::microseconds offset)
+ {
+ m_now += offset;
+ while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
+ }
+
+ void AdvanceToEvent()
+ {
+ while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
+ if (!m_events.empty()) {
+ m_now = m_events.top();
+ m_events.pop();
+ }
+ }
+
+ void DisconnectedPeer(int peer)
+ {
+ // Apply to naive structure: all announcements for that peer are wiped.
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ if (m_announcements[txhash][peer].m_state != State::NOTHING) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ Cleanup(txhash);
+ }
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.DisconnectedPeer(peer);
+ }
+
+ void ForgetTxHash(int txhash)
+ {
+ // Apply to naive structure: all announcements for that txhash are wiped.
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ }
+ Cleanup(txhash);
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ForgetTxHash(TXHASHES[txhash]);
+ }
+
+ void ReceivedInv(int peer, int txhash, bool is_wtxid, bool preferred, std::chrono::microseconds reqtime)
+ {
+ // Apply to naive structure: if no announcement for txidnum/peer combination
+ // already, create a new CANDIDATE; otherwise do nothing.
+ Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state == State::NOTHING) {
+ ann.m_preferred = preferred;
+ ann.m_state = State::CANDIDATE;
+ ann.m_time = reqtime;
+ ann.m_is_wtxid = is_wtxid;
+ ann.m_sequence = m_current_sequence++;
+ ann.m_priority = m_tracker.ComputePriority(TXHASHES[txhash], peer, ann.m_preferred);
+
+ // Add event so that AdvanceToEvent can quickly jump to the point where its reqtime passes.
+ if (reqtime > m_now) m_events.push(reqtime);
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ReceivedInv(peer, GenTxid{is_wtxid, TXHASHES[txhash]}, preferred, reqtime);
+ }
+
+ void RequestedTx(int peer, int txhash, std::chrono::microseconds exptime)
+ {
+ // Apply to naive structure: if a CANDIDATE announcement exists for peer/txhash,
+ // convert it to REQUESTED, and change any existing REQUESTED announcement for the same txhash to COMPLETED.
+ if (m_announcements[txhash][peer].m_state == State::CANDIDATE) {
+ for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
+ if (m_announcements[txhash][peer2].m_state == State::REQUESTED) {
+ m_announcements[txhash][peer2].m_state = State::COMPLETED;
+ }
+ }
+ m_announcements[txhash][peer].m_state = State::REQUESTED;
+ m_announcements[txhash][peer].m_time = exptime;
+ }
+
+ // Add event so that AdvanceToEvent can quickly jump to the point where its exptime passes.
+ if (exptime > m_now) m_events.push(exptime);
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.RequestedTx(peer, TXHASHES[txhash], exptime);
+ }
+
+ void ReceivedResponse(int peer, int txhash)
+ {
+ // Apply to naive structure: convert anything to COMPLETED.
+ if (m_announcements[txhash][peer].m_state != State::NOTHING) {
+ m_announcements[txhash][peer].m_state = State::COMPLETED;
+ Cleanup(txhash);
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ReceivedResponse(peer, TXHASHES[txhash]);
+ }
+
+ void GetRequestable(int peer)
+ {
+ // Implement using naive structure:
+
+ //! list of (sequence number, txhash, is_wtxid) tuples.
+ std::vector<std::tuple<uint64_t, int, bool>> result;
+ std::vector<std::pair<NodeId, GenTxid>> expected_expired;
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ // Mark any expired REQUESTED announcements as COMPLETED.
+ for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
+ Announcement& ann2 = m_announcements[txhash][peer2];
+ if (ann2.m_state == State::REQUESTED && ann2.m_time <= m_now) {
+ expected_expired.emplace_back(peer2, GenTxid{ann2.m_is_wtxid, TXHASHES[txhash]});
+ ann2.m_state = State::COMPLETED;
+ break;
+ }
+ }
+ // And delete txids with only COMPLETED announcements left.
+ Cleanup(txhash);
+ // CANDIDATEs for which this announcement has the highest priority get returned.
+ const Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state == State::CANDIDATE && GetSelected(txhash) == peer) {
+ result.emplace_back(ann.m_sequence, txhash, ann.m_is_wtxid);
+ }
+ }
+ // Sort the results by sequence number.
+ std::sort(result.begin(), result.end());
+ std::sort(expected_expired.begin(), expected_expired.end());
+
+ // Compare with TxRequestTracker's implementation.
+ std::vector<std::pair<NodeId, GenTxid>> expired;
+ const auto actual = m_tracker.GetRequestable(peer, m_now, &expired);
+ std::sort(expired.begin(), expired.end());
+ assert(expired == expected_expired);
+
+ m_tracker.PostGetRequestableSanityCheck(m_now);
+ assert(result.size() == actual.size());
+ for (size_t pos = 0; pos < actual.size(); ++pos) {
+ assert(TXHASHES[std::get<1>(result[pos])] == actual[pos].GetHash());
+ assert(std::get<2>(result[pos]) == actual[pos].IsWtxid());
+ }
+ }
+
+ void Check()
+ {
+ // Compare CountTracked and CountLoad with naive structure.
+ size_t total = 0;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ size_t tracked = 0;
+ size_t inflight = 0;
+ size_t candidates = 0;
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ tracked += m_announcements[txhash][peer].m_state != State::NOTHING;
+ inflight += m_announcements[txhash][peer].m_state == State::REQUESTED;
+ candidates += m_announcements[txhash][peer].m_state == State::CANDIDATE;
+ }
+ assert(m_tracker.Count(peer) == tracked);
+ assert(m_tracker.CountInFlight(peer) == inflight);
+ assert(m_tracker.CountCandidates(peer) == candidates);
+ total += tracked;
+ }
+ // Compare Size.
+ assert(m_tracker.Size() == total);
+
+ // Invoke internal consistency check of TxRequestTracker object.
+ m_tracker.SanityCheck();
+ }
+};
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ // Tester object (which encapsulates a TxRequestTracker).
+ Tester tester;
+
+ // Decode the input as a sequence of instructions with parameters
+ auto it = buffer.begin();
+ while (it != buffer.end()) {
+ int cmd = *(it++) % 11;
+ int peer, txidnum, delaynum;
+ switch (cmd) {
+ case 0: // Make time jump to the next event (m_time of CANDIDATE or REQUESTED)
+ tester.AdvanceToEvent();
+ break;
+ case 1: // Change time
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.AdvanceTime(DELAYS[delaynum]);
+ break;
+ case 2: // Query for requestable txs
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ tester.GetRequestable(peer);
+ break;
+ case 3: // Peer went offline
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ tester.DisconnectedPeer(peer);
+ break;
+ case 4: // No longer need tx
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ForgetTxHash(txidnum % MAX_TXHASHES);
+ break;
+ case 5: // Received immediate preferred inv
+ case 6: // Same, but non-preferred.
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
+ std::chrono::microseconds::min());
+ break;
+ case 7: // Received delayed preferred inv
+ case 8: // Same, but non-preferred.
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
+ tester.Now() + DELAYS[delaynum]);
+ break;
+ case 9: // Requested tx from peer
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.RequestedTx(peer, txidnum % MAX_TXHASHES, tester.Now() + DELAYS[delaynum]);
+ break;
+ case 10: // Received response
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedResponse(peer, txidnum % MAX_TXHASHES);
+ break;
+ default:
+ assert(false);
+ }
+ }
+ tester.Check();
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 9f9552edb9..ed6093a8a8 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -257,7 +257,7 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
- return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()};
+ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
}
void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index d465ee6759..611e9f2623 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(key_io_invalid)
std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key
- for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
+ for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST }) {
SelectParams(chain);
destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 4e4c44266a..3362b8d17c 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -264,4 +264,32 @@ BOOST_AUTO_TEST_CASE(pubkey_unserialize)
}
}
+BOOST_AUTO_TEST_CASE(bip340_test_vectors)
+{
+ static const std::vector<std::pair<std::array<std::string, 3>, bool>> VECTORS = {
+ {{"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}, true},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}, true},
+ {{"DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}, true},
+ {{"25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}, true},
+ {{"D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"}, true},
+ {{"EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"}, false},
+ {{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}
+ };
+
+ for (const auto& test : VECTORS) {
+ auto pubkey = ParseHex(test.first[0]);
+ auto msg = ParseHex(test.first[1]);
+ auto sig = ParseHex(test.first[2]);
+ BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
deleted file mode 100644
index ea18debbd3..0000000000
--- a/src/test/limitedmap_tests.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2012-2019 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <limitedmap.h>
-
-#include <test/util/setup_common.h>
-
-#include <boost/test/unit_test.hpp>
-
-BOOST_FIXTURE_TEST_SUITE(limitedmap_tests, BasicTestingSetup)
-
-BOOST_AUTO_TEST_CASE(limitedmap_test)
-{
- // create a limitedmap capped at 10 items
- limitedmap<int, int> map(10);
-
- // check that the max size is 10
- BOOST_CHECK(map.max_size() == 10);
-
- // check that it's empty
- BOOST_CHECK(map.size() == 0);
-
- // insert (-1, -1)
- map.insert(std::pair<int, int>(-1, -1));
-
- // make sure that the size is updated
- BOOST_CHECK(map.size() == 1);
-
- // make sure that the new item is in the map
- BOOST_CHECK(map.count(-1) == 1);
-
- // insert 10 new items
- for (int i = 0; i < 10; i++) {
- map.insert(std::pair<int, int>(i, i + 1));
- }
-
- // make sure that the map now contains 10 items...
- BOOST_CHECK(map.size() == 10);
-
- // ...and that the first item has been discarded
- BOOST_CHECK(map.count(-1) == 0);
-
- // iterate over the map, both with an index and an iterator
- limitedmap<int, int>::const_iterator it = map.begin();
- for (int i = 0; i < 10; i++) {
- // make sure the item is present
- BOOST_CHECK(map.count(i) == 1);
-
- // use the iterator to check for the expected key and value
- BOOST_CHECK(it->first == i);
- BOOST_CHECK(it->second == i + 1);
-
- // use find to check for the value
- BOOST_CHECK(map.find(i)->second == i + 1);
-
- // update and recheck
- map.update(it, i + 2);
- BOOST_CHECK(map.find(i)->second == i + 2);
-
- it++;
- }
-
- // check that we've exhausted the iterator
- BOOST_CHECK(it == map.end());
-
- // resize the map to 5 items
- map.max_size(5);
-
- // check that the max size and size are now 5
- BOOST_CHECK(map.max_size() == 5);
- BOOST_CHECK(map.size() == 5);
-
- // check that items less than 5 have been discarded
- // and items greater than 5 are retained
- for (int i = 0; i < 10; i++) {
- if (i < 5) {
- BOOST_CHECK(map.count(i) == 0);
- } else {
- BOOST_CHECK(map.count(i) == 1);
- }
- }
-
- // erase some items not in the map
- for (int i = 100; i < 1000; i += 100) {
- map.erase(i);
- }
-
- // check that the size is unaffected
- BOOST_CHECK(map.size() == 5);
-
- // erase the remaining elements
- for (int i = 5; i < 10; i++) {
- map.erase(i);
- }
-
- // check that the map is now empty
- BOOST_CHECK(map.empty());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 62a0dc4241..3de79a9f45 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -36,17 +36,6 @@ struct MinerTestingSetup : public TestingSetup {
BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
-// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
-class HasReason {
-public:
- explicit HasReason(const std::string& reason) : m_reason(reason) {}
- bool operator() (const std::runtime_error& e) const {
- return std::string(e.what()).find(m_reason) != std::string::npos;
- };
-private:
- const std::string m_reason;
-};
-
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
@@ -209,7 +198,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
// Note that by default, these tests run with size accounting enabled.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index ab42be21bd..37eca8b7ef 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -10,14 +10,18 @@
#include <net.h>
#include <netbase.h>
#include <serialize.h>
+#include <span.h>
#include <streams.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
+#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <version.h>
#include <boost/test/unit_test.hpp>
+#include <ios>
#include <memory>
#include <string>
@@ -180,17 +184,461 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
- bool fInboundIn = false;
- // Test that fFeeler is false by default.
- std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn);
- BOOST_CHECK(pnode1->fInbound == false);
- BOOST_CHECK(pnode1->fFeeler == false);
+ std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 0,
+ /* nLocalHostNonceIn = */ 0,
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
+ BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
+ BOOST_CHECK(pnode1->IsManualConn() == false);
+ BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode1->IsFeelerConn() == false);
+ BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode1->IsInboundConn() == false);
+ BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 1,
+ /* nLocalHostNonceIn = */ 1,
+ CAddress(), pszDest, ConnectionType::INBOUND,
+ /* inbound_onion = */ false);
+ BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
+ BOOST_CHECK(pnode2->IsManualConn() == false);
+ BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode2->IsFeelerConn() == false);
+ BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode2->IsInboundConn() == true);
+ BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 0,
+ /* nLocalHostNonceIn = */ 0,
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY,
+ /* inbound_onion = */ true);
+ BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
+ BOOST_CHECK(pnode3->IsManualConn() == false);
+ BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode3->IsFeelerConn() == false);
+ BOOST_CHECK(pnode3->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode3->IsInboundConn() == false);
+ BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 1,
+ /* nLocalHostNonceIn = */ 1,
+ CAddress(), pszDest, ConnectionType::INBOUND,
+ /* inbound_onion = */ true);
+ BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
+ BOOST_CHECK(pnode4->IsManualConn() == false);
+ BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode4->IsFeelerConn() == false);
+ BOOST_CHECK(pnode4->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode4->IsInboundConn() == true);
+ BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION);
+}
+
+BOOST_AUTO_TEST_CASE(cnetaddr_basic)
+{
+ CNetAddr addr;
+
+ // IPv4, INADDR_ANY
+ BOOST_REQUIRE(LookupHost("0.0.0.0", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0");
+
+ // IPv4, INADDR_NONE
+ BOOST_REQUIRE(LookupHost("255.255.255.255", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255");
+
+ // IPv4, casual
+ BOOST_REQUIRE(LookupHost("12.34.56.78", addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv4());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78");
+
+ // IPv6, in6addr_any
+ BOOST_REQUIRE(LookupHost("::", addr, false));
+ BOOST_REQUIRE(!addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+
+ BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "::");
+
+ // IPv6, casual
+ BOOST_REQUIRE(LookupHost("1122:3344:5566:7788:9900:aabb:ccdd:eeff", addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
+
+ // 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.
+ 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.
+ // 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());
+ BOOST_REQUIRE(addr.IsIPv6());
+ 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.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+
+ // TORv3
+ const char* torv3_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";
+ BOOST_REQUIRE(addr.SetSpecial(torv3_addr));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsTor());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
+
+ // TORv3, broken, with wrong checksum
+ BOOST_CHECK(!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.onion"));
+
+ // TORv3, broken, with wrong version
+ BOOST_CHECK(!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscrye.onion"));
+
+ // TORv3, malicious
+ BOOST_CHECK(!addr.SetSpecial(std::string{
+ "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd\0wtf.onion", 66}));
+
+ // TOR, bogus length
+ BOOST_CHECK(!addr.SetSpecial(std::string{"mfrggzak.onion"}));
+
+ // TOR, invalid base32
+ BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
+
+ // Internal
+ addr.SetInternal("esffpp");
+ BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid
+ BOOST_REQUIRE(addr.IsInternal());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");
+
+ // Totally bogus
+ BOOST_CHECK(!addr.SetSpecial("totally bogus"));
+}
- fInboundIn = true;
- std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn);
- BOOST_CHECK(pnode2->fInbound == true);
- BOOST_CHECK(pnode2->fFeeler == false);
+BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v1)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000000000000000");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1.2.3.4", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000ffff01020304");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "1a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "fd87d87eeb43f1f2f3f4f5f6f7f8f9fa");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000000000000000");
+ s.clear();
+
+ addr.SetInternal("a");
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "fd6b88c08724ca978112ca1bbdcafac2");
+ s.clear();
+}
+
+BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v2)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr
+ // serialize method produces an address in v2 format.
+ s.SetVersion(s.GetVersion() | ADDRV2_FORMAT);
+
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "021000000000000000000000000000000000");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1.2.3.4", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "010401020304");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "030af1f2f3f4f5f6f7f8f9fa");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetInternal("a"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "0210fd6b88c08724ca978112ca1bbdcafac2");
+ s.clear();
+}
+
+BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr
+ // unserialize method expects an address in v2 format.
+ s.SetVersion(s.GetVersion() | ADDRV2_FORMAT);
+
+ // Valid IPv4.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "04" // address length
+ "01020304")); // address
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsIPv4());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "1.2.3.4");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv4, valid length but address itself is shorter.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "04" // address length
+ "0102")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("end of data"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv4, with bogus length.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "05" // address length
+ "01020304")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 IPv4 address with length 5 (should be 4)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv4, with extreme length.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "fd0102" // address length (513 as CompactSize)
+ "01020304")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("Address too long: 513 > 512"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid IPv6.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "0102030405060708090a0b0c0d0e0f10")); // address
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsIPv6());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "102:304:506:708:90a:b0c:d0e:f10");
+ BOOST_REQUIRE(s.empty());
+
+ // Valid IPv6, contains embedded "internal".
+ s << MakeSpan(ParseHex(
+ "02" // network type (IPv6)
+ "10" // address length
+ "fd6b88c08724ca978112ca1bbdcafac2")); // address: 0xfd + sha256("bitcoin")[0:5] +
+ // sha256(name)[0:10]
+ s >> addr;
+ BOOST_CHECK(addr.IsInternal());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "zklycewkdo64v6wc.internal");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv6, with bogus length.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "04" // address length
+ "00")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 IPv6 address with length 4 (should be 16)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv6, contains embedded IPv4.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "00000000000000000000ffff01020304")); // address
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv6, contains embedded TORv2.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "fd87d87eeb430102030405060708090a")); // address
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Valid TORv2.
+ 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_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
+ "79bcc625184b05194975c28b66b66b04" // address
+ "69f7f6556fb1ac3189a79b40dda32f1f"
+ ));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsTor());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(),
+ "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid TORv3, with bogus length.
+ s << MakeSpan(ParseHex("04" // network type (TORv3)
+ "00" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 TORv3 address with length 0 (should be 32)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid I2P.
+ s << MakeSpan(ParseHex("05" // network type (I2P)
+ "20" // address length
+ "a2894dabaec08c0051a481a6dac88b64" // address
+ "f98232ae42d4b6fd2fa81952dfe36a87"));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsI2P());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(),
+ "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid I2P, with bogus length.
+ s << MakeSpan(ParseHex("05" // network type (I2P)
+ "03" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 I2P address with length 3 (should be 32)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid CJDNS.
+ s << MakeSpan(ParseHex("06" // network type (CJDNS)
+ "10" // address length
+ "fc000001000200030004000500060007" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsCJDNS());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
+ BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid CJDNS, with bogus length.
+ s << MakeSpan(ParseHex("06" // network type (CJDNS)
+ "01" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 CJDNS address with length 1 (should be 16)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Unknown, with extreme length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "fe00000002" // address length (CompactSize's MAX_SIZE)
+ "01020304050607" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("Address too long: 33554432 > 512"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Unknown, with reasonable length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "04" // address length
+ "01020304" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Unknown, with zero length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "00" // address length
+ "" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
}
// prior to PR #14728, this test triggers an undefined behavior
@@ -214,7 +662,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
in_addr ipv4AddrPeer;
ipv4AddrPeer.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
- std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, false);
+ std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY);
pnode->fSuccessfullyConnected.store(true);
// the peer claims to be reaching us via IPv6
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 49073ea657..f5d26fafef 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -4,9 +4,13 @@
#include <net_permissions.h>
#include <netbase.h>
+#include <protocol.h>
+#include <serialize.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/translation.h>
+#include <version.h>
#include <string>
@@ -185,6 +189,7 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(!ResolveSubNet("1.2.3.0/-1").IsValid());
BOOST_CHECK(ResolveSubNet("1.2.3.0/32").IsValid());
BOOST_CHECK(!ResolveSubNet("1.2.3.0/33").IsValid());
+ BOOST_CHECK(!ResolveSubNet("1.2.3.0/300").IsValid());
BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/0").IsValid());
BOOST_CHECK(ResolveSubNet("1:2:3:4:5:6:7:8/33").IsValid());
BOOST_CHECK(!ResolveSubNet("1:2:3:4:5:6:7:8/-1").IsValid());
@@ -216,6 +221,11 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:8")));
BOOST_CHECK(!CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).Match(ResolveIP("1:2:3:4:5:6:7:9")));
BOOST_CHECK(CSubNet(ResolveIP("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128");
+ // IPv4 address with IPv6 netmask or the other way around.
+ BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid());
+ BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid());
+ // Can't subnet TOR (or any other non-IPv4 and non-IPv6 network).
+ BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid());
subnet = ResolveSubNet("1.2.3.4/255.255.255.255");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
@@ -290,11 +300,13 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16");
subnet = ResolveSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000");
BOOST_CHECK_EQUAL(subnet.ToString(), "::/0");
+ // Invalid netmasks (with 1-bits after 0-bits)
subnet = ResolveSubNet("1.2.3.4/255.255.232.0");
- BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0");
+ BOOST_CHECK(!subnet.IsValid());
+ subnet = ResolveSubNet("1.2.3.4/255.0.255.255");
+ BOOST_CHECK(!subnet.IsValid());
subnet = ResolveSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
- BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f");
-
+ BOOST_CHECK(!subnet.IsValid());
}
BOOST_AUTO_TEST_CASE(netbase_getgroup)
@@ -428,10 +440,112 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret));
- BOOST_CHECK(LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
+ // We only do subnetting for IPv4 and IPv6
+ BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
}
+// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
+// try a few edge cases for port, service flags and time.
+
+static const std::vector<CAddress> fixture_addresses({
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0 /* port */),
+ NODE_NONE,
+ 0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */
+ ),
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0x00f1 /* port */),
+ NODE_NETWORK,
+ 0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */
+ ),
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0xf1f2 /* port */),
+ static_cast<ServiceFlags>(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED),
+ 0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */
+ )
+});
+
+// fixture_addresses should equal to this when serialized in V1 format.
+// When this is unserialized from V1 format it should equal to fixture_addresses.
+static constexpr const char* stream_addrv1_hex =
+ "03" // number of entries
+
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "0000000000000000" // service flags, NODE_NONE
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
+ "0000" // port
+
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "0100000000000000" // service flags, NODE_NETWORK
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "00f1" // port
+
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "4804000000000000" // service flags, NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "f1f2"; // port
+
+// fixture_addresses should equal to this when serialized in V2 format.
+// When this is unserialized from V2 format it should equal to fixture_addresses.
+static constexpr const char* stream_addrv2_hex =
+ "03" // number of entries
+
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "00" // service flags, COMPACTSIZE(NODE_NONE)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "0000" // port
+
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "01" // service flags, COMPACTSIZE(NODE_NETWORK)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "00f1" // port
+
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "fd4804" // service flags, COMPACTSIZE(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "f1f2"; // port
+
+BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
+{
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+
+ s << fixture_addresses;
+ BOOST_CHECK_EQUAL(HexStr(s), stream_addrv1_hex);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
+{
+ CDataStream s(ParseHex(stream_addrv1_hex), SER_NETWORK, PROTOCOL_VERSION);
+ std::vector<CAddress> addresses_unserialized;
+
+ s >> addresses_unserialized;
+ BOOST_CHECK(fixture_addresses == addresses_unserialized);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
+{
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+
+ s << fixture_addresses;
+ BOOST_CHECK_EQUAL(HexStr(s), stream_addrv2_hex);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
+{
+ CDataStream s(ParseHex(stream_addrv2_hex), SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ std::vector<CAddress> addresses_unserialized;
+
+ s >> addresses_unserialized;
+ BOOST_CHECK(fixture_addresses == addresses_unserialized);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 0f9872f434..1d7f4861fb 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -14,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup)
/* Test calculation of next difficulty target with no constraints applying */
BOOST_AUTO_TEST_CASE(get_next_work)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1261130161; // Block #30240
CBlockIndex pindexLast;
pindexLast.nHeight = 32255;
@@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(get_next_work)
/* Test the constraint on the upper bound for next work */
BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1231006505; // Block #0
CBlockIndex pindexLast;
pindexLast.nHeight = 2015;
@@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
/* Test the constraint on the lower bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1279008237; // Block #66528
CBlockIndex pindexLast;
pindexLast.nHeight = 68543;
@@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
/* Test the constraint on the upper bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time
CBlockIndex pindexLast;
pindexLast.nHeight = 46367;
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
nBits = UintToArith256(consensus.powLimit).GetCompact(true);
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits = ~0x00800000;
hash.SetHex("0x1");
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 nBits_arith = UintToArith256(consensus.powLimit);
@@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith = UintToArith256(consensus.powLimit);
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith{0};
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
std::vector<CBlockIndex> blocks(10000);
for (int i = 0; i < 10000; i++) {
blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
@@ -135,4 +135,51 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
}
}
+void sanity_check_chainparams(const ArgsManager& args, std::string chainName)
+{
+ const auto chainParams = CreateChainParams(args, chainName);
+ const auto consensus = chainParams->GetConsensus();
+
+ // hash genesis is correct
+ BOOST_CHECK_EQUAL(consensus.hashGenesisBlock, chainParams->GenesisBlock().GetHash());
+
+ // target timespan is an even multiple of spacing
+ BOOST_CHECK_EQUAL(consensus.nPowTargetTimespan % consensus.nPowTargetSpacing, 0);
+
+ // genesis nBits is positive, doesn't overflow and is lower than powLimit
+ arith_uint256 pow_compact;
+ bool neg, over;
+ pow_compact.SetCompact(chainParams->GenesisBlock().nBits, &neg, &over);
+ BOOST_CHECK(!neg && pow_compact != 0);
+ BOOST_CHECK(!over);
+ BOOST_CHECK(UintToArith256(consensus.powLimit) >= pow_compact);
+
+ // check max target * 4*nPowTargetTimespan doesn't overflow -- see pow.cpp:CalculateNextWorkRequired()
+ if (!consensus.fPowNoRetargeting) {
+ arith_uint256 targ_max("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+ targ_max /= consensus.nPowTargetTimespan*4;
+ BOOST_CHECK(UintToArith256(consensus.powLimit) < targ_max);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(ChainParams_MAIN_sanity)
+{
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::MAIN);
+}
+
+BOOST_AUTO_TEST_CASE(ChainParams_REGTEST_sanity)
+{
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::REGTEST);
+}
+
+BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity)
+{
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::TESTNET);
+}
+
+BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity)
+{
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::SIGNET);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 87678af4d1..1d6bcadf69 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -349,21 +349,16 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
result = GetScriptForMultisig(2, std::vector<CPubKey>(pubkeys, pubkeys + 3));
BOOST_CHECK(result == expected);
- // GetScriptForWitness
- CScript witnessScript;
-
- witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
+ // WitnessV0KeyHash
expected.clear();
expected << OP_0 << ToByteVector(pubkeys[0].GetID());
- result = GetScriptForWitness(witnessScript);
+ result = GetScriptForDestination(WitnessV0KeyHash(Hash160(ToByteVector(pubkeys[0]))));
BOOST_CHECK(result == expected);
-
- witnessScript.clear();
- witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- result = GetScriptForWitness(witnessScript);
+ result = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID()));
BOOST_CHECK(result == expected);
- witnessScript.clear();
+ // WitnessV0ScriptHash (multisig)
+ CScript witnessScript;
witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG;
uint256 scriptHash;
@@ -372,7 +367,7 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
expected.clear();
expected << OP_0 << ToByteVector(scriptHash);
- result = GetScriptForWitness(witnessScript);
+ result = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
BOOST_CHECK(result == expected);
}
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 0830743d61..7c53bd0002 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -5,10 +5,12 @@
#include <test/data/script_tests.json.h>
#include <core_io.h>
+#include <fs.h>
#include <key.h>
#include <rpc/util.h>
#include <script/script.h>
#include <script/script_error.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <streams.h>
@@ -1339,14 +1341,12 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
}
-static CScript
-ScriptFromHex(const char* hex)
+static CScript ScriptFromHex(const std::string& str)
{
- std::vector<unsigned char> data = ParseHex(hex);
+ std::vector<unsigned char> data = ParseHex(str);
return CScript(data.begin(), data.end());
}
-
BOOST_AUTO_TEST_CASE(script_FindAndDelete)
{
// Exercise the FindAndDelete functionality
@@ -1472,6 +1472,36 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
#if defined(HAVE_CONSENSUS_LIB)
+static CMutableTransaction TxFromHex(const std::string& str)
+{
+ CMutableTransaction tx;
+ VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
+ return tx;
+}
+
+static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ std::vector<CTxOut> prevouts;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ CTxOut txout;
+ VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout;
+ prevouts.push_back(std::move(txout));
+ }
+ return prevouts;
+}
+
+static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ CScriptWitness scriptwitness;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ auto bytes = ParseHex(univalue[i].get_str());
+ scriptwitness.stack.push_back(std::move(bytes));
+ }
+ return scriptwitness;
+}
+
/* Test simple (successful) usage of bitcoinconsensus_verify_script */
BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
{
@@ -1610,5 +1640,107 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
+static std::vector<unsigned int> AllConsensusFlags()
+{
+ std::vector<unsigned int> ret;
+
+ for (unsigned int i = 0; i < 128; ++i) {
+ unsigned int flag = 0;
+ if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
+ if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
+ if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
+ if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
+ if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
+
+ // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
+ if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
+ // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
+ if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
+
+ ret.push_back(flag);
+ }
+
+ return ret;
+}
+
+/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
+static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
+
+static void AssetTest(const UniValue& test)
+{
+ BOOST_CHECK(test.isObject());
+
+ CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
+ const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
+ BOOST_CHECK(prevouts.size() == mtx.vin.size());
+ size_t idx = test["index"].get_int64();
+ unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
+ bool fin = test.exists("final") && test["final"].get_bool();
+
+ if (test.exists("success")) {
+ mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
+ mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
+ CTransaction tx(mtx);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ for (const auto flags : ALL_CONSENSUS_FLAGS) {
+ // "final": true tests are valid for all flags. Others are only valid with flags that are
+ // a subset of test_flags.
+ if (fin || ((flags & test_flags) == flags)) {
+ bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ BOOST_CHECK(ret);
+ }
+ }
+ }
+
+ if (test.exists("failure")) {
+ mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
+ mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
+ CTransaction tx(mtx);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ for (const auto flags : ALL_CONSENSUS_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) {
+ bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ BOOST_CHECK(!ret);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(script_assets_test)
+{
+ // See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
+ // the script_assets_test.json file used by this test.
+
+ const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
+ BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
+ if (dir == nullptr) return;
+ auto path = fs::path(dir) / "script_assets_test.json";
+ bool exists = fs::exists(path);
+ BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
+ if (!exists) return;
+ fs::ifstream file(path);
+ BOOST_CHECK(file.is_open());
+ file.seekg(0, std::ios::end);
+ size_t length = file.tellg();
+ file.seekg(0, std::ios::beg);
+ std::string data(length, '\0');
+ file.read(&data[0], data.size());
+ UniValue tests = read_json(data);
+ BOOST_CHECK(tests.isArray());
+ BOOST_CHECK(tests.size() > 0);
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ AssetTest(tests[i]);
+ }
+ file.close();
+}
+
#endif
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index c0bb92258b..bc862de78a 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -28,7 +28,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
{
if (nIn >= txTo.vin.size())
{
- return UINT256_ONE();
+ return uint256::ONE;
}
CMutableTransaction txTmp(txTo);
@@ -58,7 +58,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
unsigned int nOut = nIn;
if (nOut >= txTmp.vout.size())
{
- return UINT256_ONE();
+ return uint256::ONE;
}
txTmp.vout.resize(nOut+1);
for (unsigned int i = 0; i < nOut; i++)
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 6e36bce7a1..7e5274450d 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -154,8 +154,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// P2WPKH witness program
{
- CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
- CScript scriptPubKey = GetScriptForWitness(p2pk);
+ CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));
CScript scriptSig = CScript();
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
@@ -183,8 +182,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// P2WPKH nested in P2SH
{
- CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
- CScript scriptSig = GetScriptForWitness(p2pk);
+ CScript scriptSig = GetScriptForDestination(WitnessV0KeyHash(pubkey));
CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig));
scriptSig = CScript() << ToByteVector(scriptSig);
CScriptWitness scriptWitness;
@@ -199,7 +197,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// P2WSH witness program
{
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
- CScript scriptPubKey = GetScriptForWitness(witnessScript);
+ CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
CScript scriptSig = CScript();
CScriptWitness scriptWitness;
scriptWitness.stack.push_back(std::vector<unsigned char>(0));
@@ -215,7 +213,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// P2WSH nested in P2SH
{
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
- CScript redeemScript = GetScriptForWitness(witnessScript);
+ CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << ToByteVector(redeemScript);
CScriptWitness scriptWitness;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 4bf6e734ce..b7ee280336 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -57,6 +57,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
+ {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
};
unsigned int ParseScriptFlags(std::string strFlags)
@@ -361,6 +362,8 @@ static CScript PushAll(const std::vector<valtype>& values)
result << OP_0;
} else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) {
result << CScript::EncodeOP_N(v[0]);
+ } else if (v.size() == 1 && v[0] == 0x81) {
+ result << OP_1NEGATE;
} else {
result << v;
}
@@ -499,13 +502,19 @@ BOOST_AUTO_TEST_CASE(test_witness)
BOOST_CHECK(keystore.AddCScript(scriptPubkey1L));
BOOST_CHECK(keystore.AddCScript(scriptPubkey2L));
BOOST_CHECK(keystore.AddCScript(scriptMulti));
- BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey1)));
- BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey2)));
- BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey1L)));
- BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptPubkey2L)));
- BOOST_CHECK(keystore.AddCScript(GetScriptForWitness(scriptMulti)));
+ CScript destination_script_1, destination_script_2, destination_script_1L, destination_script_2L, destination_script_multi;
+ destination_script_1 = GetScriptForDestination(WitnessV0KeyHash(pubkey1));
+ destination_script_2 = GetScriptForDestination(WitnessV0KeyHash(pubkey2));
+ destination_script_1L = GetScriptForDestination(WitnessV0KeyHash(pubkey1L));
+ destination_script_2L = GetScriptForDestination(WitnessV0KeyHash(pubkey2L));
+ destination_script_multi = GetScriptForDestination(WitnessV0ScriptHash(scriptMulti));
+ BOOST_CHECK(keystore.AddCScript(destination_script_1));
+ BOOST_CHECK(keystore.AddCScript(destination_script_2));
+ BOOST_CHECK(keystore.AddCScript(destination_script_1L));
+ BOOST_CHECK(keystore.AddCScript(destination_script_2L));
+ BOOST_CHECK(keystore.AddCScript(destination_script_multi));
BOOST_CHECK(keystore2.AddCScript(scriptMulti));
- BOOST_CHECK(keystore2.AddCScript(GetScriptForWitness(scriptMulti)));
+ BOOST_CHECK(keystore2.AddCScript(destination_script_multi));
BOOST_CHECK(keystore2.AddKeyPubKey(key3, pubkey3));
CTransactionRef output1, output2;
@@ -537,8 +546,8 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// Witness pay-to-compressed-pubkey (v0).
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2), output2, input2);
+ CreateCreditAndSpend(keystore, destination_script_1, output1, input1);
+ CreateCreditAndSpend(keystore, destination_script_2, output2, input2);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
@@ -549,9 +558,9 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// P2SH witness pay-to-compressed-pubkey (v0).
- CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1))), output1, input1);
- CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2))), output2, input2);
- ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1));
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_1)), output1, input1);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_2)), output2, input2);
+ ReplaceRedeemScript(input2.vin[0].scriptSig, destination_script_1);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
@@ -587,12 +596,12 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
// Signing disabled for witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1, false);
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false);
+ CreateCreditAndSpend(keystore, destination_script_1L, output1, input1, false);
+ CreateCreditAndSpend(keystore, destination_script_2L, output2, input2, false);
// Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1).
- CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1L))), output1, input1, false);
- CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2L))), output2, input2, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_1L)), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_2L)), output2, input2, false);
// Normal 2-of-2 multisig
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
@@ -616,10 +625,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// Witness 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForWitness(scriptMulti), output1, input1, false);
+ CreateCreditAndSpend(keystore, destination_script_multi, output1, input1, false);
CheckWithFlag(output1, input1, 0, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- CreateCreditAndSpend(keystore2, GetScriptForWitness(scriptMulti), output2, input2, false);
+ CreateCreditAndSpend(keystore2, destination_script_multi, output2, input2, false);
CheckWithFlag(output2, input2, 0, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
@@ -628,10 +637,10 @@ BOOST_AUTO_TEST_CASE(test_witness)
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
// P2SH witness 2-of-2 multisig
- CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output1, input1, false);
+ CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_multi)), output1, input1, false);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
- CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output2, input2, false);
+ CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(destination_script_multi)), output2, input2, false);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
BOOST_CHECK(*output1 == *output2);
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
new file mode 100644
index 0000000000..1d137b03b1
--- /dev/null
+++ b/src/test/txrequest_tests.cpp
@@ -0,0 +1,738 @@
+// 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 <txrequest.h>
+#include <uint256.h>
+
+#include <test/util/setup_common.h>
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(txrequest_tests, BasicTestingSetup)
+
+namespace {
+
+constexpr std::chrono::microseconds MIN_TIME = std::chrono::microseconds::min();
+constexpr std::chrono::microseconds MAX_TIME = std::chrono::microseconds::max();
+constexpr std::chrono::microseconds MICROSECOND = std::chrono::microseconds{1};
+constexpr std::chrono::microseconds NO_TIME = std::chrono::microseconds{0};
+
+/** An Action is a function to call at a particular (simulated) timestamp. */
+using Action = std::pair<std::chrono::microseconds, std::function<void()>>;
+
+/** Object that stores actions from multiple interleaved scenarios, and data shared across them.
+ *
+ * The Scenario below is used to fill this.
+ */
+struct Runner
+{
+ /** The TxRequestTracker being tested. */
+ TxRequestTracker txrequest;
+
+ /** List of actions to be executed (in order of increasing timestamp). */
+ std::vector<Action> actions;
+
+ /** Which node ids have been assigned already (to prevent reuse). */
+ std::set<NodeId> peerset;
+
+ /** Which txhashes have been assigned already (to prevent reuse). */
+ std::set<uint256> txhashset;
+
+ /** Which (peer, gtxid) combinations are known to be expired. These need to be accumulated here instead of
+ * checked directly in the GetRequestable return value to avoid introducing a dependency between the various
+ * parallel tests. */
+ std::multiset<std::pair<NodeId, GenTxid>> expired;
+};
+
+std::chrono::microseconds RandomTime8s() { return std::chrono::microseconds{1 + InsecureRandBits(23)}; }
+std::chrono::microseconds RandomTime1y() { return std::chrono::microseconds{1 + InsecureRandBits(45)}; }
+
+/** A proxy for a Runner that helps build a sequence of consecutive test actions on a TxRequestTracker.
+ *
+ * Each Scenario is a proxy through which actions for the (sequential) execution of various tests are added to a
+ * Runner. The actions from multiple scenarios are then run concurrently, resulting in these tests being performed
+ * against a TxRequestTracker in parallel. Every test has its own unique txhashes and NodeIds which are not
+ * reused in other tests, and thus they should be independent from each other. Running them in parallel however
+ * means that we verify the behavior (w.r.t. one test's txhashes and NodeIds) even when the state of the data
+ * structure is more complicated due to the presence of other tests.
+ */
+class Scenario
+{
+ Runner& m_runner;
+ std::chrono::microseconds m_now;
+ std::string m_testname;
+
+public:
+ Scenario(Runner& runner, std::chrono::microseconds starttime) : m_runner(runner), m_now(starttime) {}
+
+ /** Set a name for the current test, to give more clear error messages. */
+ void SetTestName(std::string testname)
+ {
+ m_testname = std::move(testname);
+ }
+
+ /** Advance this Scenario's time; this affects the timestamps newly scheduled events get. */
+ void AdvanceTime(std::chrono::microseconds amount)
+ {
+ assert(amount.count() >= 0);
+ m_now += amount;
+ }
+
+ /** Schedule a ForgetTxHash call at the Scheduler's current time. */
+ void ForgetTxHash(const uint256& txhash)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ForgetTxHash(txhash);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a ReceivedInv call at the Scheduler's current time. */
+ void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool pref, std::chrono::microseconds reqtime)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ReceivedInv(peer, gtxid, pref, reqtime);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a DisconnectedPeer call at the Scheduler's current time. */
+ void DisconnectedPeer(NodeId peer)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.DisconnectedPeer(peer);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a RequestedTx call at the Scheduler's current time. */
+ void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds exptime)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.RequestedTx(peer, txhash, exptime);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a ReceivedResponse call at the Scheduler's current time. */
+ void ReceivedResponse(NodeId peer, const uint256& txhash)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ReceivedResponse(peer, txhash);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule calls to verify the TxRequestTracker's state at the Scheduler's current time.
+ *
+ * @param peer The peer whose state will be inspected.
+ * @param expected The expected return value for GetRequestable(peer)
+ * @param candidates The expected return value CountCandidates(peer)
+ * @param inflight The expected return value CountInFlight(peer)
+ * @param completed The expected return value of Count(peer), minus candidates and inflight.
+ * @param checkname An arbitrary string to include in error messages, for test identificatrion.
+ * @param offset Offset with the current time to use (must be <= 0). This allows simulations of time going
+ * backwards (but note that the ordering of this event only follows the scenario's m_now.
+ */
+ void Check(NodeId peer, const std::vector<GenTxid>& expected, size_t candidates, size_t inflight,
+ size_t completed, const std::string& checkname,
+ std::chrono::microseconds offset = std::chrono::microseconds{0})
+ {
+ const auto comment = m_testname + " " + checkname;
+ auto& runner = m_runner;
+ const auto now = m_now;
+ assert(offset.count() <= 0);
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ std::vector<std::pair<NodeId, GenTxid>> expired_now;
+ auto ret = runner.txrequest.GetRequestable(peer, now + offset, &expired_now);
+ for (const auto& entry : expired_now) runner.expired.insert(entry);
+ runner.txrequest.SanityCheck();
+ runner.txrequest.PostGetRequestableSanityCheck(now + offset);
+ size_t total = candidates + inflight + completed;
+ size_t real_total = runner.txrequest.Count(peer);
+ size_t real_candidates = runner.txrequest.CountCandidates(peer);
+ size_t real_inflight = runner.txrequest.CountInFlight(peer);
+ BOOST_CHECK_MESSAGE(real_total == total, strprintf("[" + comment + "] total %i (%i expected)", real_total, total));
+ BOOST_CHECK_MESSAGE(real_inflight == inflight, strprintf("[" + comment + "] inflight %i (%i expected)", real_inflight, inflight));
+ BOOST_CHECK_MESSAGE(real_candidates == candidates, strprintf("[" + comment + "] candidates %i (%i expected)", real_candidates, candidates));
+ BOOST_CHECK_MESSAGE(ret == expected, "[" + comment + "] mismatching requestables");
+ });
+ }
+
+ /** Verify that an announcement for gtxid by peer has expired some time before this check is scheduled.
+ *
+ * Every expected expiration should be accounted for through exactly one call to this function.
+ */
+ void CheckExpired(NodeId peer, GenTxid gtxid)
+ {
+ const auto& testname = m_testname;
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ auto it = runner.expired.find(std::pair<NodeId, GenTxid>{peer, gtxid});
+ BOOST_CHECK_MESSAGE(it != runner.expired.end(), "[" + testname + "] missing expiration");
+ if (it != runner.expired.end()) runner.expired.erase(it);
+ });
+ }
+
+ /** Generate a random txhash, whose priorities for certain peers are constrained.
+ *
+ * For example, NewTxHash({{p1,p2,p3},{p2,p4,p5}}) will generate a txhash T such that both:
+ * - priority(p1,T) > priority(p2,T) > priority(p3,T)
+ * - priority(p2,T) > priority(p4,T) > priority(p5,T)
+ * where priority is the predicted internal TxRequestTracker's priority, assuming all announcements
+ * are within the same preferredness class.
+ */
+ uint256 NewTxHash(const std::vector<std::vector<NodeId>>& orders = {})
+ {
+ uint256 ret;
+ bool ok;
+ do {
+ ret = InsecureRand256();
+ ok = true;
+ for (const auto& order : orders) {
+ for (size_t pos = 1; pos < order.size(); ++pos) {
+ uint64_t prio_prev = m_runner.txrequest.ComputePriority(ret, order[pos - 1], true);
+ uint64_t prio_cur = m_runner.txrequest.ComputePriority(ret, order[pos], true);
+ if (prio_prev <= prio_cur) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) break;
+ }
+ if (ok) {
+ ok = m_runner.txhashset.insert(ret).second;
+ }
+ } while(!ok);
+ return ret;
+ }
+
+ /** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
+ GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
+ {
+ return {InsecureRandBool(), NewTxHash(orders)};
+ }
+
+ /** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
+ * (across all Scenarios combined). */
+ NodeId NewPeer()
+ {
+ bool ok;
+ NodeId ret;
+ do {
+ ret = InsecureRandBits(63);
+ ok = m_runner.peerset.insert(ret).second;
+ } while(!ok);
+ return ret;
+ }
+
+ std::chrono::microseconds Now() const { return m_now; }
+};
+
+/** Add to scenario a test with a single tx announced by a single peer.
+ *
+ * config is an integer in [0, 32), which controls which variant of the test is used.
+ */
+void BuildSingleTest(Scenario& scenario, int config)
+{
+ auto peer = scenario.NewPeer();
+ auto gtxid = scenario.NewGTxid();
+ bool immediate = config & 1;
+ bool preferred = config & 2;
+ auto delay = immediate ? NO_TIME : RandomTime8s();
+
+ scenario.SetTestName(strprintf("Single(config=%i)", config));
+
+ // Receive an announcement, either immediately requestable or delayed.
+ scenario.ReceivedInv(peer, gtxid, preferred, immediate ? MIN_TIME : scenario.Now() + delay);
+ if (immediate) {
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s1");
+ } else {
+ scenario.Check(peer, {}, 1, 0, 0, "s2");
+ scenario.AdvanceTime(delay - MICROSECOND);
+ scenario.Check(peer, {}, 1, 0, 0, "s3");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s4");
+ }
+
+ if (config >> 3) { // We'll request the transaction
+ scenario.AdvanceTime(RandomTime8s());
+ auto expiry = RandomTime8s();
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s5");
+ scenario.RequestedTx(peer, gtxid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peer, {}, 0, 1, 0, "s6");
+
+ if ((config >> 3) == 1) { // The request will time out
+ scenario.AdvanceTime(expiry - MICROSECOND);
+ scenario.Check(peer, {}, 0, 1, 0, "s7");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {}, 0, 0, 0, "s8");
+ scenario.CheckExpired(peer, gtxid);
+ return;
+ } else {
+ scenario.AdvanceTime(std::chrono::microseconds{InsecureRandRange(expiry.count())});
+ scenario.Check(peer, {}, 0, 1, 0, "s9");
+ if ((config >> 3) == 3) { // A response will arrive for the transaction
+ scenario.ReceivedResponse(peer, gtxid.GetHash());
+ scenario.Check(peer, {}, 0, 0, 0, "s10");
+ return;
+ }
+ }
+ }
+
+ if (config & 4) { // The peer will go offline
+ scenario.DisconnectedPeer(peer);
+ } else { // The transaction is no longer needed
+ scenario.ForgetTxHash(gtxid.GetHash());
+ }
+ scenario.Check(peer, {}, 0, 0, 0, "s11");
+}
+
+/** Add to scenario a test with a single tx announced by two peers, to verify the
+ * right peer is selected for requests.
+ *
+ * config is an integer in [0, 32), which controls which variant of the test is used.
+ */
+void BuildPriorityTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("Priority(config=%i)", config));
+
+ // Two peers. They will announce in order {peer1, peer2}.
+ auto peer1 = scenario.NewPeer(), peer2 = scenario.NewPeer();
+ // Construct a transaction that under random rules would be preferred by peer2 or peer1,
+ // depending on configuration.
+ bool prio1 = config & 1;
+ auto gtxid = prio1 ? scenario.NewGTxid({{peer1, peer2}}) : scenario.NewGTxid({{peer2, peer1}});
+ bool pref1 = config & 2, pref2 = config & 4;
+
+ scenario.ReceivedInv(peer1, gtxid, pref1, MIN_TIME);
+ scenario.Check(peer1, {gtxid}, 1, 0, 0, "p1");
+ if (InsecureRandBool()) {
+ scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(peer1, {gtxid}, 1, 0, 0, "p2");
+ }
+
+ scenario.ReceivedInv(peer2, gtxid, pref2, MIN_TIME);
+ bool stage2_prio =
+ // At this point, peer2 will be given priority if:
+ // - It is preferred and peer1 is not
+ (pref2 && !pref1) ||
+ // - They're in the same preference class,
+ // and the randomized priority favors peer2 over peer1.
+ (pref1 == pref2 && !prio1);
+ NodeId priopeer = stage2_prio ? peer2 : peer1, otherpeer = stage2_prio ? peer1 : peer2;
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p3");
+ scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p4");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p5");
+ scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p6");
+
+ // We possibly request from the selected peer.
+ if (config & 8) {
+ scenario.RequestedTx(priopeer, gtxid.GetHash(), MAX_TIME);
+ scenario.Check(priopeer, {}, 0, 1, 0, "p7");
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p8");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ }
+
+ // The peer which was selected (or requested from) now goes offline, or a NOTFOUND is received from them.
+ if (config & 16) {
+ scenario.DisconnectedPeer(priopeer);
+ } else {
+ scenario.ReceivedResponse(priopeer, gtxid.GetHash());
+ }
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(priopeer, {}, 0, 0, !(config & 16), "p8");
+ scenario.Check(otherpeer, {gtxid}, 1, 0, 0, "p9");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+
+ // Now the other peer goes offline.
+ scenario.DisconnectedPeer(otherpeer);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(peer1, {}, 0, 0, 0, "p10");
+ scenario.Check(peer2, {}, 0, 0, 0, "p11");
+}
+
+/** Add to scenario a randomized test in which N peers announce the same transaction, to verify
+ * the order in which they are requested. */
+void BuildBigPriorityTest(Scenario& scenario, int peers)
+{
+ scenario.SetTestName(strprintf("BigPriority(peers=%i)", peers));
+
+ // We will have N peers announce the same transaction.
+ std::map<NodeId, bool> preferred;
+ std::vector<NodeId> pref_peers, npref_peers;
+ int num_pref = InsecureRandRange(peers + 1) ; // Some preferred, ...
+ int num_npref = peers - num_pref; // some not preferred.
+ for (int i = 0; i < num_pref; ++i) {
+ pref_peers.push_back(scenario.NewPeer());
+ preferred[pref_peers.back()] = true;
+ }
+ for (int i = 0; i < num_npref; ++i) {
+ npref_peers.push_back(scenario.NewPeer());
+ preferred[npref_peers.back()] = false;
+ }
+ // Make a list of all peers, in order of intended request order (concatenation of pref_peers and npref_peers).
+ std::vector<NodeId> request_order;
+ for (int i = 0; i < num_pref; ++i) request_order.push_back(pref_peers[i]);
+ for (int i = 0; i < num_npref; ++i) request_order.push_back(npref_peers[i]);
+
+ // Determine the announcement order randomly.
+ std::vector<NodeId> announce_order = request_order;
+ Shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
+
+ // Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
+ // within npref_peers.
+ auto gtxid = scenario.NewGTxid({pref_peers, npref_peers});
+
+ // Decide reqtimes in opposite order of the expected request order. This means that as time passes we expect the
+ // to-be-requested-from-peer will change every time a subsequent reqtime is passed.
+ std::map<NodeId, std::chrono::microseconds> reqtimes;
+ auto reqtime = scenario.Now();
+ for (int i = peers - 1; i >= 0; --i) {
+ reqtime += RandomTime8s();
+ reqtimes[request_order[i]] = reqtime;
+ }
+
+ // Actually announce from all peers simultaneously (but in announce_order).
+ for (const auto peer : announce_order) {
+ scenario.ReceivedInv(peer, gtxid, preferred[peer], reqtimes[peer]);
+ }
+ for (const auto peer : announce_order) {
+ scenario.Check(peer, {}, 1, 0, 0, "b1");
+ }
+
+ // Let time pass and observe the to-be-requested-from peer change, from nonpreferred to preferred, and from
+ // high priority to low priority within each class.
+ for (int i = peers - 1; i >= 0; --i) {
+ scenario.AdvanceTime(reqtimes[request_order[i]] - scenario.Now() - MICROSECOND);
+ scenario.Check(request_order[i], {}, 1, 0, 0, "b2");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(request_order[i], {gtxid}, 1, 0, 0, "b3");
+ }
+
+ // Peers now in random order go offline, or send NOTFOUNDs. At every point in time the new to-be-requested-from
+ // peer should be the best remaining one, so verify this after every response.
+ for (int i = 0; i < peers; ++i) {
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ const int pos = InsecureRandRange(request_order.size());
+ const auto peer = request_order[pos];
+ request_order.erase(request_order.begin() + pos);
+ if (InsecureRandBool()) {
+ scenario.DisconnectedPeer(peer);
+ scenario.Check(peer, {}, 0, 0, 0, "b4");
+ } else {
+ scenario.ReceivedResponse(peer, gtxid.GetHash());
+ scenario.Check(peer, {}, 0, 0, request_order.size() > 0, "b5");
+ }
+ if (request_order.size()) {
+ scenario.Check(request_order[0], {gtxid}, 1, 0, 0, "b6");
+ }
+ }
+
+ // Everything is gone in the end.
+ for (const auto peer : announce_order) {
+ scenario.Check(peer, {}, 0, 0, 0, "b7");
+ }
+}
+
+/** Add to scenario a test with one peer announcing two transactions, to verify they are
+ * fetched in announcement order.
+ *
+ * config is an integer in [0, 4) inclusive, and selects the variant of the test.
+ */
+void BuildRequestOrderTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("RequestOrder(config=%i)", config));
+
+ auto peer = scenario.NewPeer();
+ auto gtxid1 = scenario.NewGTxid();
+ auto gtxid2 = scenario.NewGTxid();
+
+ auto reqtime2 = scenario.Now() + RandomTime8s();
+ auto reqtime1 = reqtime2 + RandomTime8s();
+
+ scenario.ReceivedInv(peer, gtxid1, config & 1, reqtime1);
+ // Simulate time going backwards by giving the second announcement an earlier reqtime.
+ scenario.ReceivedInv(peer, gtxid2, config & 2, reqtime2);
+
+ scenario.AdvanceTime(reqtime2 - MICROSECOND - scenario.Now());
+ scenario.Check(peer, {}, 2, 0, 0, "o1");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {gtxid2}, 2, 0, 0, "o2");
+ scenario.AdvanceTime(reqtime1 - MICROSECOND - scenario.Now());
+ scenario.Check(peer, {gtxid2}, 2, 0, 0, "o3");
+ scenario.AdvanceTime(MICROSECOND);
+ // Even with time going backwards in between announcements, the return value of GetRequestable is in
+ // announcement order.
+ scenario.Check(peer, {gtxid1, gtxid2}, 2, 0, 0, "o4");
+
+ scenario.DisconnectedPeer(peer);
+ scenario.Check(peer, {}, 0, 0, 0, "o5");
+}
+
+/** Add to scenario a test that verifies behavior related to both txid and wtxid with the same
+ * hash being announced.
+ *
+ * config is an integer in [0, 4) inclusive, and selects the variant of the test used.
+*/
+void BuildWtxidTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("Wtxid(config=%i)", config));
+
+ auto peerT = scenario.NewPeer();
+ auto peerW = scenario.NewPeer();
+ auto txhash = scenario.NewTxHash();
+ GenTxid txid{false, txhash};
+ GenTxid wtxid{true, txhash};
+
+ auto reqtimeT = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
+ auto reqtimeW = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
+
+ // Announce txid first or wtxid first.
+ if (config & 1) {
+ scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
+ } else {
+ scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
+ }
+
+ // Let time pass if needed, and check that the preferred announcement (txid or wtxid)
+ // is correctly to-be-requested (and with the correct wtxidness).
+ auto max_reqtime = std::max(reqtimeT, reqtimeW);
+ if (max_reqtime > scenario.Now()) scenario.AdvanceTime(max_reqtime - scenario.Now());
+ if (config & 2) {
+ scenario.Check(peerT, {txid}, 1, 0, 0, "w1");
+ scenario.Check(peerW, {}, 1, 0, 0, "w2");
+ } else {
+ scenario.Check(peerT, {}, 1, 0, 0, "w3");
+ scenario.Check(peerW, {wtxid}, 1, 0, 0, "w4");
+ }
+
+ // Let the preferred announcement be requested. It's not going to be delivered.
+ auto expiry = RandomTime8s();
+ if (config & 2) {
+ scenario.RequestedTx(peerT, txid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peerT, {}, 0, 1, 0, "w5");
+ scenario.Check(peerW, {}, 1, 0, 0, "w6");
+ } else {
+ scenario.RequestedTx(peerW, wtxid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peerT, {}, 1, 0, 0, "w7");
+ scenario.Check(peerW, {}, 0, 1, 0, "w8");
+ }
+
+ // After reaching expiration time of the preferred announcement, verify that the
+ // remaining one is requestable
+ scenario.AdvanceTime(expiry);
+ if (config & 2) {
+ scenario.Check(peerT, {}, 0, 0, 1, "w9");
+ scenario.Check(peerW, {wtxid}, 1, 0, 0, "w10");
+ scenario.CheckExpired(peerT, txid);
+ } else {
+ scenario.Check(peerT, {txid}, 1, 0, 0, "w11");
+ scenario.Check(peerW, {}, 0, 0, 1, "w12");
+ scenario.CheckExpired(peerW, wtxid);
+ }
+
+ // If a good transaction with either that hash as wtxid or txid arrives, both
+ // announcements are gone.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ForgetTxHash(txhash);
+ scenario.Check(peerT, {}, 0, 0, 0, "w13");
+ scenario.Check(peerW, {}, 0, 0, 0, "w14");
+}
+
+/** Add to scenario a test that exercises clocks that go backwards. */
+void BuildTimeBackwardsTest(Scenario& scenario)
+{
+ auto peer1 = scenario.NewPeer();
+ auto peer2 = scenario.NewPeer();
+ auto gtxid = scenario.NewGTxid({{peer1, peer2}});
+
+ // Announce from peer2.
+ auto reqtime = scenario.Now() + RandomTime8s();
+ scenario.ReceivedInv(peer2, gtxid, true, reqtime);
+ scenario.Check(peer2, {}, 1, 0, 0, "r1");
+ scenario.AdvanceTime(reqtime - scenario.Now());
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r2");
+ // Check that if the clock goes backwards by 1us, the transaction would stop being requested.
+ scenario.Check(peer2, {}, 1, 0, 0, "r3", -MICROSECOND);
+ // But it reverts to being requested if time goes forward again.
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r4");
+
+ // Announce from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer1, gtxid, true, MAX_TIME);
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r5");
+ scenario.Check(peer1, {}, 1, 0, 0, "r6");
+
+ // Request from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ auto expiry = scenario.Now() + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid.GetHash(), expiry);
+ scenario.Check(peer1, {}, 0, 1, 0, "r7");
+ scenario.Check(peer2, {}, 1, 0, 0, "r8");
+
+ // Expiration passes.
+ scenario.AdvanceTime(expiry - scenario.Now());
+ scenario.Check(peer1, {}, 0, 0, 1, "r9");
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r10"); // Request goes back to peer2.
+ scenario.CheckExpired(peer1, gtxid);
+ scenario.Check(peer1, {}, 0, 0, 1, "r11", -MICROSECOND); // Going back does not unexpire.
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r12", -MICROSECOND);
+
+ // Peer2 goes offline, meaning no viable announcements remain.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.DisconnectedPeer(peer2);
+ scenario.Check(peer1, {}, 0, 0, 0, "r13");
+ scenario.Check(peer2, {}, 0, 0, 0, "r14");
+}
+
+/** Add to scenario a test that involves RequestedTx() calls for txhashes not returned by GetRequestable. */
+void BuildWeirdRequestsTest(Scenario& scenario)
+{
+ auto peer1 = scenario.NewPeer();
+ auto peer2 = scenario.NewPeer();
+ auto gtxid1 = scenario.NewGTxid({{peer1, peer2}});
+ auto gtxid2 = scenario.NewGTxid({{peer2, peer1}});
+
+ // Announce gtxid1 by peer1.
+ scenario.ReceivedInv(peer1, gtxid1, true, MIN_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q1");
+
+ // Announce gtxid2 by peer2.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer2, gtxid2, true, MIN_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q2");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q3");
+
+ // We request gtxid2 from *peer1* - no effect.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q4");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q5");
+
+ // Now request gtxid1 from peer1 - marks it as REQUESTED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ auto expiryA = scenario.Now() + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryA);
+ scenario.Check(peer1, {}, 0, 1, 0, "q6");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q7");
+
+ // Request it a second time - nothing happens, as it's already REQUESTED.
+ auto expiryB = expiryA + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryB);
+ scenario.Check(peer1, {}, 0, 1, 0, "q8");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q9");
+
+ // Also announce gtxid1 from peer2 now, so that the txhash isn't forgotten when the peer1 request expires.
+ scenario.ReceivedInv(peer2, gtxid1, true, MIN_TIME);
+ scenario.Check(peer1, {}, 0, 1, 0, "q10");
+ scenario.Check(peer2, {gtxid2}, 2, 0, 0, "q11");
+
+ // When reaching expiryA, it expires (not expiryB, which is later).
+ scenario.AdvanceTime(expiryA - scenario.Now());
+ scenario.Check(peer1, {}, 0, 0, 1, "q12");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q13");
+ scenario.CheckExpired(peer1, gtxid1);
+
+ // Requesting it yet again from peer1 doesn't do anything, as it's already COMPLETED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 0, 1, "q14");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q15");
+
+ // Now announce gtxid2 from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer1, gtxid2, true, MIN_TIME);
+ scenario.Check(peer1, {}, 1, 0, 1, "q16");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q17");
+
+ // And request it from peer1 (weird as peer2 has the preference).
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 1, 1, "q18");
+ scenario.Check(peer2, {gtxid1}, 2, 0, 0, "q19");
+
+ // If peer2 now (normally) requests gtxid2, the existing request by peer1 becomes COMPLETED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer2, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 0, 2, "q20");
+ scenario.Check(peer2, {gtxid1}, 1, 1, 0, "q21");
+
+ // If peer2 goes offline, no viable announcements remain.
+ scenario.DisconnectedPeer(peer2);
+ scenario.Check(peer1, {}, 0, 0, 0, "q22");
+ scenario.Check(peer2, {}, 0, 0, 0, "q23");
+}
+
+void TestInterleavedScenarios()
+{
+ // Create a list of functions which add tests to scenarios.
+ std::vector<std::function<void(Scenario&)>> builders;
+ // Add instances of every test, for every configuration.
+ for (int n = 0; n < 64; ++n) {
+ builders.emplace_back([n](Scenario& scenario){ BuildWtxidTest(scenario, n); });
+ builders.emplace_back([n](Scenario& scenario){ BuildRequestOrderTest(scenario, n & 3); });
+ builders.emplace_back([n](Scenario& scenario){ BuildSingleTest(scenario, n & 31); });
+ builders.emplace_back([n](Scenario& scenario){ BuildPriorityTest(scenario, n & 31); });
+ builders.emplace_back([n](Scenario& scenario){ BuildBigPriorityTest(scenario, (n & 7) + 1); });
+ builders.emplace_back([](Scenario& scenario){ BuildTimeBackwardsTest(scenario); });
+ builders.emplace_back([](Scenario& scenario){ BuildWeirdRequestsTest(scenario); });
+ }
+ // Randomly shuffle all those functions.
+ Shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
+
+ Runner runner;
+ auto starttime = RandomTime1y();
+ // Construct many scenarios, and run (up to) 10 randomly-chosen tests consecutively in each.
+ while (builders.size()) {
+ // Introduce some variation in the start time of each scenario, so they don't all start off
+ // concurrently, but get a more random interleaving.
+ auto scenario_start = starttime + RandomTime8s() + RandomTime8s() + RandomTime8s();
+ Scenario scenario(runner, scenario_start);
+ for (int j = 0; builders.size() && j < 10; ++j) {
+ builders.back()(scenario);
+ builders.pop_back();
+ }
+ }
+ // Sort all the actions from all those scenarios chronologically, resulting in the actions from
+ // distinct scenarios to become interleaved. Use stable_sort so that actions from one scenario
+ // aren't reordered w.r.t. each other.
+ std::stable_sort(runner.actions.begin(), runner.actions.end(), [](const Action& a1, const Action& a2) {
+ return a1.first < a2.first;
+ });
+
+ // Run all actions from all scenarios, in order.
+ for (auto& action : runner.actions) {
+ action.second();
+ }
+
+ BOOST_CHECK_EQUAL(runner.txrequest.Size(), 0U);
+ BOOST_CHECK(runner.expired.empty());
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(TxRequestTest)
+{
+ for (int i = 0; i < 5; ++i) {
+ TestInterleavedScenarios();
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index c3d7af8323..7e6246d68f 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -40,8 +40,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
false,
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
nullptr /* plTxnReplaced */,
- true /* bypass_limits */,
- 0 /* nAbsurdFee */));
+ true /* bypass_limits */));
// Check that the transaction hasn't been added to mempool.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index cdef7dcc3c..bed2ba3608 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -30,7 +30,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
TxValidationState state;
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
+ nullptr /* plTxnReplaced */, true /* bypass_limits */);
};
// Create a double-spend of mature coinbase txn:
@@ -157,7 +157,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey));
CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
- CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey);
+ CScript p2wpkh_scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(coinbaseKey.GetPubKey()));
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(coinbaseKey));
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index c0ae2f8cf2..ae626d4613 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -278,4 +278,10 @@ BOOST_AUTO_TEST_CASE( operator_with_self )
BOOST_CHECK(v == UintToArith256(uint256S("0")));
}
+BOOST_AUTO_TEST_CASE( check_ONE )
+{
+ uint256 one = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
+ BOOST_CHECK_EQUAL(one, uint256::ONE);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index b2ae1cb845..2d3137e1e2 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -97,8 +97,8 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
SelectParams(chainName);
SeedInsecureRand();
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
- InitLogging();
- AppInitParameterInteraction();
+ InitLogging(*m_node.args);
+ AppInitParameterInteraction(*m_node.args);
LogInstance().StartLogging();
SHA256AutoDetect();
ECC_Start();
@@ -141,8 +141,11 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ m_node.mempool = MakeUnique<CTxMemPool>(&::feeEstimator);
+ m_node.mempool->setSanityCheck(1.0);
+
m_node.chainman = &::g_chainman;
- m_node.chainman->InitializeChainstate();
+ m_node.chainman->InitializeChainstate(*m_node.mempool);
::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk());
@@ -164,14 +167,12 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
g_parallel_script_checks = true;
- m_node.mempool = &::mempool;
- m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
- m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ m_node.peerman = MakeUnique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
{
CConnman::Options options;
- options.m_msgproc = m_node.peer_logic.get();
+ options.m_msgproc = m_node.peerman.get();
m_node.connman->Init(options);
}
}
@@ -186,8 +187,8 @@ TestingSetup::~TestingSetup()
m_node.connman.reset();
m_node.banman.reset();
m_node.args = nullptr;
- UnloadBlockIndex(m_node.mempool);
- m_node.mempool = nullptr;
+ UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman);
+ m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
m_node.chainman = nullptr;
@@ -196,49 +197,34 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup()
{
- // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
- // TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-segwitheight", "432");
- // Need to recreate chainparams
- SelectParams(CBaseChainParams::REGTEST);
-
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
- CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
- for (int i = 0; i < COINBASE_MATURITY; i++)
- {
+ CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ for (int i = 0; i < COINBASE_MATURITY; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
-// Create a new block with just given transactions, coinbase paying to
-// scriptPubKey, and try to add it to the current chain.
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
- CBlock& block = pblocktemplate->block;
+ CTxMemPool empty_pool;
+ CBlock block = BlockAssembler(empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
- // Replace mempool-selected txns with just coinbase plus passed-in txns:
- block.vtx.resize(1);
- for (const CMutableTransaction& tx : txns)
+ Assert(block.vtx.size() == 1);
+ for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
- // IncrementExtraNonce creates a valid coinbase and merkleRoot
- {
- LOCK(cs_main);
- unsigned int extraNonce = 0;
- IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce);
}
+ RegenerateCommitments(block);
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
- CBlock result = block;
- return result;
+ return block;
}
TestChain100Setup::~TestChain100Setup()
@@ -246,8 +232,8 @@ TestChain100Setup::~TestChain100Setup()
gArgs.ForceSetArg("-segwitheight", "0");
}
-
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx)
+{
return FromTx(MakeTransactionRef(tx));
}
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 78b279e42a..a09c8c122d 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -102,15 +102,16 @@ class CBlock;
struct CMutableTransaction;
class CScript;
-//
-// Testing fixture that pre-creates a
-// 100-block REGTEST-mode block chain
-//
+/**
+ * Testing fixture that pre-creates a 100-block REGTEST-mode block chain
+ */
struct TestChain100Setup : public RegTestingSetup {
TestChain100Setup();
- // Create a new block with just given transactions, coinbase paying to
- // scriptPubKey, and try to add it to the current chain.
+ /**
+ * Create a new block with just given transactions, coinbase paying to
+ * scriptPubKey, and try to add it to the current chain.
+ */
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey);
@@ -152,4 +153,20 @@ CBlock getBlock13b8a();
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
std::ostream& operator<<(std::ostream& os, const uint256& num);
+/**
+ * BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
+ * Use as
+ * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo"));
+ */
+class HasReason {
+public:
+ explicit HasReason(const std::string& reason) : m_reason(reason) {}
+ template <typename E>
+ bool operator() (const E& e) const {
+ return std::string(e.what()).find(m_reason) != std::string::npos;
+ };
+private:
+ const std::string m_reason;
+};
+
#endif
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index b49370c967..241c56934e 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -550,57 +550,52 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
- BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")
- && test_args.m_settings.ro_config[""].count("b")
- && test_args.m_settings.ro_config[""].count("ccc")
- && test_args.m_settings.ro_config[""].count("d")
- && test_args.m_settings.ro_config[""].count("fff")
- && test_args.m_settings.ro_config[""].count("ggg")
- && test_args.m_settings.ro_config[""].count("h")
- && test_args.m_settings.ro_config[""].count("i")
- );
- BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")
- && test_args.m_settings.ro_config["sec1"].count("h")
- && test_args.m_settings.ro_config["sec2"].count("ccc")
- && test_args.m_settings.ro_config["sec2"].count("iii")
- );
-
- BOOST_CHECK(test_args.IsArgSet("-a")
- && test_args.IsArgSet("-b")
- && test_args.IsArgSet("-ccc")
- && test_args.IsArgSet("-d")
- && test_args.IsArgSet("-fff")
- && test_args.IsArgSet("-ggg")
- && test_args.IsArgSet("-h")
- && test_args.IsArgSet("-i")
- && !test_args.IsArgSet("-zzz")
- && !test_args.IsArgSet("-iii")
- );
-
- BOOST_CHECK(test_args.GetArg("-a", "xxx") == ""
- && test_args.GetArg("-b", "xxx") == "1"
- && test_args.GetArg("-ccc", "xxx") == "argument"
- && test_args.GetArg("-d", "xxx") == "e"
- && test_args.GetArg("-fff", "xxx") == "0"
- && test_args.GetArg("-ggg", "xxx") == "1"
- && test_args.GetArg("-h", "xxx") == "0"
- && test_args.GetArg("-i", "xxx") == "1"
- && test_args.GetArg("-zzz", "xxx") == "xxx"
- && test_args.GetArg("-iii", "xxx") == "xxx"
- );
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("a"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("b"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("ccc"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("d"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("fff"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("ggg"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("h"));
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("i"));
+ BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc"));
+ BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("h"));
+ BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("ccc"));
+ BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("iii"));
+
+ BOOST_CHECK(test_args.IsArgSet("-a"));
+ BOOST_CHECK(test_args.IsArgSet("-b"));
+ BOOST_CHECK(test_args.IsArgSet("-ccc"));
+ BOOST_CHECK(test_args.IsArgSet("-d"));
+ BOOST_CHECK(test_args.IsArgSet("-fff"));
+ BOOST_CHECK(test_args.IsArgSet("-ggg"));
+ BOOST_CHECK(test_args.IsArgSet("-h"));
+ BOOST_CHECK(test_args.IsArgSet("-i"));
+ BOOST_CHECK(!test_args.IsArgSet("-zzz"));
+ BOOST_CHECK(!test_args.IsArgSet("-iii"));
+
+ BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), "");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-ccc", "xxx"), "argument");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-d", "xxx"), "e");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-h", "xxx"), "0");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-i", "xxx"), "1");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx");
for (const bool def : {false, true}) {
- BOOST_CHECK(test_args.GetBoolArg("-a", def)
- && test_args.GetBoolArg("-b", def)
- && !test_args.GetBoolArg("-ccc", def)
- && !test_args.GetBoolArg("-d", def)
- && !test_args.GetBoolArg("-fff", def)
- && test_args.GetBoolArg("-ggg", def)
- && !test_args.GetBoolArg("-h", def)
- && test_args.GetBoolArg("-i", def)
- && test_args.GetBoolArg("-zzz", def) == def
- && test_args.GetBoolArg("-iii", def) == def
- );
+ BOOST_CHECK(test_args.GetBoolArg("-a", def));
+ BOOST_CHECK(test_args.GetBoolArg("-b", def));
+ BOOST_CHECK(!test_args.GetBoolArg("-ccc", def));
+ BOOST_CHECK(!test_args.GetBoolArg("-d", def));
+ BOOST_CHECK(!test_args.GetBoolArg("-fff", def));
+ BOOST_CHECK(test_args.GetBoolArg("-ggg", def));
+ BOOST_CHECK(!test_args.GetBoolArg("-h", def));
+ BOOST_CHECK(test_args.GetBoolArg("-i", def));
+ BOOST_CHECK(test_args.GetBoolArg("-zzz", def) == def);
+ BOOST_CHECK(test_args.GetBoolArg("-iii", def) == def);
}
BOOST_CHECK(test_args.GetArgs("-a").size() == 1
@@ -636,13 +631,12 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
test_args.SelectConfigNetwork("sec1");
// same as original
- BOOST_CHECK(test_args.GetArg("-a", "xxx") == ""
- && test_args.GetArg("-b", "xxx") == "1"
- && test_args.GetArg("-fff", "xxx") == "0"
- && test_args.GetArg("-ggg", "xxx") == "1"
- && test_args.GetArg("-zzz", "xxx") == "xxx"
- && test_args.GetArg("-iii", "xxx") == "xxx"
- );
+ BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), "");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx");
+ BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx");
// d is overridden
BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee");
// section-specific setting
@@ -657,14 +651,13 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
test_args.SelectConfigNetwork("sec2");
// same as original
- BOOST_CHECK(test_args.GetArg("-a", "xxx") == ""
- && test_args.GetArg("-b", "xxx") == "1"
- && test_args.GetArg("-d", "xxx") == "e"
- && test_args.GetArg("-fff", "xxx") == "0"
- && test_args.GetArg("-ggg", "xxx") == "1"
- && test_args.GetArg("-zzz", "xxx") == "xxx"
- && test_args.GetArg("-h", "xxx") == "0"
- );
+ BOOST_CHECK(test_args.GetArg("-a", "xxx") == "");
+ BOOST_CHECK(test_args.GetArg("-b", "xxx") == "1");
+ BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e");
+ BOOST_CHECK(test_args.GetArg("-fff", "xxx") == "0");
+ BOOST_CHECK(test_args.GetArg("-ggg", "xxx") == "1");
+ BOOST_CHECK(test_args.GetArg("-zzz", "xxx") == "xxx");
+ BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0");
// section-specific setting
BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2");
// section takes priority for multiple values
@@ -855,8 +848,8 @@ struct ArgsMergeTestingSetup : public BasicTestingSetup {
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
for (bool soft_set : {false, true}) {
for (bool force_set : {false, true}) {
- for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
- for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
+ for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) {
+ for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) {
for (bool net_specific : {false, true}) {
fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
}
@@ -1010,7 +1003,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
- BOOST_CHECK_EQUAL(out_sha_hex, "8fd4877bb8bf337badca950ede6c917441901962f160e52514e06a60dea46cde");
+ BOOST_CHECK_EQUAL(out_sha_hex, "d1e436c1cd510d0ec44d5205d4b4e3bee6387d316e0075c58206cb16603f3d82");
}
// Similar test as above, but for ArgsManager::GetChainName function.
@@ -1113,7 +1106,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <output>
- BOOST_CHECK_EQUAL(out_sha_hex, "f0b3a3c29869edc765d579c928f7f1690a71fbb673b49ccf39cbc4de18156a0d");
+ BOOST_CHECK_EQUAL(out_sha_hex, "f263493e300023b6509963887444c41386f44b63bc30047eb8402e8c1144854c");
}
BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 8e85b7df3e..ea17cb50f1 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -291,8 +291,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
state,
tx,
&plTxnReplaced,
- /* bypass_limits */ false,
- /* nAbsurdFee */ 0));
+ /* bypass_limits */ false));
}
}
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 2076a1096a..c8a375275f 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -20,6 +20,7 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
//! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
@@ -34,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 887a48124f..36badafc4e 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -23,12 +23,13 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(chainstatemanager)
{
ChainstateManager manager;
+ CTxMemPool mempool;
std::vector<CChainState*> chainstates;
const CChainParams& chainparams = Params();
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -54,7 +55,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -104,6 +105,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
size_t max_cache = 10000;
manager.m_total_coinsdb_cache = max_cache;
manager.m_total_coinstip_cache = max_cache;
@@ -112,7 +114,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -129,7 +131,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -147,7 +149,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
-
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 8bac914f05..a3b344d2c9 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -18,8 +18,9 @@ BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
//!
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
+ CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{blockman};
+ CChainState chainstate{mempool, blockman};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
CTxMemPool tx_pool{};
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 3b961db52d..c3816af0cd 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -4,11 +4,11 @@
#include <chainparams.h>
#include <net.h>
+#include <signet.h>
#include <validation.h>
#include <test/util/setup_common.h>
-#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(validation_tests, TestingSetup)
@@ -39,7 +39,7 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
TestBlockSubsidyHalvings(150); // As in regtest
TestBlockSubsidyHalvings(1000); // Just another interval
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(block_subsidy_test)
BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
CAmount nSum = 0;
for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
@@ -58,20 +58,65 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000});
}
-static bool ReturnFalse() { return false; }
-static bool ReturnTrue() { return true; }
-
-BOOST_AUTO_TEST_CASE(test_combiner_all)
+BOOST_AUTO_TEST_CASE(signet_parse_tests)
{
- boost::signals2::signal<bool (), CombinerAll> Test;
- BOOST_CHECK(Test());
- Test.connect(&ReturnFalse);
- BOOST_CHECK(!Test());
- Test.connect(&ReturnTrue);
- BOOST_CHECK(!Test());
- Test.disconnect(&ReturnFalse);
- BOOST_CHECK(Test());
- Test.disconnect(&ReturnTrue);
- BOOST_CHECK(Test());
+ ArgsManager signet_argsman;
+ signet_argsman.ForceSetArg("-signetchallenge", "51"); // set challenge to OP_TRUE
+ const auto signet_params = CreateChainParams(signet_argsman, CBaseChainParams::SIGNET);
+ CBlock block;
+ BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector<uint8_t>{OP_TRUE});
+ CScript challenge{OP_TRUE};
+
+ // empty block is invalid
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no witness commitment
+ CMutableTransaction cb;
+ cb.vout.emplace_back(0, CScript{});
+ block.vtx.push_back(MakeTransactionRef(cb));
+ block.vtx.push_back(MakeTransactionRef(cb)); // Add dummy tx to excercise merkle root code
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no header is treated valid
+ std::vector<uint8_t> witness_commitment_section_141{0xaa, 0x21, 0xa9, 0xed};
+ for (int i = 0; i < 32; ++i) {
+ witness_commitment_section_141.push_back(0xff);
+ }
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no data after header, valid
+ std::vector<uint8_t> witness_commitment_section_325{0xec, 0xc7, 0xda, 0xa2};
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Premature end of data, invalid
+ witness_commitment_section_325.push_back(0x01);
+ witness_commitment_section_325.push_back(0x51);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // has data, valid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Extraneous data, invalid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 11c6bdad91..50444f7bbe 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
// Sanity checks of version bit deployments
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ 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));
@@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
// Use the TESTDUMMY deployment for testing purposes.
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 5f2c40bac6..52bf83b676 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -18,9 +18,7 @@
#define LOCKABLE __attribute__((lockable))
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
-#define GUARDED_VAR __attribute__((guarded_var))
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-#define PT_GUARDED_VAR __attribute__((pt_guarded_var))
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
@@ -33,14 +31,12 @@
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
-#define ASSERT_EXCLUSIVE_LOCK(...) __attribute((assert_exclusive_lock(__VA_ARGS__)))
+#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
#else
#define LOCKABLE
#define SCOPED_LOCKABLE
#define GUARDED_BY(x)
-#define GUARDED_VAR
#define PT_GUARDED_BY(x)
-#define PT_GUARDED_VAR
#define ACQUIRED_AFTER(...)
#define ACQUIRED_BEFORE(...)
#define EXCLUSIVE_LOCK_FUNCTION(...)
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 6b3a79017b..354092752d 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -36,11 +36,6 @@ int64_t GetAdjustedTime()
return GetTime() + GetTimeOffset();
}
-static int64_t abs64(int64_t n)
-{
- return (n >= 0 ? n : -n);
-}
-
#define BITCOIN_TIMEDATA_MAX_SAMPLES 200
void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
@@ -79,7 +74,8 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
int64_t nMedian = vTimeOffsets.median();
std::vector<int64_t> vSorted = vTimeOffsets.sorted();
// Only let other nodes change our time by so much
- if (abs64(nMedian) <= std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT))) {
+ int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
+ if (nMedian >= -max_adjustment && nMedian <= max_adjustment) {
nTimeOffset = nMedian;
} else {
nTimeOffset = 0;
@@ -89,7 +85,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
bool fMatch = false;
for (const int64_t nOffset : vSorted) {
- if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true;
+ if (nOffset != 0 && nOffset > -5 * 60 && nOffset < 5 * 60) fMatch = true;
}
if (!fMatch) {
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 5d56d1ff89..8ebe3d750d 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -3,13 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
#include <torcontrol.h>
-#include <util/strencodings.h>
-#include <netbase.h>
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <crypto/hmac_sha256.h>
#include <net.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <util/strencodings.h>
#include <util/system.h>
-#include <crypto/hmac_sha256.h>
#include <vector>
#include <deque>
@@ -81,12 +84,12 @@ public:
/**
* Connect to a Tor control port.
- * target is address of the form host:port.
+ * tor_control_center is address of the form host:port.
* connected is the handler that is called when connection is successfully established.
* disconnected is a handler that is called when the connection is broken.
* Return true on success.
*/
- bool Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected);
+ bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
/**
* Disconnect from Tor control port.
@@ -193,16 +196,16 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
}
}
-bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
+bool TorControlConnection::Connect(const std::string& tor_control_center, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
{
if (b_conn)
Disconnect();
- // Parse target address:port
+ // Parse tor_control_center address:port
struct sockaddr_storage connect_to_addr;
int connect_to_addrlen = sizeof(connect_to_addr);
- if (evutil_parse_sockaddr_port(target.c_str(),
+ if (evutil_parse_sockaddr_port(tor_control_center.c_str(),
(struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) {
- LogPrintf("tor: Error parsing socket address %s\n", target);
+ LogPrintf("tor: Error parsing socket address %s\n", tor_control_center);
return false;
}
@@ -215,9 +218,9 @@ bool TorControlConnection::Connect(const std::string &target, const ConnectionCB
this->connected = _connected;
this->disconnected = _disconnected;
- // Finally, connect to target
+ // Finally, connect to tor_control_center
if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) {
- LogPrintf("tor: Error connecting to address %s\n", target);
+ LogPrintf("tor: Error connecting to address %s\n", tor_control_center);
return false;
}
return true;
@@ -410,7 +413,7 @@ static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
class TorController
{
public:
- TorController(struct event_base* base, const std::string& target);
+ TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
~TorController();
/** Get name of file to store private key in */
@@ -420,7 +423,7 @@ public:
void Reconnect();
private:
struct event_base* base;
- std::string target;
+ const std::string m_tor_control_center;
TorControlConnection conn;
std::string private_key;
std::string service_id;
@@ -428,6 +431,7 @@ private:
struct event *reconnect_ev;
float reconnect_timeout;
CService service;
+ const CService m_target;
/** Cookie for SAFECOOKIE auth */
std::vector<uint8_t> cookie;
/** ClientNonce for SAFECOOKIE auth */
@@ -450,18 +454,19 @@ private:
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
};
-TorController::TorController(struct event_base* _base, const std::string& _target):
+TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
base(_base),
- target(_target), conn(base), reconnect(true), reconnect_ev(0),
- reconnect_timeout(RECONNECT_TIMEOUT_START)
+ m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0),
+ reconnect_timeout(RECONNECT_TIMEOUT_START),
+ m_target(target)
{
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
if (!reconnect_ev)
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
// Start connection attempts immediately
- if (!conn.Connect(_target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
- LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target);
+ LogPrintf("tor: Initiating connection to Tor control port %s failed\n", m_tor_control_center);
}
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
@@ -532,11 +537,12 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
}
// Finally - now create the service
- if (private_key.empty()) // No private key, generate one
- private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214
+ if (private_key.empty()) { // No private key, generate one
+ private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214
+ }
// Request onion service, redirect port.
// Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
- _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, Params().GetDefaultPort(), GetListenPort()),
+ _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, Params().GetDefaultPort(), m_target.ToStringIPPort()),
std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Authentication failed\n");
@@ -696,7 +702,7 @@ void TorController::disconnected_cb(TorControlConnection& _conn)
if (!reconnect)
return;
- LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", target);
+ LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center);
// Single-shot timer for reconnect. Use exponential backoff.
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
@@ -710,15 +716,15 @@ void TorController::Reconnect()
/* Try to reconnect and reestablish if we get booted - for example, Tor
* may be restarting.
*/
- if (!conn.Connect(target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
- LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target);
+ LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", m_tor_control_center);
}
}
fs::path TorController::GetPrivateKeyFile()
{
- return GetDataDir() / "onion_private_key";
+ return GetDataDir() / "onion_v3_private_key";
}
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
@@ -731,14 +737,14 @@ void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
static struct event_base *gBase;
static std::thread torControlThread;
-static void TorControlThread()
+static void TorControlThread(CService onion_service_target)
{
- TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL));
+ TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), onion_service_target);
event_base_dispatch(gBase);
}
-void StartTorControl()
+void StartTorControl(CService onion_service_target)
{
assert(!gBase);
#ifdef WIN32
@@ -752,7 +758,9 @@ void StartTorControl()
return;
}
- torControlThread = std::thread(std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread));
+ torControlThread = std::thread(&TraceThread<std::function<void()>>, "torcontrol", [onion_service_target] {
+ TorControlThread(onion_service_target);
+ });
}
void InterruptTorControl()
@@ -773,3 +781,10 @@ void StopTorControl()
gBase = nullptr;
}
}
+
+CService DefaultOnionServiceTarget()
+{
+ struct in_addr onion_service_target;
+ onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
+ return {onion_service_target, BaseParams().OnionServiceTargetPort()};
+}
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 474a4d87d9..71a6960e54 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -8,12 +8,17 @@
#ifndef BITCOIN_TORCONTROL_H
#define BITCOIN_TORCONTROL_H
+#include <string>
+
+class CService;
extern const std::string DEFAULT_TOR_CONTROL;
static const bool DEFAULT_LISTEN_ONION = true;
-void StartTorControl();
+void StartTorControl(CService onion_service_target);
void InterruptTorControl();
void StopTorControl();
+CService DefaultOnionServiceTarget();
+
#endif /* BITCOIN_TORCONTROL_H */
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index de1a3ec68f..0c2b731967 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -55,45 +55,45 @@ size_t CTxMemPoolEntry::GetTxSize() const
}
// Update the given tx for any in-mempool descendants.
-// Assumes that setMemPoolChildren is correct for the given tx and all
+// Assumes that CTxMemPool::m_children is correct for the given tx and all
// descendants.
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
{
- setEntries stageEntries, setAllDescendants;
- stageEntries = GetMemPoolChildren(updateIt);
+ CTxMemPoolEntry::Children stageEntries, descendants;
+ stageEntries = updateIt->GetMemPoolChildrenConst();
while (!stageEntries.empty()) {
- const txiter cit = *stageEntries.begin();
- setAllDescendants.insert(cit);
- stageEntries.erase(cit);
- const setEntries &setChildren = GetMemPoolChildren(cit);
- for (txiter childEntry : setChildren) {
- cacheMap::iterator cacheIt = cachedDescendants.find(childEntry);
+ const CTxMemPoolEntry& descendant = *stageEntries.begin();
+ descendants.insert(descendant);
+ stageEntries.erase(descendant);
+ const CTxMemPoolEntry::Children& children = descendant.GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& childEntry : children) {
+ cacheMap::iterator cacheIt = cachedDescendants.find(mapTx.iterator_to(childEntry));
if (cacheIt != cachedDescendants.end()) {
// We've already calculated this one, just add the entries for this set
// but don't traverse again.
for (txiter cacheEntry : cacheIt->second) {
- setAllDescendants.insert(cacheEntry);
+ descendants.insert(*cacheEntry);
}
- } else if (!setAllDescendants.count(childEntry)) {
+ } else if (!descendants.count(childEntry)) {
// Schedule for later processing
stageEntries.insert(childEntry);
}
}
}
- // setAllDescendants now contains all in-mempool descendants of updateIt.
+ // descendants now contains all in-mempool descendants of updateIt.
// Update and add to cached descendant map
int64_t modifySize = 0;
CAmount modifyFee = 0;
int64_t modifyCount = 0;
- for (txiter cit : setAllDescendants) {
- if (!setExclude.count(cit->GetTx().GetHash())) {
- modifySize += cit->GetTxSize();
- modifyFee += cit->GetModifiedFee();
+ for (const CTxMemPoolEntry& descendant : descendants) {
+ if (!setExclude.count(descendant.GetTx().GetHash())) {
+ modifySize += descendant.GetTxSize();
+ modifyFee += descendant.GetModifiedFee();
modifyCount++;
- cachedDescendants[updateIt].insert(cit);
+ cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
// Update ancestor state for each descendant
- mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
+ mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
}
}
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
@@ -119,7 +119,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// Iterate in reverse, so that whenever we are looking at a transaction
// we are sure that all in-mempool descendants have already been processed.
// This maximizes the benefit of the descendant cache and guarantees that
- // setMemPoolChildren will be updated, an assumption made in
+ // CTxMemPool::m_children will be updated, an assumption made in
// UpdateForDescendants.
for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) {
// calculate children from mapNextTx
@@ -128,8 +128,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
continue;
}
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
- // First calculate the children, and update setMemPoolChildren to
- // include them, and update their setMemPoolParents to include this tx.
+ // First calculate the children, and update CTxMemPool::m_children to
+ // include them, and update their CTxMemPoolEntry::m_parents to include this tx.
// we cache the in-mempool children to avoid duplicate updates
{
const auto epoch = GetFreshEpoch();
@@ -151,7 +151,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const
{
- setEntries parentHashes;
+ CTxMemPoolEntry::Parents staged_ancestors;
const CTransaction &tx = entry.GetTx();
if (fSearchForParents) {
@@ -161,8 +161,8 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
for (unsigned int i = 0; i < tx.vin.size(); i++) {
Optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
if (piter) {
- parentHashes.insert(*piter);
- if (parentHashes.size() + 1 > limitAncestorCount) {
+ staged_ancestors.insert(**piter);
+ if (staged_ancestors.size() + 1 > limitAncestorCount) {
errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
return false;
}
@@ -172,16 +172,17 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
// If we're not searching for parents, we require this to be an
// entry in the mempool already.
txiter it = mapTx.iterator_to(entry);
- parentHashes = GetMemPoolParents(it);
+ staged_ancestors = it->GetMemPoolParentsConst();
}
size_t totalSizeWithAncestors = entry.GetTxSize();
- while (!parentHashes.empty()) {
- txiter stageit = *parentHashes.begin();
+ while (!staged_ancestors.empty()) {
+ const CTxMemPoolEntry& stage = staged_ancestors.begin()->get();
+ txiter stageit = mapTx.iterator_to(stage);
setAncestors.insert(stageit);
- parentHashes.erase(stageit);
+ staged_ancestors.erase(stage);
totalSizeWithAncestors += stageit->GetTxSize();
if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) {
@@ -195,13 +196,15 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
return false;
}
- const setEntries & setMemPoolParents = GetMemPoolParents(stageit);
- for (txiter phash : setMemPoolParents) {
+ const CTxMemPoolEntry::Parents& parents = stageit->GetMemPoolParentsConst();
+ for (const CTxMemPoolEntry& parent : parents) {
+ txiter parent_it = mapTx.iterator_to(parent);
+
// If this is a new ancestor, add it.
- if (setAncestors.count(phash) == 0) {
- parentHashes.insert(phash);
+ if (setAncestors.count(parent_it) == 0) {
+ staged_ancestors.insert(parent);
}
- if (parentHashes.size() + setAncestors.size() + 1 > limitAncestorCount) {
+ if (staged_ancestors.size() + setAncestors.size() + 1 > limitAncestorCount) {
errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount);
return false;
}
@@ -213,10 +216,10 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{
- setEntries parentIters = GetMemPoolParents(it);
+ CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
// add or remove this tx as a child of each parent
- for (txiter piter : parentIters) {
- UpdateChild(piter, it, add);
+ for (const CTxMemPoolEntry& parent : parents) {
+ UpdateChild(mapTx.iterator_to(parent), it, add);
}
const int64_t updateCount = (add ? 1 : -1);
const int64_t updateSize = updateCount * it->GetTxSize();
@@ -242,9 +245,9 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
{
- const setEntries &setMemPoolChildren = GetMemPoolChildren(it);
- for (txiter updateIt : setMemPoolChildren) {
- UpdateParent(updateIt, it, false);
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& updateIt : children) {
+ UpdateParent(mapTx.iterator_to(updateIt), it, false);
}
}
@@ -257,9 +260,9 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// updateDescendants should be true whenever we're not recursively
// removing a tx and all its descendants, eg when a transaction is
// confirmed in a block.
- // Here we only update statistics and not data in mapLinks (which
- // we need to preserve until we're finished with all operations that
- // need to traverse the mempool).
+ // Here we only update statistics and not data in CTxMemPool::Parents
+ // and CTxMemPoolEntry::Children (which we need to preserve until we're
+ // finished with all operations that need to traverse the mempool).
for (txiter removeIt : entriesToRemove) {
setEntries setDescendants;
CalculateDescendants(removeIt, setDescendants);
@@ -282,24 +285,26 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// should be a bit faster.
// However, if we happen to be in the middle of processing a reorg, then
// the mempool can be in an inconsistent state. In this case, the set
- // of ancestors reachable via mapLinks will be the same as the set of
- // ancestors whose packages include this transaction, because when we
- // add a new transaction to the mempool in addUnchecked(), we assume it
- // has no children, and in the case of a reorg where that assumption is
- // false, the in-mempool children aren't linked to the in-block tx's
- // until UpdateTransactionsFromBlock() is called.
+ // of ancestors reachable via GetMemPoolParents()/GetMemPoolChildren()
+ // will be the same as the set of ancestors whose packages include this
+ // transaction, because when we add a new transaction to the mempool in
+ // addUnchecked(), we assume it has no children, and in the case of a
+ // reorg where that assumption is false, the in-mempool children aren't
+ // linked to the in-block tx's until UpdateTransactionsFromBlock() is
+ // called.
// So if we're being called during a reorg, ie before
- // UpdateTransactionsFromBlock() has been called, then mapLinks[] will
- // differ from the set of mempool parents we'd calculate by searching,
- // and it's important that we use the mapLinks[] notion of ancestor
- // transactions as the set of things to update for removal.
+ // UpdateTransactionsFromBlock() has been called, then
+ // GetMemPoolParents()/GetMemPoolChildren() will differ from the set of
+ // mempool parents we'd calculate by searching, and it's important that
+ // we use the cached notion of ancestor transactions as the set of
+ // things to update for removal.
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
// Note that UpdateAncestorsOf severs the child links that point to
// removeIt in the entries for the parents of removeIt.
UpdateAncestorsOf(false, removeIt, setAncestors);
}
// After updating all the ancestor sizes, we can now sever the link between each
- // transaction being removed and any mempool children (ie, update setMemPoolParents
+ // transaction being removed and any mempool children (ie, update CTxMemPoolEntry::m_parents
// for each direct child of a transaction being removed).
for (txiter removeIt : entriesToRemove) {
UpdateChildrenForRemoval(removeIt);
@@ -359,7 +364,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// Used by AcceptToMemoryPool(), which DOES do
// all the appropriate checks.
indexed_transaction_set::iterator newit = mapTx.insert(entry).first;
- mapLinks.insert(make_pair(newit, TxLinks()));
// Update transaction for any feeDelta created by PrioritiseTransaction
// TODO: refactor so that the fee delta is calculated before inserting
@@ -405,12 +409,16 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
{
+ // We increment mempool sequence value no matter removal reason
+ // even if not directly reported below.
+ uint64_t mempool_sequence = GetAndIncrementSequence();
+
if (reason != MemPoolRemovalReason::BLOCK) {
// Notify clients that a transaction has been removed from the mempool
// for any reason except being included in a block. Clients interested
// in transactions included in blocks can subscribe to the BlockConnected
// notification.
- GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason);
+ GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);
}
const uint256 hash = it->GetTx().GetHash();
@@ -430,15 +438,14 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
totalTxSize -= it->GetTxSize();
cachedInnerUsage -= it->DynamicMemoryUsage();
- cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children);
- mapLinks.erase(it);
+ cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
mapTx.erase(it);
nTransactionsUpdated++;
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash, false);}
}
// Calculates descendants of entry that are not already in setDescendants, and adds to
-// setDescendants. Assumes entryit is already a tx in the mempool and setMemPoolChildren
+// setDescendants. Assumes entryit is already a tx in the mempool and CTxMemPoolEntry::m_children
// is correct for tx and all descendants.
// Also assumes that if an entry is in setDescendants already, then all
// in-mempool descendants of it are already in setDescendants as well, so that we
@@ -457,8 +464,9 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants
setDescendants.insert(it);
stage.erase(it);
- const setEntries &setChildren = GetMemPoolChildren(it);
- for (txiter childiter : setChildren) {
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& child : children) {
+ txiter childiter = mapTx.iterator_to(child);
if (!setDescendants.count(childiter)) {
stage.insert(childiter);
}
@@ -584,7 +592,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
void CTxMemPool::_clear()
{
- mapLinks.clear();
mapTx.clear();
mapNextTx.clear();
totalTxSize = 0;
@@ -633,12 +640,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
checkTotal += it->GetTxSize();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
- txlinksMap::const_iterator linksiter = mapLinks.find(it);
- assert(linksiter != mapLinks.end());
- const TxLinks &links = linksiter->second;
- innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children);
+ innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
bool fDependsWait = false;
- setEntries setParentCheck;
+ CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
@@ -646,7 +650,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
fDependsWait = true;
- setParentCheck.insert(it2);
+ setParentCheck.insert(*it2);
} else {
assert(pcoins->HaveCoin(txin.prevout));
}
@@ -657,7 +661,11 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(it3->second == &tx);
i++;
}
- assert(setParentCheck == GetMemPoolParents(it));
+ auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
+ return a.GetTx().GetHash() == b.GetTx().GetHash();
+ };
+ assert(setParentCheck.size() == it->GetMemPoolParentsConst().size());
+ assert(std::equal(setParentCheck.begin(), setParentCheck.end(), it->GetMemPoolParentsConst().begin(), comp));
// Verify ancestor state is correct.
setEntries setAncestors;
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
@@ -680,17 +688,18 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(it->GetModFeesWithAncestors() == nFeesCheck);
// Check children against mapNextTx
- CTxMemPool::setEntries setChildrenCheck;
+ CTxMemPoolEntry::Children setChildrenCheck;
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
uint64_t child_sizes = 0;
for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) {
txiter childit = mapTx.find(iter->second->GetHash());
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
- if (setChildrenCheck.insert(childit).second) {
+ if (setChildrenCheck.insert(*childit).second) {
child_sizes += childit->GetTxSize();
}
}
- assert(setChildrenCheck == GetMemPoolChildren(it));
+ assert(setChildrenCheck.size() == it->GetMemPoolChildrenConst().size());
+ assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), it->GetMemPoolChildrenConst().begin(), comp));
// Also check to make sure size is greater than sum with immediate children.
// just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
@@ -852,9 +861,9 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
}
-void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
+void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
{
- LOCK(cs);
+ AssertLockHeld(cs);
std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash);
if (pos == mapDeltas.end())
return;
@@ -862,9 +871,9 @@ void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
nFeeDelta += delta;
}
-void CTxMemPool::ClearPrioritisation(const uint256 hash)
+void CTxMemPool::ClearPrioritisation(const uint256& hash)
{
- LOCK(cs);
+ AssertLockHeld(cs);
mapDeltas.erase(hash);
}
@@ -920,7 +929,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
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.
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
+ return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}
void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) {
@@ -968,40 +977,26 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimat
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
{
- setEntries s;
- if (add && mapLinks[entry].children.insert(child).second) {
+ AssertLockHeld(cs);
+ CTxMemPoolEntry::Children s;
+ if (add && entry->GetMemPoolChildren().insert(*child).second) {
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
- } else if (!add && mapLinks[entry].children.erase(child)) {
+ } else if (!add && entry->GetMemPoolChildren().erase(*child)) {
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
}
}
void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add)
{
- setEntries s;
- if (add && mapLinks[entry].parents.insert(parent).second) {
+ AssertLockHeld(cs);
+ CTxMemPoolEntry::Parents s;
+ if (add && entry->GetMemPoolParents().insert(*parent).second) {
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
- } else if (!add && mapLinks[entry].parents.erase(parent)) {
+ } else if (!add && entry->GetMemPoolParents().erase(*parent)) {
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
}
}
-const CTxMemPool::setEntries & CTxMemPool::GetMemPoolParents(txiter entry) const
-{
- assert (entry != mapTx.end());
- txlinksMap::const_iterator it = mapLinks.find(entry);
- assert(it != mapLinks.end());
- return it->second.parents;
-}
-
-const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) const
-{
- assert (entry != mapTx.end());
- txlinksMap::const_iterator it = mapLinks.find(entry);
- assert(it != mapLinks.end());
- return it->second.children;
-}
-
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
LOCK(cs);
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
@@ -1087,12 +1082,12 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
txiter candidate = candidates.back();
candidates.pop_back();
if (!counted.insert(candidate).second) continue;
- const setEntries& parents = GetMemPoolParents(candidate);
+ const CTxMemPoolEntry::Parents& parents = candidate->GetMemPoolParentsConst();
if (parents.size() == 0) {
maximum = std::max(maximum, candidate->GetCountWithDescendants());
} else {
- for (txiter i : parents) {
- candidates.push_back(i);
+ for (const CTxMemPoolEntry& i : parents) {
+ candidates.push_back(mapTx.iterator_to(i));
}
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 4743e1b63a..f513f14af6 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -49,6 +49,20 @@ struct LockPoints
LockPoints() : height(0), time(0), maxInputBlock(nullptr) { }
};
+struct CompareIteratorByHash {
+ // SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
+ // (e.g. a wrapped CTxMemPoolEntry&)
+ template <typename T>
+ bool operator()(const std::reference_wrapper<T>& a, const std::reference_wrapper<T>& b) const
+ {
+ return a.get().GetTx().GetHash() < b.get().GetTx().GetHash();
+ }
+ template <typename T>
+ bool operator()(const T& a, const T& b) const
+ {
+ return a->GetTx().GetHash() < b->GetTx().GetHash();
+ }
+};
/** \class CTxMemPoolEntry
*
* CTxMemPoolEntry stores data about the corresponding transaction, as well
@@ -63,8 +77,16 @@ struct LockPoints
class CTxMemPoolEntry
{
+public:
+ typedef std::reference_wrapper<const CTxMemPoolEntry> CTxMemPoolEntryRef;
+ // two aliases, should the types ever diverge
+ typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Parents;
+ typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Children;
+
private:
const CTransactionRef tx;
+ mutable Parents m_parents;
+ mutable Children m_children;
const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
const size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
const size_t nUsageSize; //!< ... and total memory usage
@@ -127,6 +149,11 @@ public:
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; }
+ const Parents& GetMemPoolParentsConst() const { return m_parents; }
+ const Children& GetMemPoolChildrenConst() const { return m_children; }
+ Parents& GetMemPoolParents() const { return m_parents; }
+ Children& GetMemPoolChildren() const { return m_children; }
+
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms
};
@@ -474,6 +501,11 @@ private:
mutable uint64_t m_epoch;
mutable bool m_has_epoch_guard;
+ // 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};
+
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
bool m_is_loaded GUARDED_BY(cs){false};
@@ -547,37 +579,22 @@ public:
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
std::vector<std::pair<uint256, txiter>> vTxHashes GUARDED_BY(cs); //!< All tx witness hashes/entries in mapTx, in random order
- struct CompareIteratorByHash {
- bool operator()(const txiter &a, const txiter &b) const {
- return a->GetTx().GetHash() < b->GetTx().GetHash();
- }
- };
typedef std::set<txiter, CompareIteratorByHash> setEntries;
- const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
- const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
private:
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
- struct TxLinks {
- setEntries parents;
- setEntries children;
- };
-
- typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
- txlinksMap mapLinks;
- void UpdateParent(txiter entry, txiter parent, bool add);
- void UpdateChild(txiter entry, txiter child, bool add);
+ void UpdateParent(txiter entry, txiter parent, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void UpdateChild(txiter entry, txiter child, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
- * track locally submitted transactions to periodically retry initial broadcast
- * map of txid -> wtxid
+ * Track locally submitted transactions to periodically retry initial broadcast.
*/
- std::map<uint256, uint256> m_unbroadcast_txids GUARDED_BY(cs);
+ std::set<uint256> m_unbroadcast_txids GUARDED_BY(cs);
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs);
@@ -626,8 +643,8 @@ public:
/** Affect CreateNewBlock prioritisation of transactions */
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
- void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const;
- void ClearPrioritisation(const uint256 hash);
+ void ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void ClearPrioritisation(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Get the transaction in the pool that spends the same prevout */
const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -710,9 +727,9 @@ public:
return mapTx.size();
}
- uint64_t GetTotalTxSize() const
+ uint64_t GetTotalTxSize() const EXCLUSIVE_LOCKS_REQUIRED(cs)
{
- LOCK(cs);
+ AssertLockHeld(cs);
return totalTxSize;
}
@@ -739,27 +756,38 @@ public:
size_t DynamicMemoryUsage() const;
/** Adds a transaction to the unbroadcast set */
- void AddUnbroadcastTx(const uint256& txid, const uint256& wtxid) {
+ void AddUnbroadcastTx(const uint256& txid)
+ {
LOCK(cs);
- // Sanity Check: the transaction should also be in the mempool
- if (exists(txid)) {
- m_unbroadcast_txids[txid] = wtxid;
- }
- }
+ // Sanity check the transaction is in the mempool & insert into
+ // unbroadcast set.
+ if (exists(txid)) m_unbroadcast_txids.insert(txid);
+ };
/** Removes a transaction from the unbroadcast set */
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);
/** Returns transactions in unbroadcast set */
- std::map<uint256, uint256> GetUnbroadcastTxs() const {
+ std::set<uint256> GetUnbroadcastTxs() const
+ {
LOCK(cs);
return m_unbroadcast_txids;
}
/** Returns whether a txid is in the unbroadcast set */
- bool IsUnbroadcastTx(const uint256& txid) const {
- LOCK(cs);
- return (m_unbroadcast_txids.count(txid) != 0);
+ bool IsUnbroadcastTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+ AssertLockHeld(cs);
+ return m_unbroadcast_txids.count(txid) != 0;
+ }
+
+ /** Guards this internal counter for external reporting */
+ uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ return m_sequence_number++;
+ }
+
+ uint64_t GetSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ return m_sequence_number;
}
private:
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
new file mode 100644
index 0000000000..09eb78e927
--- /dev/null
+++ b/src/txrequest.cpp
@@ -0,0 +1,756 @@
+// 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 <txrequest.h>
+
+#include <crypto/siphash.h>
+#include <net.h>
+#include <primitives/transaction.h>
+#include <random.h>
+#include <uint256.h>
+#include <util/memory.h>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+
+#include <chrono>
+#include <unordered_map>
+#include <utility>
+
+#include <assert.h>
+
+namespace {
+
+/** The various states a (txhash,peer) pair can be in.
+ *
+ * Note that CANDIDATE is split up into 3 substates (DELAYED, BEST, READY), allowing more efficient implementation.
+ * Also note that the sorting order of ByTxHashView relies on the specific order of values in this enum.
+ *
+ * Expected behaviour is:
+ * - When first announced by a peer, the state is CANDIDATE_DELAYED until reqtime is reached.
+ * - Announcements that have reached their reqtime but not been requested will be either CANDIDATE_READY or
+ * CANDIDATE_BEST. Neither of those has an expiration time; they remain in that state until they're requested or
+ * no longer needed. CANDIDATE_READY announcements are promoted to CANDIDATE_BEST when they're the best one left.
+ * - When requested, an announcement will be in state REQUESTED until expiry is reached.
+ * - If expiry is reached, or the peer replies to the request (either with NOTFOUND or the tx), the state becomes
+ * COMPLETED.
+ */
+enum class State : uint8_t {
+ /** A CANDIDATE announcement whose reqtime is in the future. */
+ CANDIDATE_DELAYED,
+ /** A CANDIDATE announcement that's not CANDIDATE_DELAYED or CANDIDATE_BEST. */
+ CANDIDATE_READY,
+ /** The best CANDIDATE for a given txhash; only if there is no REQUESTED announcement already for that txhash.
+ * The CANDIDATE_BEST is the highest-priority announcement among all CANDIDATE_READY (and _BEST) ones for that
+ * txhash. */
+ CANDIDATE_BEST,
+ /** A REQUESTED announcement. */
+ REQUESTED,
+ /** A COMPLETED announcement. */
+ COMPLETED,
+};
+
+//! Type alias for sequence numbers.
+using SequenceNumber = uint64_t;
+
+/** An announcement. This is the data we track for each txid or wtxid that is announced to us by each peer. */
+struct Announcement {
+ /** Txid or wtxid that was announced. */
+ const uint256 m_txhash;
+ /** For CANDIDATE_{DELAYED,BEST,READY} the reqtime; for REQUESTED the expiry. */
+ std::chrono::microseconds m_time;
+ /** What peer the request was from. */
+ const NodeId m_peer;
+ /** What sequence number this announcement has. */
+ const SequenceNumber m_sequence : 59;
+ /** Whether the request is preferred. */
+ const bool m_preferred : 1;
+ /** Whether this is a wtxid request. */
+ const bool m_is_wtxid : 1;
+
+ /** What state this announcement is in.
+ * This is a uint8_t instead of a State to silence a GCC warning in versions prior to 8.4 and 9.3.
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 */
+ uint8_t m_state : 3;
+
+ /** Convert m_state to a State enum. */
+ State GetState() const { return static_cast<State>(m_state); }
+
+ /** Convert a State enum to a uint8_t and store it in m_state. */
+ void SetState(State state) { m_state = static_cast<uint8_t>(state); }
+
+ /** Whether this announcement is selected. There can be at most 1 selected peer per txhash. */
+ bool IsSelected() const
+ {
+ return GetState() == State::CANDIDATE_BEST || GetState() == State::REQUESTED;
+ }
+
+ /** Whether this announcement is waiting for a certain time to pass. */
+ bool IsWaiting() const
+ {
+ return GetState() == State::REQUESTED || GetState() == State::CANDIDATE_DELAYED;
+ }
+
+ /** Whether this announcement can feasibly be selected if the current IsSelected() one disappears. */
+ bool IsSelectable() const
+ {
+ return GetState() == State::CANDIDATE_READY || GetState() == State::CANDIDATE_BEST;
+ }
+
+ /** Construct a new announcement from scratch, initially in CANDIDATE_DELAYED state. */
+ Announcement(const GenTxid& gtxid, NodeId peer, bool preferred, std::chrono::microseconds reqtime,
+ SequenceNumber sequence) :
+ m_txhash(gtxid.GetHash()), m_time(reqtime), m_peer(peer), m_sequence(sequence), m_preferred(preferred),
+ m_is_wtxid(gtxid.IsWtxid()), m_state(static_cast<uint8_t>(State::CANDIDATE_DELAYED)) {}
+};
+
+//! Type alias for priorities.
+using Priority = uint64_t;
+
+/** A functor with embedded salt that computes priority of an announcement.
+ *
+ * Higher priorities are selected first.
+ */
+class PriorityComputer {
+ const uint64_t m_k0, m_k1;
+public:
+ explicit PriorityComputer(bool deterministic) :
+ m_k0{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)},
+ m_k1{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)} {}
+
+ Priority operator()(const uint256& txhash, NodeId peer, bool preferred) const
+ {
+ uint64_t low_bits = CSipHasher(m_k0, m_k1).Write(txhash.begin(), txhash.size()).Write(peer).Finalize() >> 1;
+ return low_bits | uint64_t{preferred} << 63;
+ }
+
+ Priority operator()(const Announcement& ann) const
+ {
+ return operator()(ann.m_txhash, ann.m_peer, ann.m_preferred);
+ }
+};
+
+// Definitions for the 3 indexes used in the main data structure.
+//
+// Each index has a By* type to identify it, a By*View data type to represent the view of announcement it is sorted
+// by, and an By*ViewExtractor type to convert an announcement into the By*View type.
+// See https://www.boost.org/doc/libs/1_58_0/libs/multi_index/doc/reference/key_extraction.html#key_extractors
+// for more information about the key extraction concept.
+
+// The ByPeer index is sorted by (peer, state == CANDIDATE_BEST, txhash)
+//
+// Uses:
+// * Looking up existing announcements by peer/txhash, by checking both (peer, false, txhash) and
+// (peer, true, txhash).
+// * Finding all CANDIDATE_BEST announcements for a given peer in GetRequestable.
+struct ByPeer {};
+using ByPeerView = std::tuple<NodeId, bool, const uint256&>;
+struct ByPeerViewExtractor
+{
+ using result_type = ByPeerView;
+ result_type operator()(const Announcement& ann) const
+ {
+ return ByPeerView{ann.m_peer, ann.GetState() == State::CANDIDATE_BEST, ann.m_txhash};
+ }
+};
+
+// The ByTxHash index is sorted by (txhash, state, priority).
+//
+// Note: priority == 0 whenever state != CANDIDATE_READY.
+//
+// Uses:
+// * Deleting all announcements with a given txhash in ForgetTxHash.
+// * Finding the best CANDIDATE_READY to convert to CANDIDATE_BEST, when no other CANDIDATE_READY or REQUESTED
+// announcement exists for that txhash.
+// * Determining when no more non-COMPLETED announcements for a given txhash exist, so the COMPLETED ones can be
+// deleted.
+struct ByTxHash {};
+using ByTxHashView = std::tuple<const uint256&, State, Priority>;
+class ByTxHashViewExtractor {
+ const PriorityComputer& m_computer;
+public:
+ ByTxHashViewExtractor(const PriorityComputer& computer) : m_computer(computer) {}
+ using result_type = ByTxHashView;
+ result_type operator()(const Announcement& ann) const
+ {
+ const Priority prio = (ann.GetState() == State::CANDIDATE_READY) ? m_computer(ann) : 0;
+ return ByTxHashView{ann.m_txhash, ann.GetState(), prio};
+ }
+};
+
+enum class WaitState {
+ //! Used for announcements that need efficient testing of "is their timestamp in the future?".
+ FUTURE_EVENT,
+ //! Used for announcements whose timestamp is not relevant.
+ NO_EVENT,
+ //! Used for announcements that need efficient testing of "is their timestamp in the past?".
+ PAST_EVENT,
+};
+
+WaitState GetWaitState(const Announcement& ann)
+{
+ if (ann.IsWaiting()) return WaitState::FUTURE_EVENT;
+ if (ann.IsSelectable()) return WaitState::PAST_EVENT;
+ return WaitState::NO_EVENT;
+}
+
+// The ByTime index is sorted by (wait_state, time).
+//
+// All announcements with a timestamp in the future can be found by iterating the index forward from the beginning.
+// All announcements with a timestamp in the past can be found by iterating the index backwards from the end.
+//
+// Uses:
+// * Finding CANDIDATE_DELAYED announcements whose reqtime has passed, and REQUESTED announcements whose expiry has
+// passed.
+// * Finding CANDIDATE_READY/BEST announcements whose reqtime is in the future (when the clock time went backwards).
+struct ByTime {};
+using ByTimeView = std::pair<WaitState, std::chrono::microseconds>;
+struct ByTimeViewExtractor
+{
+ using result_type = ByTimeView;
+ result_type operator()(const Announcement& ann) const
+ {
+ return ByTimeView{GetWaitState(ann), ann.m_time};
+ }
+};
+
+/** Data type for the main data structure (Announcement objects with ByPeer/ByTxHash/ByTime indexes). */
+using Index = boost::multi_index_container<
+ Announcement,
+ boost::multi_index::indexed_by<
+ boost::multi_index::ordered_unique<boost::multi_index::tag<ByPeer>, ByPeerViewExtractor>,
+ boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByTxHash>, ByTxHashViewExtractor>,
+ boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByTime>, ByTimeViewExtractor>
+ >
+>;
+
+/** Helper type to simplify syntax of iterator types. */
+template<typename Tag>
+using Iter = typename Index::index<Tag>::type::iterator;
+
+/** Per-peer statistics object. */
+struct PeerInfo {
+ size_t m_total = 0; //!< Total number of announcements for this peer.
+ size_t m_completed = 0; //!< Number of COMPLETED announcements for this peer.
+ size_t m_requested = 0; //!< Number of REQUESTED announcements for this peer.
+};
+
+/** Per-txhash statistics object. Only used for sanity checking. */
+struct TxHashInfo
+{
+ //! Number of CANDIDATE_DELAYED announcements for this txhash.
+ size_t m_candidate_delayed = 0;
+ //! Number of CANDIDATE_READY announcements for this txhash.
+ size_t m_candidate_ready = 0;
+ //! Number of CANDIDATE_BEST announcements for this txhash (at most one).
+ size_t m_candidate_best = 0;
+ //! Number of REQUESTED announcements for this txhash (at most one; mutually exclusive with CANDIDATE_BEST).
+ size_t m_requested = 0;
+ //! The priority of the CANDIDATE_BEST announcement if one exists, or max() otherwise.
+ Priority m_priority_candidate_best = std::numeric_limits<Priority>::max();
+ //! The highest priority of all CANDIDATE_READY announcements (or min() if none exist).
+ Priority m_priority_best_candidate_ready = std::numeric_limits<Priority>::min();
+ //! All peers we have an announcement for this txhash for.
+ std::vector<NodeId> m_peers;
+};
+
+/** Compare two PeerInfo objects. Only used for sanity checking. */
+bool operator==(const PeerInfo& a, const PeerInfo& b)
+{
+ return std::tie(a.m_total, a.m_completed, a.m_requested) ==
+ std::tie(b.m_total, b.m_completed, b.m_requested);
+};
+
+/** (Re)compute the PeerInfo map from the index. Only used for sanity checking. */
+std::unordered_map<NodeId, PeerInfo> RecomputePeerInfo(const Index& index)
+{
+ std::unordered_map<NodeId, PeerInfo> ret;
+ for (const Announcement& ann : index) {
+ PeerInfo& info = ret[ann.m_peer];
+ ++info.m_total;
+ info.m_requested += (ann.GetState() == State::REQUESTED);
+ info.m_completed += (ann.GetState() == State::COMPLETED);
+ }
+ return ret;
+}
+
+/** Compute the TxHashInfo map. Only used for sanity checking. */
+std::map<uint256, TxHashInfo> ComputeTxHashInfo(const Index& index, const PriorityComputer& computer)
+{
+ std::map<uint256, TxHashInfo> ret;
+ for (const Announcement& ann : index) {
+ TxHashInfo& info = ret[ann.m_txhash];
+ // Classify how many announcements of each state we have for this txhash.
+ info.m_candidate_delayed += (ann.GetState() == State::CANDIDATE_DELAYED);
+ info.m_candidate_ready += (ann.GetState() == State::CANDIDATE_READY);
+ info.m_candidate_best += (ann.GetState() == State::CANDIDATE_BEST);
+ info.m_requested += (ann.GetState() == State::REQUESTED);
+ // And track the priority of the best CANDIDATE_READY/CANDIDATE_BEST announcements.
+ if (ann.GetState() == State::CANDIDATE_BEST) {
+ info.m_priority_candidate_best = computer(ann);
+ }
+ if (ann.GetState() == State::CANDIDATE_READY) {
+ info.m_priority_best_candidate_ready = std::max(info.m_priority_best_candidate_ready, computer(ann));
+ }
+ // Also keep track of which peers this txhash has an announcement for (so we can detect duplicates).
+ info.m_peers.push_back(ann.m_peer);
+ }
+ return ret;
+}
+
+GenTxid ToGenTxid(const Announcement& ann)
+{
+ return {ann.m_is_wtxid, ann.m_txhash};
+}
+
+} // namespace
+
+/** Actual implementation for TxRequestTracker's data structure. */
+class TxRequestTracker::Impl {
+ //! The current sequence number. Increases for every announcement. This is used to sort txhashes returned by
+ //! GetRequestable in announcement order.
+ SequenceNumber m_current_sequence{0};
+
+ //! This tracker's priority computer.
+ const PriorityComputer m_computer;
+
+ //! This tracker's main data structure. See SanityCheck() for the invariants that apply to it.
+ Index m_index;
+
+ //! Map with this tracker's per-peer statistics.
+ std::unordered_map<NodeId, PeerInfo> m_peerinfo;
+
+public:
+ void SanityCheck() const
+ {
+ // Recompute m_peerdata from m_index. This verifies the data in it as it should just be caching statistics
+ // on m_index. It also verifies the invariant that no PeerInfo announcements with m_total==0 exist.
+ assert(m_peerinfo == RecomputePeerInfo(m_index));
+
+ // Calculate per-txhash statistics from m_index, and validate invariants.
+ for (auto& item : ComputeTxHashInfo(m_index, m_computer)) {
+ TxHashInfo& info = item.second;
+
+ // Cannot have only COMPLETED peer (txhash should have been forgotten already)
+ assert(info.m_candidate_delayed + info.m_candidate_ready + info.m_candidate_best + info.m_requested > 0);
+
+ // Can have at most 1 CANDIDATE_BEST/REQUESTED peer
+ assert(info.m_candidate_best + info.m_requested <= 1);
+
+ // If there are any CANDIDATE_READY announcements, there must be exactly one CANDIDATE_BEST or REQUESTED
+ // announcement.
+ if (info.m_candidate_ready > 0) {
+ assert(info.m_candidate_best + info.m_requested == 1);
+ }
+
+ // If there is both a CANDIDATE_READY and a CANDIDATE_BEST announcement, the CANDIDATE_BEST one must be
+ // at least as good (equal or higher priority) as the best CANDIDATE_READY.
+ if (info.m_candidate_ready && info.m_candidate_best) {
+ assert(info.m_priority_candidate_best >= info.m_priority_best_candidate_ready);
+ }
+
+ // No txhash can have been announced by the same peer twice.
+ std::sort(info.m_peers.begin(), info.m_peers.end());
+ assert(std::adjacent_find(info.m_peers.begin(), info.m_peers.end()) == info.m_peers.end());
+ }
+ }
+
+ void PostGetRequestableSanityCheck(std::chrono::microseconds now) const
+ {
+ for (const Announcement& ann : m_index) {
+ if (ann.IsWaiting()) {
+ // REQUESTED and CANDIDATE_DELAYED must have a time in the future (they should have been converted
+ // to COMPLETED/CANDIDATE_READY respectively).
+ assert(ann.m_time > now);
+ } else if (ann.IsSelectable()) {
+ // CANDIDATE_READY and CANDIDATE_BEST cannot have a time in the future (they should have remained
+ // CANDIDATE_DELAYED, or should have been converted back to it if time went backwards).
+ assert(ann.m_time <= now);
+ }
+ }
+ }
+
+private:
+ //! Wrapper around Index::...::erase that keeps m_peerinfo up to date.
+ template<typename Tag>
+ Iter<Tag> Erase(Iter<Tag> it)
+ {
+ auto peerit = m_peerinfo.find(it->m_peer);
+ peerit->second.m_completed -= it->GetState() == State::COMPLETED;
+ peerit->second.m_requested -= it->GetState() == State::REQUESTED;
+ if (--peerit->second.m_total == 0) m_peerinfo.erase(peerit);
+ return m_index.get<Tag>().erase(it);
+ }
+
+ //! Wrapper around Index::...::modify that keeps m_peerinfo up to date.
+ template<typename Tag, typename Modifier>
+ void Modify(Iter<Tag> it, Modifier modifier)
+ {
+ auto peerit = m_peerinfo.find(it->m_peer);
+ peerit->second.m_completed -= it->GetState() == State::COMPLETED;
+ peerit->second.m_requested -= it->GetState() == State::REQUESTED;
+ m_index.get<Tag>().modify(it, std::move(modifier));
+ peerit->second.m_completed += it->GetState() == State::COMPLETED;
+ peerit->second.m_requested += it->GetState() == State::REQUESTED;
+ }
+
+ //! Convert a CANDIDATE_DELAYED announcement into a CANDIDATE_READY. If this makes it the new best
+ //! CANDIDATE_READY (and no REQUESTED exists) and better than the CANDIDATE_BEST (if any), it becomes the new
+ //! CANDIDATE_BEST.
+ void PromoteCandidateReady(Iter<ByTxHash> it)
+ {
+ assert(it != m_index.get<ByTxHash>().end());
+ assert(it->GetState() == State::CANDIDATE_DELAYED);
+ // Convert CANDIDATE_DELAYED to CANDIDATE_READY first.
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_READY); });
+ // The following code relies on the fact that the ByTxHash is sorted by txhash, and then by state (first
+ // _DELAYED, then _READY, then _BEST/REQUESTED). Within the _READY announcements, the best one (highest
+ // priority) comes last. Thus, if an existing _BEST exists for the same txhash that this announcement may
+ // be preferred over, it must immediately follow the newly created _READY.
+ auto it_next = std::next(it);
+ if (it_next == m_index.get<ByTxHash>().end() || it_next->m_txhash != it->m_txhash ||
+ it_next->GetState() == State::COMPLETED) {
+ // This is the new best CANDIDATE_READY, and there is no IsSelected() announcement for this txhash
+ // already.
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
+ } else if (it_next->GetState() == State::CANDIDATE_BEST) {
+ Priority priority_old = m_computer(*it_next);
+ Priority priority_new = m_computer(*it);
+ if (priority_new > priority_old) {
+ // There is a CANDIDATE_BEST announcement already, but this one is better.
+ Modify<ByTxHash>(it_next, [](Announcement& ann){ ann.SetState(State::CANDIDATE_READY); });
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
+ }
+ }
+ }
+
+ //! Change the state of an announcement to something non-IsSelected(). If it was IsSelected(), the next best
+ //! announcement will be marked CANDIDATE_BEST.
+ void ChangeAndReselect(Iter<ByTxHash> it, State new_state)
+ {
+ assert(new_state == State::COMPLETED || new_state == State::CANDIDATE_DELAYED);
+ assert(it != m_index.get<ByTxHash>().end());
+ if (it->IsSelected() && it != m_index.get<ByTxHash>().begin()) {
+ auto it_prev = std::prev(it);
+ // The next best CANDIDATE_READY, if any, immediately precedes the REQUESTED or CANDIDATE_BEST
+ // announcement in the ByTxHash index.
+ if (it_prev->m_txhash == it->m_txhash && it_prev->GetState() == State::CANDIDATE_READY) {
+ // If one such CANDIDATE_READY exists (for this txhash), convert it to CANDIDATE_BEST.
+ Modify<ByTxHash>(it_prev, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
+ }
+ }
+ Modify<ByTxHash>(it, [new_state](Announcement& ann){ ann.SetState(new_state); });
+ }
+
+ //! Check if 'it' is the only announcement for a given txhash that isn't COMPLETED.
+ bool IsOnlyNonCompleted(Iter<ByTxHash> it)
+ {
+ assert(it != m_index.get<ByTxHash>().end());
+ assert(it->GetState() != State::COMPLETED); // Not allowed to call this on COMPLETED announcements.
+
+ // This announcement has a predecessor that belongs to the same txhash. Due to ordering, and the
+ // fact that 'it' is not COMPLETED, its predecessor cannot be COMPLETED here.
+ if (it != m_index.get<ByTxHash>().begin() && std::prev(it)->m_txhash == it->m_txhash) return false;
+
+ // This announcement has a successor that belongs to the same txhash, and is not COMPLETED.
+ if (std::next(it) != m_index.get<ByTxHash>().end() && std::next(it)->m_txhash == it->m_txhash &&
+ std::next(it)->GetState() != State::COMPLETED) return false;
+
+ return true;
+ }
+
+ /** Convert any announcement to a COMPLETED one. If there are no non-COMPLETED announcements left for this
+ * txhash, they are deleted. If this was a REQUESTED announcement, and there are other CANDIDATEs left, the
+ * best one is made CANDIDATE_BEST. Returns whether the announcement still exists. */
+ bool MakeCompleted(Iter<ByTxHash> it)
+ {
+ assert(it != m_index.get<ByTxHash>().end());
+
+ // Nothing to be done if it's already COMPLETED.
+ if (it->GetState() == State::COMPLETED) return true;
+
+ if (IsOnlyNonCompleted(it)) {
+ // This is the last non-COMPLETED announcement for this txhash. Delete all.
+ uint256 txhash = it->m_txhash;
+ do {
+ it = Erase<ByTxHash>(it);
+ } while (it != m_index.get<ByTxHash>().end() && it->m_txhash == txhash);
+ return false;
+ }
+
+ // Mark the announcement COMPLETED, and select the next best announcement (the first CANDIDATE_READY) if
+ // needed.
+ ChangeAndReselect(it, State::COMPLETED);
+
+ return true;
+ }
+
+ //! Make the data structure consistent with a given point in time:
+ //! - REQUESTED annoucements 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)
+ {
+ if (expired) expired->clear();
+
+ // Iterate over all CANDIDATE_DELAYED and REQUESTED from old to new, as long as they're in the past,
+ // and convert them to CANDIDATE_READY and COMPLETED respectively.
+ while (!m_index.empty()) {
+ auto it = m_index.get<ByTime>().begin();
+ if (it->GetState() == State::CANDIDATE_DELAYED && it->m_time <= now) {
+ PromoteCandidateReady(m_index.project<ByTxHash>(it));
+ } else if (it->GetState() == State::REQUESTED && it->m_time <= now) {
+ if (expired) expired->emplace_back(it->m_peer, ToGenTxid(*it));
+ MakeCompleted(m_index.project<ByTxHash>(it));
+ } else {
+ break;
+ }
+ }
+
+ while (!m_index.empty()) {
+ // If time went backwards, we may need to demote CANDIDATE_BEST and CANDIDATE_READY announcements back
+ // to CANDIDATE_DELAYED. This is an unusual edge case, and unlikely to matter in production. However,
+ // it makes it much easier to specify and test TxRequestTracker::Impl's behaviour.
+ auto it = std::prev(m_index.get<ByTime>().end());
+ if (it->IsSelectable() && it->m_time > now) {
+ ChangeAndReselect(m_index.project<ByTxHash>(it), State::CANDIDATE_DELAYED);
+ } else {
+ break;
+ }
+ }
+ }
+
+public:
+ Impl(bool deterministic) :
+ m_computer(deterministic),
+ // Explicitly initialize m_index as we need to pass a reference to m_computer to ByTxHashViewExtractor.
+ m_index(boost::make_tuple(
+ boost::make_tuple(ByPeerViewExtractor(), std::less<ByPeerView>()),
+ boost::make_tuple(ByTxHashViewExtractor(m_computer), std::less<ByTxHashView>()),
+ boost::make_tuple(ByTimeViewExtractor(), std::less<ByTimeView>())
+ )) {}
+
+ // Disable copying and assigning (a default copy won't work due the stateful ByTxHashViewExtractor).
+ Impl(const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ void DisconnectedPeer(NodeId peer)
+ {
+ auto& index = m_index.get<ByPeer>();
+ auto it = index.lower_bound(ByPeerView{peer, false, uint256::ZERO});
+ while (it != index.end() && it->m_peer == peer) {
+ // Check what to continue with after this iteration. 'it' will be deleted in what follows, so we need to
+ // decide what to continue with afterwards. There are a number of cases to consider:
+ // - std::next(it) is end() or belongs to a different peer. In that case, this is the last iteration
+ // of the loop (denote this by setting it_next to end()).
+ // - 'it' is not the only non-COMPLETED announcement for its txhash. This means it will be deleted, but
+ // no other Announcement objects will be modified. Continue with std::next(it) if it belongs to the
+ // same peer, but decide this ahead of time (as 'it' may change position in what follows).
+ // - 'it' is the only non-COMPLETED announcement for its txhash. This means it will be deleted along
+ // with all other announcements for the same txhash - which may include std::next(it). However, other
+ // than 'it', no announcements for the same peer can be affected (due to (peer, txhash) uniqueness).
+ // In other words, the situation where std::next(it) is deleted can only occur if std::next(it)
+ // belongs to a different peer but the same txhash as 'it'. This is covered by the first bulletpoint
+ // already, and we'll have set it_next to end().
+ auto it_next = (std::next(it) == index.end() || std::next(it)->m_peer != peer) ? index.end() :
+ std::next(it);
+ // If the announcement isn't already COMPLETED, first make it COMPLETED (which will mark other
+ // CANDIDATEs as CANDIDATE_BEST, or delete all of a txhash's announcements if no non-COMPLETED ones are
+ // left).
+ if (MakeCompleted(m_index.project<ByTxHash>(it))) {
+ // Then actually delete the announcement (unless it was already deleted by MakeCompleted).
+ Erase<ByPeer>(it);
+ }
+ it = it_next;
+ }
+ }
+
+ void ForgetTxHash(const uint256& txhash)
+ {
+ auto it = m_index.get<ByTxHash>().lower_bound(ByTxHashView{txhash, State::CANDIDATE_DELAYED, 0});
+ while (it != m_index.get<ByTxHash>().end() && it->m_txhash == txhash) {
+ it = Erase<ByTxHash>(it);
+ }
+ }
+
+ void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool preferred,
+ std::chrono::microseconds reqtime)
+ {
+ // Bail out if we already have a CANDIDATE_BEST announcement for this (txhash, peer) combination. The case
+ // where there is a non-CANDIDATE_BEST announcement already will be caught by the uniqueness property of the
+ // ByPeer index when we try to emplace the new object below.
+ if (m_index.get<ByPeer>().count(ByPeerView{peer, true, gtxid.GetHash()})) return;
+
+ // Try creating the announcement with CANDIDATE_DELAYED state (which will fail due to the uniqueness
+ // of the ByPeer index if a non-CANDIDATE_BEST announcement already exists with the same txhash and peer).
+ // Bail out in that case.
+ auto ret = m_index.get<ByPeer>().emplace(gtxid, peer, preferred, reqtime, m_current_sequence);
+ if (!ret.second) return;
+
+ // Update accounting metadata.
+ ++m_peerinfo[peer].m_total;
+ ++m_current_sequence;
+ }
+
+ //! Find the GenTxids to request now from peer.
+ std::vector<GenTxid> GetRequestable(NodeId peer, std::chrono::microseconds now,
+ std::vector<std::pair<NodeId, GenTxid>>* expired)
+ {
+ // Move time.
+ SetTimePoint(now, expired);
+
+ // Find all CANDIDATE_BEST announcements for this peer.
+ std::vector<const Announcement*> selected;
+ auto it_peer = m_index.get<ByPeer>().lower_bound(ByPeerView{peer, true, uint256::ZERO});
+ while (it_peer != m_index.get<ByPeer>().end() && it_peer->m_peer == peer &&
+ it_peer->GetState() == State::CANDIDATE_BEST) {
+ selected.emplace_back(&*it_peer);
+ ++it_peer;
+ }
+
+ // Sort by sequence number.
+ std::sort(selected.begin(), selected.end(), [](const Announcement* a, const Announcement* b) {
+ return a->m_sequence < b->m_sequence;
+ });
+
+ // Convert to GenTxid and return.
+ std::vector<GenTxid> ret;
+ ret.reserve(selected.size());
+ std::transform(selected.begin(), selected.end(), std::back_inserter(ret), [](const Announcement* ann) {
+ return ToGenTxid(*ann);
+ });
+ return ret;
+ }
+
+ void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds expiry)
+ {
+ auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txhash});
+ if (it == m_index.get<ByPeer>().end()) {
+ // There is no CANDIDATE_BEST announcement, look for a _READY or _DELAYED instead. If the caller only
+ // ever invokes RequestedTx with the values returned by GetRequestable, and no other non-const functions
+ // other than ForgetTxHash and GetRequestable in between, this branch will never execute (as txhashes
+ // returned by GetRequestable always correspond to CANDIDATE_BEST announcements).
+
+ it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txhash});
+ if (it == m_index.get<ByPeer>().end() || (it->GetState() != State::CANDIDATE_DELAYED &&
+ it->GetState() != State::CANDIDATE_READY)) {
+ // There is no CANDIDATE announcement tracked for this peer, so we have nothing to do. Either this
+ // txhash wasn't tracked at all (and the caller should have called ReceivedInv), or it was already
+ // requested and/or completed for other reasons and this is just a superfluous RequestedTx call.
+ return;
+ }
+
+ // Look for an existing CANDIDATE_BEST or REQUESTED with the same txhash. We only need to do this if the
+ // found announcement had a different state than CANDIDATE_BEST. If it did, invariants guarantee that no
+ // other CANDIDATE_BEST or REQUESTED can exist.
+ auto it_old = m_index.get<ByTxHash>().lower_bound(ByTxHashView{txhash, State::CANDIDATE_BEST, 0});
+ if (it_old != m_index.get<ByTxHash>().end() && it_old->m_txhash == txhash) {
+ if (it_old->GetState() == State::CANDIDATE_BEST) {
+ // The data structure's invariants require that there can be at most one CANDIDATE_BEST or one
+ // REQUESTED announcement per txhash (but not both simultaneously), so we have to convert any
+ // existing CANDIDATE_BEST to another CANDIDATE_* when constructing another REQUESTED.
+ // It doesn't matter whether we pick CANDIDATE_READY or _DELAYED here, as SetTimePoint()
+ // will correct it at GetRequestable() time. If time only goes forward, it will always be
+ // _READY, so pick that to avoid extra work in SetTimePoint().
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::CANDIDATE_READY); });
+ } else if (it_old->GetState() == State::REQUESTED) {
+ // As we're no longer waiting for a response to the previous REQUESTED announcement, convert it
+ // to COMPLETED. This also helps guaranteeing progress.
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::COMPLETED); });
+ }
+ }
+ }
+
+ Modify<ByPeer>(it, [expiry](Announcement& ann) {
+ ann.SetState(State::REQUESTED);
+ ann.m_time = expiry;
+ });
+ }
+
+ void ReceivedResponse(NodeId peer, const uint256& txhash)
+ {
+ // We need to search the ByPeer index for both (peer, false, txhash) and (peer, true, txhash).
+ auto it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txhash});
+ if (it == m_index.get<ByPeer>().end()) {
+ it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txhash});
+ }
+ if (it != m_index.get<ByPeer>().end()) MakeCompleted(m_index.project<ByTxHash>(it));
+ }
+
+ size_t CountInFlight(NodeId peer) const
+ {
+ auto it = m_peerinfo.find(peer);
+ if (it != m_peerinfo.end()) return it->second.m_requested;
+ return 0;
+ }
+
+ size_t CountCandidates(NodeId peer) const
+ {
+ auto it = m_peerinfo.find(peer);
+ if (it != m_peerinfo.end()) return it->second.m_total - it->second.m_requested - it->second.m_completed;
+ return 0;
+ }
+
+ size_t Count(NodeId peer) const
+ {
+ auto it = m_peerinfo.find(peer);
+ if (it != m_peerinfo.end()) return it->second.m_total;
+ return 0;
+ }
+
+ //! Count how many announcements are being tracked in total across all peers and transactions.
+ size_t Size() const { return m_index.size(); }
+
+ uint64_t ComputePriority(const uint256& txhash, NodeId peer, bool preferred) const
+ {
+ // Return Priority as a uint64_t as Priority is internal.
+ return uint64_t{m_computer(txhash, peer, preferred)};
+ }
+
+};
+
+TxRequestTracker::TxRequestTracker(bool deterministic) :
+ m_impl{MakeUnique<TxRequestTracker::Impl>(deterministic)} {}
+
+TxRequestTracker::~TxRequestTracker() = default;
+
+void TxRequestTracker::ForgetTxHash(const uint256& txhash) { m_impl->ForgetTxHash(txhash); }
+void TxRequestTracker::DisconnectedPeer(NodeId peer) { m_impl->DisconnectedPeer(peer); }
+size_t TxRequestTracker::CountInFlight(NodeId peer) const { return m_impl->CountInFlight(peer); }
+size_t TxRequestTracker::CountCandidates(NodeId peer) const { return m_impl->CountCandidates(peer); }
+size_t TxRequestTracker::Count(NodeId peer) const { return m_impl->Count(peer); }
+size_t TxRequestTracker::Size() const { return m_impl->Size(); }
+void TxRequestTracker::SanityCheck() const { m_impl->SanityCheck(); }
+
+void TxRequestTracker::PostGetRequestableSanityCheck(std::chrono::microseconds now) const
+{
+ m_impl->PostGetRequestableSanityCheck(now);
+}
+
+void TxRequestTracker::ReceivedInv(NodeId peer, const GenTxid& gtxid, bool preferred,
+ std::chrono::microseconds reqtime)
+{
+ m_impl->ReceivedInv(peer, gtxid, preferred, reqtime);
+}
+
+void TxRequestTracker::RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds expiry)
+{
+ m_impl->RequestedTx(peer, txhash, expiry);
+}
+
+void TxRequestTracker::ReceivedResponse(NodeId peer, const uint256& txhash)
+{
+ m_impl->ReceivedResponse(peer, txhash);
+}
+
+std::vector<GenTxid> TxRequestTracker::GetRequestable(NodeId peer, std::chrono::microseconds now,
+ std::vector<std::pair<NodeId, GenTxid>>* expired)
+{
+ return m_impl->GetRequestable(peer, now, expired);
+}
+
+uint64_t TxRequestTracker::ComputePriority(const uint256& txhash, NodeId peer, bool preferred) const
+{
+ return m_impl->ComputePriority(txhash, peer, preferred);
+}
diff --git a/src/txrequest.h b/src/txrequest.h
new file mode 100644
index 0000000000..cd3042c87e
--- /dev/null
+++ b/src/txrequest.h
@@ -0,0 +1,211 @@
+// 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_TXREQUEST_H
+#define BITCOIN_TXREQUEST_H
+
+#include <primitives/transaction.h>
+#include <net.h> // For NodeId
+#include <uint256.h>
+
+#include <chrono>
+#include <vector>
+
+#include <stdint.h>
+
+/** Data structure to keep track of, and schedule, transaction downloads from peers.
+ *
+ * === Specification ===
+ *
+ * We keep track of which peers have announced which transactions, and use that to determine which requests
+ * should go to which peer, when, and in what order.
+ *
+ * The following information is tracked per peer/tx combination ("announcement"):
+ * - Which peer announced it (through their NodeId)
+ * - The txid or wtxid of the transaction (collectively called "txhash" in what follows)
+ * - Whether it was a tx or wtx announcement (see BIP339).
+ * - What the earliest permitted time is that that transaction can be requested from that peer (called "reqtime").
+ * - Whether it's from a "preferred" peer or not. Which announcements get this flag is determined by the caller, but
+ * this is designed for outbound peers, or other peers that we have a higher level of trust in. Even when the
+ * peers' preferredness changes, the preferred flag of existing announcements from that peer won't change.
+ * - Whether or not the transaction was requested already, and if so, when it times out (called "expiry").
+ * - Whether or not the transaction request failed already (timed out, or invalid transaction or NOTFOUND was
+ * received).
+ *
+ * Transaction requests are then assigned to peers, following these rules:
+ *
+ * - No transaction is requested as long as another request for the same txhash is outstanding (it needs to fail
+ * first by passing expiry, or a NOTFOUND or invalid transaction has to be received for it).
+ *
+ * Rationale: to avoid wasting bandwidth on multiple copies of the same transaction. Note that this only works
+ * per txhash, so if the same transaction is announced both through txid and wtxid, we have no means
+ * to prevent fetching both (the caller can however mitigate this by delaying one, see further).
+ *
+ * - The same transaction is never requested twice from the same peer, unless the announcement was forgotten in
+ * between, and re-announced. Announcements are forgotten only:
+ * - If a peer goes offline, all its announcements are forgotten.
+ * - If a transaction has been successfully received, or is otherwise no longer needed, the caller can call
+ * ForgetTxHash, which removes all announcements across all peers with the specified txhash.
+ * - If for a given txhash only already-failed announcements remain, they are all forgotten.
+ *
+ * Rationale: giving a peer multiple chances to announce a transaction would allow them to bias requests in their
+ * favor, worsening transaction censoring attacks. The flip side is that as long as an attacker manages
+ * to prevent us from receiving a transaction, failed announcements (including those from honest peers)
+ * will linger longer, increasing memory usage somewhat. The impact of this is limited by imposing a
+ * cap on the number of tracked announcements per peer. As failed requests in response to announcements
+ * from honest peers should be rare, this almost solely hinders attackers.
+ * Transaction censoring attacks can be done by announcing transactions quickly while not answering
+ * requests for them. See https://allquantor.at/blockchainbib/pdf/miller2015topology.pdf for more
+ * information.
+ *
+ * - Transactions are not requested from a peer until its reqtime has passed.
+ *
+ * Rationale: enable the calling code to define a delay for less-than-ideal peers, so that (presumed) better
+ * peers have a chance to give their announcement first.
+ *
+ * - If multiple viable candidate peers exist according to the above rules, pick a peer as follows:
+ *
+ * - If any preferred peers are available, non-preferred peers are not considered for what follows.
+ *
+ * Rationale: preferred peers are more trusted by us, so are less likely to be under attacker control.
+ *
+ * - Pick a uniformly random peer among the candidates.
+ *
+ * Rationale: random assignments are hard to influence for attackers.
+ *
+ * Together these rules strike a balance between being fast in non-adverserial conditions and minimizing
+ * susceptibility to censorship attacks. An attacker that races the network:
+ * - Will be unsuccessful if all preferred connections are honest (and there is at least one preferred connection).
+ * - If there are P preferred connections of which Ph>=1 are honest, the attacker can delay us from learning
+ * about a transaction by k expiration periods, where k ~ 1 + NHG(N=P-1,K=P-Ph-1,r=1), which has mean
+ * P/(Ph+1) (where NHG stands for Negative Hypergeometric distribution). The "1 +" is due to the fact that the
+ * attacker can be the first to announce through a preferred connection in this scenario, which very likely means
+ * they get the first request.
+ * - If all P preferred connections are to the attacker, and there are NP non-preferred connections of which NPh>=1
+ * are honest, where we assume that the attacker can disconnect and reconnect those connections, the distribution
+ * becomes k ~ P + NB(p=1-NPh/NP,r=1) (where NB stands for Negative Binomial distribution), which has mean
+ * P-1+NP/NPh.
+ *
+ * Complexity:
+ * - Memory usage is proportional to the total number of tracked announcements (Size()) plus the number of
+ * peers with a nonzero number of tracked announcements.
+ * - CPU usage is generally logarithmic in the total number of tracked announcements, plus the number of
+ * announcements affected by an operation (amortized O(1) per announcement).
+ */
+class TxRequestTracker {
+ // Avoid littering this header file with implementation details.
+ class Impl;
+ const std::unique_ptr<Impl> m_impl;
+
+public:
+ //! Construct a TxRequestTracker.
+ explicit TxRequestTracker(bool deterministic = false);
+ ~TxRequestTracker();
+
+ // Conceptually, the data structure consists of a collection of "announcements", one for each peer/txhash
+ // combination:
+ //
+ // - CANDIDATE announcements represent transactions that were announced by a peer, and that become available for
+ // download after their reqtime has passed.
+ //
+ // - REQUESTED announcements represent transactions that have been requested, and which we're awaiting a
+ // response for from that peer. Their expiry value determines when the request times out.
+ //
+ // - COMPLETED announcements represent transactions that have been requested from a peer, and a NOTFOUND or a
+ // transaction was received in response (valid or not), or they timed out. They're only kept around to
+ // prevent requesting them again. If only COMPLETED announcements for a given txhash remain (so no CANDIDATE
+ // or REQUESTED ones), all of them are deleted (this is an invariant, and maintained by all operations below).
+ //
+ // The operations below manipulate the data structure.
+
+ /** Adds a new CANDIDATE announcement.
+ *
+ * Does nothing if one already exists for that (txhash, peer) combination (whether it's CANDIDATE, REQUESTED, or
+ * COMPLETED). Note that the txid/wtxid property is ignored for determining uniqueness, so if an announcement
+ * is added for a wtxid H, while one for txid H from the same peer already exists, it will be ignored. This is
+ * harmless as the txhashes being equal implies it is a non-segwit transaction, so it doesn't matter how it is
+ * fetched. The new announcement is given the specified preferred and reqtime values, and takes its is_wtxid
+ * from the specified gtxid.
+ */
+ void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool preferred,
+ std::chrono::microseconds reqtime);
+
+ /** Deletes all announcements for a given peer.
+ *
+ * It should be called when a peer goes offline.
+ */
+ void DisconnectedPeer(NodeId peer);
+
+ /** Deletes all announcements for a given txhash (both txid and wtxid ones).
+ *
+ * This should be called when a transaction is no longer needed. The caller should ensure that new announcements
+ * for the same txhash will not trigger new ReceivedInv calls, at least in the short term after this call.
+ */
+ void ForgetTxHash(const uint256& txhash);
+
+ /** Find the txids to request now from peer.
+ *
+ * It does the following:
+ * - Convert all REQUESTED announcements (for all txhashes/peers) with (expiry <= now) to COMPLETED ones.
+ * These are returned in expired, if non-nullptr.
+ * - Requestable announcements are selected: CANDIDATE announcements from the specified peer with
+ * (reqtime <= now) for which no existing REQUESTED announcement with the same txhash from a different peer
+ * exists, and for which the specified peer is the best choice among all (reqtime <= now) CANDIDATE
+ * announcements with the same txhash (subject to preferredness rules, and tiebreaking using a deterministic
+ * salted hash of peer and txhash).
+ * - The selected announcements are converted to GenTxids using their is_wtxid flag, and returned in
+ * announcement order (even if multiple were added at the same time, or when the clock went backwards while
+ * they were being added). This is done to minimize disruption from dependent transactions being requested
+ * out of order: if multiple dependent transactions are announced simultaneously by one peer, and end up
+ * being requested from them, the requests will happen in announcement order.
+ */
+ std::vector<GenTxid> GetRequestable(NodeId peer, std::chrono::microseconds now,
+ std::vector<std::pair<NodeId, GenTxid>>* expired = nullptr);
+
+ /** Marks a transaction as requested, with a specified expiry.
+ *
+ * If no CANDIDATE announcement for the provided peer and txhash exists, this call has no effect. Otherwise:
+ * - That announcement is converted to REQUESTED.
+ * - If any other REQUESTED announcement for the same txhash already existed, it means an unexpected request
+ * was made (GetRequestable will never advise doing so). In this case it is converted to COMPLETED, as we're
+ * no longer waiting for a response to it.
+ */
+ void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds expiry);
+
+ /** Converts a CANDIDATE or REQUESTED announcement to a COMPLETED one. If no such announcement exists for the
+ * provided peer and txhash, nothing happens.
+ *
+ * It should be called whenever a transaction or NOTFOUND was received from a peer. When the transaction is
+ * not needed entirely anymore, ForgetTxhash should be called instead of, or in addition to, this call.
+ */
+ void ReceivedResponse(NodeId peer, const uint256& txhash);
+
+ // The operations below inspect the data structure.
+
+ /** Count how many REQUESTED announcements a peer has. */
+ size_t CountInFlight(NodeId peer) const;
+
+ /** Count how many CANDIDATE announcements a peer has. */
+ size_t CountCandidates(NodeId peer) const;
+
+ /** Count how many announcements a peer has (REQUESTED, CANDIDATE, and COMPLETED combined). */
+ size_t Count(NodeId peer) const;
+
+ /** Count how many announcements are being tracked in total across all peers and transaction hashes. */
+ size_t Size() const;
+
+ /** Access to the internal priority computation (testing only) */
+ uint64_t ComputePriority(const uint256& txhash, NodeId peer, bool preferred) const;
+
+ /** Run internal consistency check (testing only). */
+ void SanityCheck() const;
+
+ /** Run a time-dependent internal consistency check (testing only).
+ *
+ * This can only be called immediately after GetRequestable, with the same 'now' parameter.
+ */
+ void PostGetRequestableSanityCheck(std::chrono::microseconds now) const;
+};
+
+#endif // BITCOIN_TXREQUEST_H
diff --git a/src/uint256.cpp b/src/uint256.cpp
index ee1b34eadd..f358b62903 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -80,7 +80,5 @@ template std::string base_blob<256>::ToString() const;
template void base_blob<256>::SetHex(const char*);
template void base_blob<256>::SetHex(const std::string&);
-uint256& UINT256_ONE() {
- static uint256* one = new uint256(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
- return *one;
-}
+const uint256 uint256::ZERO(0);
+const uint256 uint256::ONE(1);
diff --git a/src/uint256.h b/src/uint256.h
index 8ab747ef49..ceae70707e 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -20,10 +20,11 @@ protected:
static constexpr int WIDTH = BITS / 8;
uint8_t m_data[WIDTH];
public:
- base_blob()
- {
- memset(m_data, 0, sizeof(m_data));
- }
+ /* construct 0 value by default */
+ constexpr base_blob() : m_data() {}
+
+ /* constructor for constants between 1 and 255 */
+ constexpr explicit base_blob(uint8_t v) : m_data{v} {}
explicit base_blob(const std::vector<unsigned char>& vch);
@@ -111,7 +112,7 @@ public:
*/
class uint160 : public base_blob<160> {
public:
- uint160() {}
+ constexpr uint160() {}
explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {}
};
@@ -122,8 +123,11 @@ public:
*/
class uint256 : public base_blob<256> {
public:
- uint256() {}
+ constexpr uint256() {}
+ constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {}
explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
+ static const uint256 ZERO;
+ static const uint256 ONE;
};
/* uint256 from const char *.
@@ -147,6 +151,4 @@ inline uint256 uint256S(const std::string& str)
return rv;
}
-uint256& UINT256_ONE();
-
#endif // BITCOIN_UINT256_H
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 3e29083712..6c94b80683 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -30,7 +30,7 @@ bilingual_str TransactionErrorString(const TransactionError err)
case TransactionError::SIGHASH_MISMATCH:
return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
- return Untranslated("Fee exceeds maximum configured by -maxtxfee");
+ return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
// no default case, so the compiler can warn about missing cases
}
assert(false);
diff --git a/src/util/message.cpp b/src/util/message.cpp
index 1e7128d225..e1d5cff48c 100644
--- a/src/util/message.cpp
+++ b/src/util/message.cpp
@@ -64,7 +64,7 @@ bool MessageSign(
return false;
}
- signature = EncodeBase64(signature_bytes.data(), signature_bytes.size());
+ signature = EncodeBase64(signature_bytes);
return true;
}
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index d10f92ffe6..3236184b0b 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -126,20 +126,20 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
hostOut = in;
}
-std::string EncodeBase64(const unsigned char* pch, size_t len)
+std::string EncodeBase64(Span<const unsigned char> input)
{
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
- str.reserve(((len + 2) / 3) * 4);
- ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
+ str.reserve(((input.size() + 2) / 3) * 4);
+ ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
while (str.size() % 4) str += '=';
return str;
}
std::string EncodeBase64(const std::string& str)
{
- return EncodeBase64((const unsigned char*)str.data(), str.size());
+ return EncodeBase64(MakeUCharSpan(str));
}
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
@@ -201,20 +201,24 @@ std::string DecodeBase64(const std::string& str, bool* pf_invalid)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-std::string EncodeBase32(const unsigned char* pch, size_t len)
+std::string EncodeBase32(Span<const unsigned char> input, bool pad)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
- str.reserve(((len + 4) / 5) * 8);
- ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len);
- while (str.size() % 8) str += '=';
+ str.reserve(((input.size() + 4) / 5) * 8);
+ ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
+ if (pad) {
+ while (str.size() % 8) {
+ str += '=';
+ }
+ }
return str;
}
-std::string EncodeBase32(const std::string& str)
+std::string EncodeBase32(const std::string& str, bool pad)
{
- return EncodeBase32((const unsigned char*)str.data(), str.size());
+ return EncodeBase32(MakeUCharSpan(str), pad);
}
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
@@ -318,6 +322,18 @@ bool ParseInt64(const std::string& str, int64_t *out)
n <= std::numeric_limits<int64_t>::max();
}
+bool ParseUInt8(const std::string& str, uint8_t *out)
+{
+ uint32_t u32;
+ if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<uint8_t>(u32);
+ }
+ return true;
+}
+
bool ParseUInt32(const std::string& str, uint32_t *out)
{
if (!ParsePrechecks(str))
@@ -407,15 +423,6 @@ std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
return out.str();
}
-int64_t atoi64(const char* psz)
-{
-#ifdef _MSC_VER
- return _atoi64(psz);
-#else
- return strtoll(psz, nullptr, 10);
-#endif
-}
-
int64_t atoi64(const std::string& str)
{
#ifdef _MSC_VER
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index eaa0fa9992..1a217dd12d 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -48,15 +48,26 @@ bool IsHex(const std::string& str);
bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
-std::string EncodeBase64(const unsigned char* pch, size_t len);
+std::string EncodeBase64(Span<const unsigned char> input);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
-std::string EncodeBase32(const unsigned char* pch, size_t len);
-std::string EncodeBase32(const std::string& str);
+
+/**
+ * Base32 encode.
+ * If `pad` is true, then the output will be padded with '=' so that its length
+ * is a multiple of 8.
+ */
+std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
+
+/**
+ * Base32 encode.
+ * If `pad` is true, then the output will be padded with '=' so that its length
+ * is a multiple of 8.
+ */
+std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
-int64_t atoi64(const char* psz);
int64_t atoi64(const std::string& str);
int atoi(const std::string& str);
@@ -100,6 +111,13 @@ NODISCARD bool ParseInt32(const std::string& str, int32_t *out);
NODISCARD bool ParseInt64(const std::string& str, int64_t *out);
/**
+ * Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
+ * @returns true if the entire string could be parsed as valid integer,
+ * false if not the entire string could be parsed or when overflow or underflow occurred.
+ */
+NODISCARD bool ParseUInt8(const std::string& str, uint8_t *out);
+
+/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
diff --git a/src/util/string.h b/src/util/string.h
index cdb41630c6..a0c87bd00c 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -7,6 +7,8 @@
#include <attributes.h>
+#include <algorithm>
+#include <array>
#include <cstring>
#include <locale>
#include <sstream>
@@ -74,4 +76,15 @@ std::string ToString(const T& t)
return oss.str();
}
+/**
+ * Check whether a container begins with the given prefix.
+ */
+template <typename T1, size_t PREFIX_LEN>
+NODISCARD inline bool HasPrefix(const T1& obj,
+ const std::array<uint8_t, PREFIX_LEN>& prefix)
+{
+ return obj.size() >= PREFIX_LEN &&
+ std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
+}
+
#endif // BITCOIN_UTIL_STRENCODINGS_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 7b74789b32..9f8035948b 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -48,12 +48,6 @@
#pragma warning(disable:4717)
#endif
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
-#define _WIN32_IE 0x0501
-
-#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -269,6 +263,7 @@ const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
// Section names to be recognized in the config file.
static const std::set<std::string> available_sections{
CBaseChainParams::REGTEST,
+ CBaseChainParams::SIGNET,
CBaseChainParams::TESTNET,
CBaseChainParams::MAIN
};
@@ -432,6 +427,14 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
SaveErrors(read_errors, errors);
return false;
}
+ for (const auto& setting : m_settings.rw_settings) {
+ std::string section;
+ std::string key = setting.first;
+ (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname
+ if (!GetArgFlags('-' + key)) {
+ LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
+ }
+ }
return true;
}
@@ -528,7 +531,7 @@ void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
std::string ArgsManager::GetHelpMessage() const
{
- const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
+ const bool show_debug = GetBoolArg("-help-debug", false);
std::string usage = "";
LOCK(cs_args);
@@ -905,7 +908,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
// If datadir is changed in .conf file:
ClearDatadirCache();
if (!CheckDataDirOption()) {
- error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", ""));
+ error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
return false;
}
return true;
@@ -922,16 +925,21 @@ std::string ArgsManager::GetChainName() const
};
const bool fRegTest = get_net("-regtest");
+ const bool fSigNet = get_net("-signet");
const bool fTestNet = get_net("-testnet");
const bool is_chain_arg_set = IsArgSet("-chain");
- if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
- throw std::runtime_error("Invalid combination of -regtest, -testnet and -chain. Can use at most one.");
+ if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) {
+ throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one.");
}
if (fRegTest)
return CBaseChainParams::REGTEST;
+ if (fSigNet) {
+ return CBaseChainParams::SIGNET;
+ }
if (fTestNet)
return CBaseChainParams::TESTNET;
+
return GetArg("-chain", CBaseChainParams::MAIN);
}
@@ -1025,7 +1033,7 @@ bool FileCommit(FILE *file)
return false;
}
#else
- #if defined(HAVE_FDATASYNC)
+ #if HAVE_FDATASYNC
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
diff --git a/src/validation.cpp b/src/validation.cpp
index cf2f9dde62..423b93479a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -33,6 +33,7 @@
#include <script/script.h>
#include <script/sigcache.h>
#include <shutdown.h>
+#include <signet.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
@@ -148,7 +149,6 @@ arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CBlockPolicyEstimator feeEstimator;
-CTxMemPool mempool(&feeEstimator);
// Internal stuff
namespace {
@@ -198,9 +198,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
std::unique_ptr<CBlockTreeDB> pblocktree;
-// See definition for documentation
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight);
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
@@ -370,9 +367,10 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
* and instead just erase from the mempool as needed.
*/
-static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
+static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(mempool.cs);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -386,7 +384,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool,
TxValidationState stateDummy;
if (!fAddToMempool || (*it)->IsCoinBase() ||
!AcceptToMemoryPool(mempool, stateDummy, *it,
- nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -465,7 +463,6 @@ public:
const int64_t m_accept_time;
std::list<CTransactionRef>* m_replaced_transactions;
const bool m_bypass_limits;
- const CAmount& m_absurd_fee;
/*
* Return any outpoints which were not previously present in the coins
* cache, but were added as a result of validating the tx for mempool
@@ -475,6 +472,7 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
+ CAmount* m_fee_out;
};
// Single transaction acceptance
@@ -559,7 +557,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
TxValidationState &state = args.m_state;
const int64_t nAcceptTime = args.m_accept_time;
const bool bypass_limits = args.m_bypass_limits;
- const CAmount& nAbsurdFee = args.m_absurd_fee;
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
// Alias what we need out of ws
@@ -687,6 +684,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return false; // state filled in by CheckTxInputs
}
+ // If fee_out is passed, return the fee to the caller
+ if (args.m_fee_out) {
+ *args.m_fee_out = nFees;
+ }
+
// Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
@@ -725,10 +727,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// blocks
if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false;
- if (nAbsurdFee && nFees > nAbsurdFee)
- return state.Invalid(TxValidationResult::TX_NOT_STANDARD,
- "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee));
-
const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts);
// Calculate in-mempool ancestors, up to a limit.
if (setConflicts.size() == 1) {
@@ -1051,7 +1049,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
if (!Finalize(args, workspace)) return false;
- GetMainSignals().TransactionAddedToMempool(ptx);
+ GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
return true;
}
@@ -1061,10 +1059,10 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
/** (try to) add transaction to memory pool with a specified acceptance time **/
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
+ MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
if (!res) {
// Remove coins that were not present in the coins cache before calling ATMPW;
@@ -1083,10 +1081,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
+ bool bypass_limits, bool test_accept, CAmount* fee_out)
{
const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
}
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -1163,6 +1161,11 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
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;
}
@@ -1254,8 +1257,9 @@ void CoinsViews::InitCache()
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
+CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash)
: m_blockman(blockman),
+ m_mempool(mempool),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
@@ -1528,14 +1532,21 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
return true;
}
- if (!txdata.m_ready) {
- txdata.Init(tx);
+ if (!txdata.m_spent_outputs_ready) {
+ std::vector<CTxOut> spent_outputs;
+ spent_outputs.reserve(tx.vin.size());
+
+ for (const auto& txin : tx.vin) {
+ const COutPoint& prevout = txin.prevout;
+ const Coin& coin = inputs.AccessCoin(prevout);
+ assert(!coin.IsSpent());
+ spent_outputs.emplace_back(coin.out);
+ }
+ txdata.Init(tx, std::move(spent_outputs));
}
+ assert(txdata.m_spent_outputs.size() == tx.vin.size());
for (unsigned int i = 0; i < tx.vin.size(); i++) {
- const COutPoint &prevout = tx.vin[i].prevout;
- const Coin& coin = inputs.AccessCoin(prevout);
- assert(!coin.IsSpent());
// We very carefully only pass in things to CScriptCheck which
// are clearly committed to by tx' witness hash. This provides
@@ -1544,7 +1555,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// spent being checked as a part of CScriptCheck.
// Verify signature
- CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
+ CScriptCheck check(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@@ -1558,7 +1569,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// splitting the network between upgraded and
// non-upgraded nodes by banning CONSENSUS-failing
// data providers.
- CScriptCheck check2(coin.out, tx, i,
+ CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
@@ -1903,6 +1914,11 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
+ // Start enforcing Taproot using versionbits logic.
+ if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE) {
+ flags |= SCRIPT_VERIFY_TAPROOT;
+ }
+
// Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit)
if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
@@ -2280,17 +2296,17 @@ bool CChainState::FlushStateToDisk(
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
- CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&::mempool);
+ CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
- FindFilesToPruneManual(g_chainman, setFilesToPrune, nManualPruneHeight);
+ m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height());
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- FindFilesToPrune(g_chainman, setFilesToPrune, chainparams.PruneAfterHeight());
+ m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -2426,7 +2442,7 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
@@ -2439,9 +2455,9 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
}
bilingual_str warning_messages;
+ int num_unexpected_version = 0;
if (!::ChainstateActive().IsInitialBlockDownload())
{
- int nUpgraded = 0;
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
@@ -2460,11 +2476,9 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
{
int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus());
if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0)
- ++nUpgraded;
+ ++num_unexpected_version;
pindex = pindex->pprev;
}
- if (nUpgraded > 0)
- AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
@@ -2473,6 +2487,9 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
+ if (num_unexpected_version > 0) {
+ LogPrint(BCLog::VALIDATION, "%d of last 100 blocks have unexpected version\n", num_unexpected_version);
+ }
}
/** Disconnect m_chain's tip.
@@ -2485,8 +2502,11 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
+bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
@@ -2517,14 +2537,14 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
- mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
+ m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
disconnectpool->removeEntry(it);
}
}
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(pindexDelete->pprev, chainparams);
+ UpdateTip(m_mempool, pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2585,6 +2605,9 @@ public:
*/
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
@@ -2625,11 +2648,11 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
+ m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(pindexNew, chainparams);
+ UpdateTip(m_mempool, pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2719,6 +2742,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
const CBlockIndex *pindexOldTip = m_chain.Tip();
const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2730,7 +2754,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2774,7 +2798,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
return false;
}
} else {
@@ -2791,9 +2815,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, true);
}
- mempool.check(&CoinsTip());
+ m_mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2867,7 +2891,8 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
LimitValidationInterfaceQueue();
{
- LOCK2(cs_main, ::mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
+ LOCK(cs_main);
+ LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@@ -3020,7 +3045,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
LimitValidationInterfaceQueue();
LOCK(cs_main);
- LOCK(::mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
+ LOCK(m_mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -3034,7 +3059,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3332,6 +3357,11 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
return false;
+ // Signet only: check block solution
+ if (consensusParams.signet_blocks && fCheckPOW && !CheckSignetBlockSolution(block, consensusParams)) {
+ return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-signet-blksig", "signet block signature validation failure");
+ }
+
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
@@ -3395,31 +3425,11 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
return (height >= params.SegwitHeight);
}
-int GetWitnessCommitmentIndex(const CBlock& block)
-{
- int commitpos = -1;
- if (!block.vtx.empty()) {
- for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
- const CTxOut& vout = block.vtx[0]->vout[o];
- if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
- vout.scriptPubKey[0] == OP_RETURN &&
- vout.scriptPubKey[1] == 0x24 &&
- vout.scriptPubKey[2] == 0xaa &&
- vout.scriptPubKey[3] == 0x21 &&
- vout.scriptPubKey[4] == 0xa9 &&
- vout.scriptPubKey[5] == 0xed) {
- commitpos = o;
- }
- }
- }
- return commitpos;
-}
-
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
{
int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00);
- if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
+ if (commitpos != NO_WITNESS_COMMITMENT && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
CMutableTransaction tx(*block.vtx[0]);
tx.vin[0].scriptWitness.stack.resize(1);
tx.vin[0].scriptWitness.stack[0] = nonce;
@@ -3433,7 +3443,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
- if (commitpos == -1) {
+ if (commitpos == NO_WITNESS_COMMITMENT) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
CTxOut out;
@@ -3571,7 +3581,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
bool fHaveWitness = false;
if (nHeight >= consensusParams.SegwitHeight) {
int commitpos = GetWitnessCommitmentIndex(block);
- if (commitpos != -1) {
+ if (commitpos != NO_WITNESS_COMMITMENT) {
bool malleated = false;
uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated);
// The malleation check is ignored; as the transaction tree itself
@@ -3904,12 +3914,12 @@ uint64_t CalculateCurrentUsage()
return retval;
}
-void ChainstateManager::PruneOneBlockFile(const int fileNumber)
+void BlockManager::PruneOneBlockFile(const int fileNumber)
{
AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile);
- for (const auto& entry : m_blockman.m_block_index) {
+ for (const auto& entry : m_block_index) {
CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -3923,12 +3933,12 @@ void ChainstateManager::PruneOneBlockFile(const int fileNumber)
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates.
- auto range = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
+ auto range = m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
if (_it->second == pindex) {
- m_blockman.m_blocks_unlinked.erase(_it);
+ m_blocks_unlinked.erase(_it);
}
}
}
@@ -3949,22 +3959,23 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
}
}
-/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight)
+void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
{
assert(fPruneMode && nManualPruneHeight > 0);
LOCK2(cs_main, cs_LastBlockFile);
- if (::ChainActive().Tip() == nullptr)
+ if (chain_tip_height < 0) {
return;
+ }
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
- int count=0;
+ unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
+ int count = 0;
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+ if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
- chainman.PruneOneBlockFile(fileNumber);
+ }
+ PruneOneBlockFile(fileNumber);
setFilesToPrune.insert(fileNumber);
count++;
}
@@ -3982,46 +3993,31 @@ void PruneBlockFilesManual(int nManualPruneHeight)
}
}
-/**
- * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
- * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
- * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
- * (which in this case means the blockchain must be re-downloaded.)
- *
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
- * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
- * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
- * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
- * A db flag records the fact that at least some block files have been pruned.
- *
- * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
- */
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd)
{
LOCK2(cs_main, cs_LastBlockFile);
- if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
+ if (chain_tip_height < 0 || nPruneTarget == 0) {
return;
}
- if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) {
+ if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
return;
}
- unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
// before the next pruning.
uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
uint64_t nBytesToPrune;
- int count=0;
+ int count = 0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
// On a prune event, the chainstate DB is flushed.
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
- if (::ChainstateActive().IsInitialBlockDownload()) {
+ if (is_ibd) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
@@ -4029,17 +4025,20 @@ static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFile
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
- if (vinfoBlockFile[fileNumber].nSize == 0)
+ if (vinfoBlockFile[fileNumber].nSize == 0) {
continue;
+ }
- if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target?
+ if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
break;
+ }
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+ if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
+ }
- chainman.PruneOneBlockFile(fileNumber);
+ PruneOneBlockFile(fileNumber);
// Queue up the files for removal
setFilesToPrune.insert(fileNumber);
nCurrentUsage -= nBytesToPrune;
@@ -4218,6 +4217,14 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
return true;
}
+void CChainState::LoadMempool(const ArgsManager& args)
+{
+ if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ ::LoadMempool(m_mempool);
+ }
+ m_mempool.SetIsLoaded(!ShutdownRequested());
+}
+
bool CChainState::LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
@@ -4517,7 +4524,8 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Loop until the tip is below nHeight, or we reach a pruned block.
while (!ShutdownRequested()) {
{
- LOCK2(cs_main, ::mempool.cs);
+ 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;
@@ -4588,10 +4596,10 @@ void CChainState::UnloadBlockIndex() {
// May NOT be used after any connections are up as much
// of the peer-processing logic assumes a consistent
// block index state
-void UnloadBlockIndex(CTxMemPool* mempool)
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
{
LOCK(cs_main);
- g_chainman.Unload();
+ chainman.Unload();
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
@@ -5077,7 +5085,7 @@ bool LoadMempool(CTxMemPool& pool)
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */,
false /* test_accept */);
if (state.IsValid()) {
++count;
@@ -5106,7 +5114,7 @@ bool LoadMempool(CTxMemPool& pool)
}
// TODO: remove this try except in v0.22
- std::map<uint256, uint256> unbroadcast_txids;
+ std::set<uint256> unbroadcast_txids;
try {
file >> unbroadcast_txids;
unbroadcast = unbroadcast_txids.size();
@@ -5114,13 +5122,10 @@ bool LoadMempool(CTxMemPool& pool)
// mempool.dat files created prior to v0.21 will not have an
// unbroadcast set. No need to log a failure if parsing fails here.
}
- for (const auto& elem : unbroadcast_txids) {
- // Don't add unbroadcast transactions that didn't get back into the
- // mempool.
- const CTransactionRef& added_tx = pool.get(elem.first);
- if (added_tx != nullptr) {
- pool.AddUnbroadcastTx(elem.first, added_tx->GetWitnessHash());
- }
+ for (const auto& txid : unbroadcast_txids) {
+ // Ensure transactions were accepted to mempool then add to
+ // unbroadcast set.
+ if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
@@ -5137,7 +5142,7 @@ bool DumpMempool(const CTxMemPool& pool)
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
- std::map<uint256, uint256> unbroadcast_txids;
+ std::set<uint256> unbroadcast_txids;
static Mutex dump_mutex;
LOCK(dump_mutex);
@@ -5209,20 +5214,6 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
}
-class CMainCleanup
-{
-public:
- CMainCleanup() {}
- ~CMainCleanup() {
- // block headers
- BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
- for (; it1 != g_chainman.BlockIndex().end(); it1++)
- delete (*it1).second;
- g_chainman.BlockIndex().clear();
- }
-};
-static CMainCleanup instance_of_cmaincleanup;
-
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
if (m_active_chainstate != nullptr) {
// If a snapshot chainstate exists, it will always be our active.
@@ -5246,7 +5237,7 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
+CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash)
{
bool is_snapshot = !snapshot_blockhash.IsNull();
std::unique_ptr<CChainState>& to_modify =
@@ -5255,8 +5246,7 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
-
- to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
+ to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
diff --git a/src/validation.h b/src/validation.h
index 534162d64a..3d9fa92c15 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -93,8 +93,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to >= 550 MiB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
-/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
-static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
struct BlockHasher
{
@@ -113,7 +111,6 @@ enum class SynchronizationState {
extern RecursiveMutex cs_main;
extern CBlockPolicyEstimator feeEstimator;
-extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
@@ -160,7 +157,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Unload database information */
-void UnloadBlockIndex(CTxMemPool* mempool);
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
/** Run an instance of the script checking thread */
void ThreadScriptCheck(int worker_num);
/**
@@ -200,10 +197,11 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
void PruneBlockFilesManual(int nManualPruneHeight);
/** (try to) add transaction to memory pool
- * plTxnReplaced will be appended to with all transactions replaced from mempool **/
+ * plTxnReplaced will be appended to with all transactions replaced from mempool
+ * @param[out] fee_out optional argument to return tx fee to the caller **/
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
@@ -245,7 +243,7 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
/**
* Closure representing one script verification
@@ -306,9 +304,6 @@ bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainpar
* Note that transaction witness validation rules are always enforced when P2SH is enforced. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
-/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
-int GetWitnessCommitmentIndex(const CBlock& block);
-
/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
@@ -357,7 +352,31 @@ struct CBlockIndexWorkComparator
* This data is used mostly in `CChainState` - information about, e.g.,
* candidate tips is not maintained here.
*/
-class BlockManager {
+class BlockManager
+{
+ friend CChainState;
+
+private:
+ /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
+ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
+
+ /**
+ * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
+ * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
+ * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+ * (which in this case means the blockchain must be re-downloaded.)
+ *
+ * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+ * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+ * A db flag records the fact that at least some block files have been pruned.
+ *
+ * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
+ */
+ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd);
+
public:
BlockMap m_block_index GUARDED_BY(cs_main);
@@ -408,6 +427,9 @@ public:
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ //! Mark one block file as pruned (modify associated database entries)
+ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to m_block_index.
@@ -417,6 +439,10 @@ public:
BlockValidationState& state,
const CChainParams& chainparams,
CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ ~BlockManager() {
+ Unload();
+ }
};
/**
@@ -511,11 +537,14 @@ private:
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
+ //! mempool that is kept in sync with the chain
+ CTxMemPool& m_mempool;
+
//! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
std::unique_ptr<CoinsViews> m_coins_views;
public:
- explicit CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
+ explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -642,7 +671,7 @@ public:
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Apply the effects of a block disconnection on the UTXO set.
- bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
@@ -668,6 +697,9 @@ public:
*/
void CheckBlockIndex(const Consensus::Params& consensusParams);
+ /** Load the persisted mempool from disk */
+ void LoadMempool(const ArgsManager& args);
+
/** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */
bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -685,8 +717,8 @@ public:
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
- bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
- bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
+ bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -818,9 +850,11 @@ public:
//! Instantiate a new chainstate and assign it based upon whether it is
//! from a snapshot.
//!
+ //! @param[in] mempool The mempool to pass to the chainstate
+ // constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
- CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
+ CChainState& InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash = uint256())
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
@@ -892,9 +926,6 @@ public:
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
- //! Mark one block file as pruned (modify associated database entries)
- void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 3dfbcc581c..1e07ff23ae 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -199,18 +199,18 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
fInitialDownload);
}
-void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx) {
- auto event = [tx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx); });
+void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
+ auto event = [tx, mempool_sequence, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
tx->GetHash().ToString(),
tx->GetWitnessHash().ToString());
}
-void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
- auto event = [tx, reason, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason); });
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
+ auto event = [tx, reason, mempool_sequence, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
tx->GetHash().ToString(),
diff --git a/src/validationinterface.h b/src/validationinterface.h
index e96f2883fc..7c3ce00fbc 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -97,7 +97,8 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {}
+
/**
* Notifies listeners of a transaction leaving mempool.
*
@@ -130,7 +131,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
+ virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {}
/**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
@@ -197,8 +198,8 @@ public:
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
- void TransactionAddedToMempool(const CTransactionRef&);
- void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason);
+ void TransactionAddedToMempool(const CTransactionRef&, uint64_t mempool_sequence);
+ void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
diff --git a/src/version.h b/src/version.h
index b5f379e1b8..019c3a3ae7 100644
--- a/src/version.h
+++ b/src/version.h
@@ -38,4 +38,7 @@ static const int INVALID_CB_NO_BAN_VERSION = 70015;
//! "wtxidrelay" command for wtxid-based relay starts with this version
static const int WTXID_RELAY_VERSION = 70016;
+// Make sure that none of the values above collide with
+// `SERIALIZE_TRANSACTION_NO_WITNESS` or `ADDRV2_FORMAT`.
+
#endif // BITCOIN_VERSION_H
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
index 20297b9f9d..20dfc044ca 100644
--- a/src/versionbitsinfo.cpp
+++ b/src/versionbitsinfo.cpp
@@ -11,4 +11,8 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
},
+ {
+ /*.name =*/ "taproot",
+ /*.gbt_force =*/ true,
+ },
};
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 24eb2ee34c..85aae0170d 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -52,18 +52,6 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
return memcmp(value, &rhs.value, sizeof(value)) == 0;
}
-bool IsBDBWalletLoaded(const fs::path& wallet_path)
-{
- fs::path env_directory;
- std::string database_filename;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto env = g_dbenvs.find(env_directory.string());
- if (env == g_dbenvs.end()) return false;
- auto database = env->second.lock();
- return database && database->IsDatabaseLoaded(database_filename);
-}
-
/**
* @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
* @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
@@ -317,17 +305,16 @@ BerkeleyDatabase::~BerkeleyDatabase()
}
}
-BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database)
+BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database)
{
database.AddRef();
- database.Open(pszMode);
- fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
+ database.Open();
+ fReadOnly = read_only;
fFlushOnClose = fFlushOnCloseIn;
env = database.env.get();
pdb = database.m_db.get();
strFile = database.strFile;
- bool fCreate = strchr(pszMode, 'c') != nullptr;
- if (fCreate && !Exists(std::string("version"))) {
+ if (!Exists(std::string("version"))) {
bool fTmp = fReadOnly;
fReadOnly = false;
Write(std::string("version"), CLIENT_VERSION);
@@ -335,12 +322,9 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
}
}
-void BerkeleyDatabase::Open(const char* pszMode)
+void BerkeleyDatabase::Open()
{
- bool fCreate = strchr(pszMode, 'c') != nullptr;
- unsigned int nFlags = DB_THREAD;
- if (fCreate)
- nFlags |= DB_CREATE;
+ unsigned int nFlags = DB_THREAD | DB_CREATE;
{
LOCK(cs_db);
@@ -371,7 +355,6 @@ void BerkeleyDatabase::Open(const char* pszMode)
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
}
- m_file_path = (env->Directory() / strFile).string();
// Call CheckUniqueFileid on the containing BDB environment to
// avoid BDB data consistency bugs that happen when different data
@@ -481,7 +464,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {}
- BerkeleyBatch db(*this, "r");
+ BerkeleyBatch db(*this, true);
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
@@ -820,7 +803,64 @@ void BerkeleyDatabase::RemoveRef()
if (env) env->m_db_in_use.notify_all();
}
-std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(const char* mode, bool flush_on_close)
+std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
+{
+ return MakeUnique<BerkeleyBatch>(*this, false, flush_on_close);
+}
+
+bool ExistsBerkeleyDatabase(const fs::path& path)
+{
+ fs::path env_directory;
+ std::string data_filename;
+ SplitWalletPath(path, env_directory, data_filename);
+ return IsBDBFile(env_directory / data_filename);
+}
+
+std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
- return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
+ std::unique_ptr<BerkeleyDatabase> db;
+ {
+ LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
+ std::string data_filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(path, data_filename);
+ if (env->m_databases.count(data_filename)) {
+ error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string()));
+ status = DatabaseStatus::FAILED_ALREADY_LOADED;
+ return nullptr;
+ }
+ db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
+ }
+
+ if (options.verify && !db->Verify(error)) {
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
+ }
+
+ status = DatabaseStatus::SUCCESS;
+ return db;
+}
+
+bool IsBDBFile(const fs::path& path)
+{
+ if (!fs::exists(path)) return false;
+
+ // A Berkeley DB Btree file has at least 4K.
+ // This check also prevents opening lock files.
+ boost::system::error_code ec;
+ auto size = fs::file_size(path, ec);
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (size < 4096) return false;
+
+ fsbridge::ifstream file(path, std::ios::binary);
+ if (!file.is_open()) return false;
+
+ file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
+ uint32_t data = 0;
+ file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
+
+ // Berkeley DB Btree magic bytes, from:
+ // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
+ // - big endian systems - 00 05 31 62
+ // - little endian systems - 62 31 05 00
+ return data == 0x00053162 || data == 0x62310500;
}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 75546924e8..9073c1b6b3 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -63,7 +63,6 @@ public:
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
- bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; }
bool Open(bilingual_str& error);
@@ -87,8 +86,8 @@ public:
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
-/** Return whether a BDB wallet database is currently loaded. */
-bool IsBDBWalletLoaded(const fs::path& wallet_path);
+/** Check format of database file */
+bool IsBDBFile(const fs::path& path);
class BerkeleyBatch;
@@ -110,9 +109,8 @@ public:
~BerkeleyDatabase() override;
- /** Open the database if it is not already opened.
- * Dummy function, doesn't do anything right now, but is needed for class abstraction */
- void Open(const char* mode) override;
+ /** Open the database if it is not already opened. */
+ void Open() override;
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
*/
@@ -143,8 +141,12 @@ public:
void ReloadDbEnv() override;
/** Verifies the environment and database file */
- bool Verify(bilingual_str& error) override;
+ bool Verify(bilingual_str& error);
+ /** Return path to main database filename */
+ std::string Filename() override { return (env->Directory() / strFile).string(); }
+
+ std::string Format() override { return "bdb"; }
/**
* Pointer to shared database environment.
*
@@ -162,7 +164,7 @@ public:
std::string strFile;
/** Make a BerkeleyBatch connected to this database */
- std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override;
+ std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
};
/** RAII class that provides access to a Berkeley database */
@@ -205,7 +207,7 @@ protected:
BerkeleyDatabase& m_database;
public:
- explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
+ explicit BerkeleyBatch(BerkeleyDatabase& database, const bool fReadOnly, bool fFlushOnCloseIn=true);
~BerkeleyBatch() override;
BerkeleyBatch(const BerkeleyBatch&) = delete;
@@ -224,4 +226,10 @@ public:
std::string BerkeleyDatabaseVersion();
+//! Check if Berkeley database exists at specified path.
+bool ExistsBerkeleyDatabase(const fs::path& path);
+
+//! Return object giving access to Berkeley database at specified path.
+std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+
#endif // BITCOIN_WALLET_BDB_H
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 079a5d3d53..1a45a2b313 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -5,6 +5,7 @@
#include <wallet/coinselection.h>
#include <optional.h>
+#include <policy/feerate.h>
#include <util/system.h>
#include <util/moneystr.h>
@@ -302,7 +303,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) {
m_outputs.push_back(output);
m_from_me &= from_me;
- m_value += output.effective_value;
+ m_value += output.txout.nValue;
m_depth = std::min(m_depth, depth);
// ancestors here express the number of ancestors the new coin will end up having, which is
// the sum, rather than the max; this will overestimate in the cases where multiple inputs
@@ -311,15 +312,19 @@ void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size
// descendants is the count as seen from the top ancestor, not the descendants as seen from the
// coin itself; thus, this value is counted as the max, not the sum
m_descendants = std::max(m_descendants, descendants);
- effective_value = m_value;
+ effective_value += output.effective_value;
+ fee += output.m_fee;
+ long_term_fee += output.m_long_term_fee;
}
std::vector<CInputCoin>::iterator OutputGroup::Discard(const CInputCoin& output) {
auto it = m_outputs.begin();
while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it;
if (it == m_outputs.end()) return it;
- m_value -= output.effective_value;
+ m_value -= output.txout.nValue;
effective_value -= output.effective_value;
+ fee -= output.m_fee;
+ long_term_fee -= output.m_long_term_fee;
return m_outputs.erase(it);
}
@@ -329,3 +334,35 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f
&& m_ancestors <= eligibility_filter.max_ancestors
&& m_descendants <= eligibility_filter.max_descendants;
}
+
+void OutputGroup::SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate)
+{
+ fee = 0;
+ long_term_fee = 0;
+ effective_value = 0;
+ for (CInputCoin& coin : m_outputs) {
+ coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes);
+ fee += coin.m_fee;
+
+ coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
+ long_term_fee += coin.m_long_term_fee;
+
+ coin.effective_value = coin.txout.nValue - coin.m_fee;
+ effective_value += coin.effective_value;
+ }
+}
+
+OutputGroup OutputGroup::GetPositiveOnlyGroup()
+{
+ OutputGroup group(*this);
+ for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
+ const CInputCoin& coin = *it;
+ // Only include outputs that are positive effective value (i.e. not dust)
+ if (coin.effective_value <= 0) {
+ it = group.Discard(coin);
+ } else {
+ ++it;
+ }
+ }
+ return group;
+}
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 5348401f45..49c1134ec6 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -9,6 +9,8 @@
#include <primitives/transaction.h>
#include <random.h>
+class CFeeRate;
+
//! target minimum change amount
static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
@@ -36,6 +38,8 @@ public:
COutPoint outpoint;
CTxOut txout;
CAmount effective_value;
+ CAmount m_fee{0};
+ CAmount m_long_term_fee{0};
/** 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 m_input_bytes{-1};
@@ -91,6 +95,10 @@ struct OutputGroup
void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants);
std::vector<CInputCoin>::iterator Discard(const CInputCoin& output);
bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const;
+
+ //! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates
+ void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate);
+ OutputGroup GetPositiveOnlyGroup();
};
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);
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 1eb82a03c7..bd1d114730 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -23,11 +23,3 @@ void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::
database_filename = "wallet.dat";
}
}
-
-fs::path WalletDataFilePath(const fs::path& wallet_path)
-{
- fs::path env_directory;
- std::string database_filename;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- return env_directory / database_filename;
-}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 0afaba5fd1..940d1cd242 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -8,7 +8,9 @@
#include <clientversion.h>
#include <fs.h>
+#include <optional.h>
#include <streams.h>
+#include <support/allocators/secure.h>
#include <util/memory.h>
#include <atomic>
@@ -17,8 +19,6 @@
struct bilingual_str;
-/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
-fs::path WalletDataFilePath(const fs::path& wallet_path);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
/** RAII class that provides access to a WalletDatabase */
@@ -109,7 +109,7 @@ public:
virtual ~WalletDatabase() {};
/** Open the database if it is not already opened. */
- virtual void Open(const char* mode) = 0;
+ virtual void Open() = 0;
//! Counts the number of active database users to be sure that the database is not closed while someone is using it
std::atomic<int> m_refcount{0};
@@ -141,18 +141,18 @@ public:
virtual void ReloadDbEnv() = 0;
+ /** Return path to main database file for logs and error messages. */
+ virtual std::string Filename() = 0;
+
+ virtual std::string Format() = 0;
+
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
- /** Verifies the environment and database file */
- virtual bool Verify(bilingual_str& error) = 0;
-
- std::string m_file_path;
-
/** Make a DatabaseBatch connected to this database */
- virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0;
+ virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
};
/** RAII class that provides access to a DummyDatabase. Never fails. */
@@ -181,7 +181,7 @@ public:
class DummyDatabase : public WalletDatabase
{
public:
- void Open(const char* mode) override {};
+ void Open() override {};
void AddRef() override {}
void RemoveRef() override {}
bool Rewrite(const char* pszSkip=nullptr) override { return true; }
@@ -191,8 +191,38 @@ public:
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
- bool Verify(bilingual_str& errorStr) override { return true; }
- std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
+ std::string Filename() override { return "dummy"; }
+ std::string Format() override { return "dummy"; }
+ std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
+};
+
+enum class DatabaseFormat {
+ BERKELEY,
+ SQLITE,
};
+struct DatabaseOptions {
+ bool require_existing = false;
+ bool require_create = false;
+ Optional<DatabaseFormat> require_format;
+ uint64_t create_flags = 0;
+ SecureString create_passphrase;
+ bool verify = true;
+};
+
+enum class DatabaseStatus {
+ SUCCESS,
+ FAILED_BAD_PATH,
+ FAILED_BAD_FORMAT,
+ FAILED_ALREADY_LOADED,
+ FAILED_ALREADY_EXISTS,
+ FAILED_NOT_FOUND,
+ FAILED_CREATE,
+ FAILED_LOAD,
+ FAILED_VERIFY,
+ FAILED_ENCRYPT,
+};
+
+std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index cacf306891..6cbad14de8 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -219,7 +219,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
CAmount fee_ret;
int change_pos_in_out = -1; // No requested location for change
bilingual_str fail_reason;
- if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ FeeCalculation fee_calc_out;
+ if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 52162ab521..5d8c4fba29 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,10 +5,12 @@
#include <init.h>
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <net.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <outputtype.h>
+#include <univalue.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <util/system.h>
@@ -48,6 +50,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
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)",
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)",
@@ -64,13 +67,13 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
- " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
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"});
}
bool WalletInit::ParameterInteraction() const
@@ -83,26 +86,12 @@ bool WalletInit::ParameterInteraction() const
return true;
}
- const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
-
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
- // -zapwallettxes implies dropping the mempool on startup
- if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
- }
-
- // -zapwallettxes implies a rescan
- if (zapwallettxes) {
- if (is_multiwallet) {
- return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-zapwallettxes"));
- }
- if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
- }
+ if (gArgs.IsArgSet("-zapwallettxes")) {
+ return InitError(Untranslated("-zapwallettxes has been removed. If you are attempting to remove a stuck transaction from your wallet, please use abandontransaction instead."));
}
if (gArgs.GetBoolArg("-sysperms", false))
@@ -118,6 +107,7 @@ void WalletInit::Construct(NodeContext& node) const
LogPrintf("Wallet disabled!\n");
return;
}
- args.SoftSetArg("-wallet", "");
- node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet")));
+ auto wallet_client = interfaces::MakeWalletClient(*node.chain, args);
+ node.wallet_client = wallet_client.get();
+ node.chain_clients.emplace_back(std::move(wallet_client));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 2a81d30133..1b057000d2 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -5,6 +5,7 @@
#include <wallet/load.h>
+#include <fs.h>
#include <interfaces/chain.h>
#include <scheduler.h>
#include <util/string.h>
@@ -13,7 +14,9 @@
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+#include <univalue.h>
+
+bool VerifyWallets(interfaces::Chain& chain)
{
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
@@ -38,22 +41,39 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
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.
+ if (!gArgs.IsArgSet("wallet")) {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ bilingual_str error_string;
+ options.require_existing = true;
+ options.verify = false;
+ if (MakeWalletDatabase("", options, status, error_string)) {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ util::SettingsValue wallets(util::SettingsValue::VARR);
+ wallets.push_back(""); // Default wallet name is ""
+ settings.rw_settings["wallet"] = wallets;
+ });
+ }
+ }
+
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
- for (const auto& wallet_file : wallet_files) {
- WalletLocation location(wallet_file);
+ for (const auto& wallet_file : gArgs.GetArgs("-wallet")) {
+ const fs::path path = fs::absolute(wallet_file, GetWalletDir());
- if (!wallet_paths.insert(location.GetPath()).second) {
+ if (!wallet_paths.insert(path).second) {
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
return false;
}
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.verify = true;
bilingual_str error_string;
- std::vector<bilingual_str> warnings;
- bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
- if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
- if (!verify_success) {
+ if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
chain.initError(error_string);
return false;
}
@@ -62,13 +82,17 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
return true;
}
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+bool LoadWallets(interfaces::Chain& chain)
{
try {
- for (const std::string& walletFile : wallet_files) {
+ for (const std::string& name : gArgs.GetArgs("-wallet")) {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings);
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ 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);
@@ -116,7 +140,8 @@ void UnloadWallets()
while (!wallets.empty()) {
auto wallet = wallets.back();
wallets.pop_back();
- RemoveWallet(wallet);
+ std::vector<bilingual_str> warnings;
+ RemoveWallet(wallet, nullopt, warnings);
UnloadWallet(std::move(wallet));
}
}
diff --git a/src/wallet/load.h b/src/wallet/load.h
index ff4f5b4b23..e12343de27 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -17,10 +17,10 @@ class Chain;
} // namespace interfaces
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+bool VerifyWallets(interfaces::Chain& chain);
//! Load wallet databases.
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+bool LoadWallets(interfaces::Chain& chain);
//! Complete startup of wallets.
void StartWallets(CScheduler& scheduler, const ArgsManager& args);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e0c3a1287a..884ab58497 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -90,9 +90,9 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
}
}
-UniValue importprivkey(const JSONRPCRequest& request)
+RPCHelpMan importprivkey()
{
- RPCHelpMan{"importprivkey",
+ return RPCHelpMan{"importprivkey",
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -116,8 +116,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -189,11 +189,13 @@ UniValue importprivkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue abortrescan(const JSONRPCRequest& request)
+RPCHelpMan abortrescan()
{
- RPCHelpMan{"abortrescan",
+ return RPCHelpMan{"abortrescan",
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
@@ -206,8 +208,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("abortrescan", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -215,11 +217,13 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
+},
+ };
}
-UniValue importaddress(const JSONRPCRequest& request)
+RPCHelpMan importaddress()
{
- RPCHelpMan{"importaddress",
+ return RPCHelpMan{"importaddress",
"\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
@@ -243,8 +247,8 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -315,11 +319,13 @@ UniValue importaddress(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan importprunedfunds()
{
- RPCHelpMan{"importprunedfunds",
+ return RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
{"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
@@ -327,15 +333,16 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
CMutableTransaction tx;
- if (!DecodeHexTx(tx, request.params[0].get_str()))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ if (!DecodeHexTx(tx, request.params[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
+ }
uint256 hashTx = tx.GetHash();
CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
@@ -371,11 +378,13 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
+},
+ };
}
-UniValue removeprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan removeprunedfunds()
{
- RPCHelpMan{"removeprunedfunds",
+ return RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
@@ -386,8 +395,8 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -408,11 +417,13 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importpubkey(const JSONRPCRequest& request)
+RPCHelpMan importpubkey()
{
- RPCHelpMan{"importpubkey",
+ return RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -432,8 +443,8 @@ UniValue importpubkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -492,12 +503,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importwallet(const JSONRPCRequest& request)
+RPCHelpMan importwallet()
{
- RPCHelpMan{"importwallet",
+ return RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
@@ -512,8 +525,8 @@ UniValue importwallet(const JSONRPCRequest& request)
"\nImport using the json rpc call\n"
+ HelpExampleRpc("importwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -649,11 +662,13 @@ UniValue importwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
return NullUniValue;
+},
+ };
}
-UniValue dumpprivkey(const JSONRPCRequest& request)
+RPCHelpMan dumpprivkey()
{
- RPCHelpMan{"dumpprivkey",
+ return RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
@@ -667,8 +682,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"")
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -693,12 +708,14 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
return EncodeSecret(vchSecret);
+},
+ };
}
-UniValue dumpwallet(const JSONRPCRequest& request)
+RPCHelpMan dumpwallet()
{
- RPCHelpMan{"dumpwallet",
+ return RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
"Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
@@ -716,8 +733,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
HelpExampleCli("dumpwallet", "\"test\"")
+ HelpExampleRpc("dumpwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
@@ -829,6 +846,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
reply.pushKV("filename", filepath.string());
return reply;
+},
+ };
}
struct ImportData
@@ -914,6 +933,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
return "unspendable script";
case TxoutType::NONSTANDARD:
case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT:
default:
return "unrecognized script";
}
@@ -1239,9 +1259,9 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
}
-UniValue importmulti(const JSONRPCRequest& mainRequest)
+RPCHelpMan importmulti()
{
- RPCHelpMan{"importmulti",
+ return RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
@@ -1314,8 +1334,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
},
- }.Check(mainRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1423,6 +1443,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
return response;
+},
+ };
}
static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
@@ -1564,9 +1586,9 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request)
+RPCHelpMan importdescriptors()
{
- RPCHelpMan{"importdescriptors",
+ return RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
@@ -1615,8 +1637,8 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
"{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
- }.Check(main_request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1713,4 +1735,6 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
}
return response;
+},
+ };
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 58eaf54175..29b55ec130 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -11,6 +11,7 @@
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
+#include <policy/policy.h>
#include <policy/rbf.h>
#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
@@ -30,6 +31,7 @@
#include <wallet/coincontrol.h>
#include <wallet/context.h>
#include <wallet/feebumper.h>
+#include <wallet/load.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -110,7 +112,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
if (wallets.empty()) {
throw JSONRPCError(
- RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
+ RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)");
}
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
@@ -229,9 +231,9 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
}
}
-static UniValue getnewaddress(const JSONRPCRequest& request)
+static RPCHelpMan getnewaddress()
{
- RPCHelpMan{"getnewaddress",
+ return RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with 'label'.\n",
@@ -246,8 +248,8 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
HelpExampleCli("getnewaddress", "")
+ HelpExampleRpc("getnewaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -277,11 +279,13 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
}
return EncodeDestination(dest);
+},
+ };
}
-static UniValue getrawchangeaddress(const JSONRPCRequest& request)
+static RPCHelpMan getrawchangeaddress()
{
- RPCHelpMan{"getrawchangeaddress",
+ return RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
{
@@ -294,8 +298,8 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
HelpExampleCli("getrawchangeaddress", "")
+ HelpExampleRpc("getrawchangeaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -319,12 +323,14 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
}
return EncodeDestination(dest);
+},
+ };
}
-static UniValue setlabel(const JSONRPCRequest& request)
+static RPCHelpMan setlabel()
{
- RPCHelpMan{"setlabel",
+ return RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
@@ -335,8 +341,8 @@ static UniValue setlabel(const JSONRPCRequest& request)
HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -357,6 +363,8 @@ static UniValue setlabel(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
@@ -389,7 +397,7 @@ void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_f
}
}
-UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value)
+UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
{
EnsureWalletIsUnlocked(pwallet);
@@ -401,38 +409,55 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
int nChangePosRet = -1;
bilingual_str error;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ FeeCalculation fee_calc_out;
+ bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ if (verbose) {
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("txid", tx->GetHash().GetHex());
+ entry.pushKV("fee_reason", StringForFeeReason(fee_calc_out.reason));
+ return entry;
+ }
return tx->GetHash().GetHex();
}
-static UniValue sendtoaddress(const JSONRPCRequest& request)
+static RPCHelpMan sendtoaddress()
{
- RPCHelpMan{"sendtoaddress",
+ return RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HELP_REQUIRING_PASSPHRASE,
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to send to."},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
- " This is not part of the transaction, just kept in your wallet."},
+ "This is not part of the transaction, just kept in your wallet."},
{"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."},
+ "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"
- " The recipient will receive less bitcoins than you enter in the amount field."},
+ "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 default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* 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."},
+ "dirty if they have previously been used in a transaction."},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra information about the transaction."},
},
- RPCResult{
- RPCResult::Type::STR_HEX, "txid", "The transaction id."
+ {
+ RPCResult{"if verbose is not set or set to false",
+ RPCResult::Type::STR_HEX, "txid", "The transaction id."
+ },
+ RPCResult{"if verbose is set to true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ },
+ },
},
RPCExamples{
HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1")
@@ -442,8 +467,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
+ HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -489,13 +514,16 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
std::vector<CRecipient> recipients;
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
+ bool verbose = request.params[9].isNull() ? false: request.params[9].get_bool();
- return SendMoney(pwallet, coin_control, recipients, mapValue);
+ return SendMoney(pwallet, coin_control, recipients, mapValue, verbose);
+},
+ };
}
-static UniValue listaddressgroupings(const JSONRPCRequest& request)
+static RPCHelpMan listaddressgroupings()
{
- RPCHelpMan{"listaddressgroupings",
+ return RPCHelpMan{"listaddressgroupings",
"\nLists groups of addresses which have had their common ownership\n"
"made public by common use as inputs or as the resulting change\n"
"in past transactions\n",
@@ -518,8 +546,8 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
HelpExampleCli("listaddressgroupings", "")
+ HelpExampleRpc("listaddressgroupings", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -550,11 +578,13 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
+},
+ };
}
-static UniValue signmessage(const JSONRPCRequest& request)
+static RPCHelpMan signmessage()
{
- RPCHelpMan{"signmessage",
+ return RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HELP_REQUIRING_PASSPHRASE,
{
@@ -574,8 +604,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -606,6 +636,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
}
return signature;
+},
+ };
}
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -654,9 +686,9 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
}
-static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+static RPCHelpMan getreceivedbyaddress()
{
- RPCHelpMan{"getreceivedbyaddress",
+ return 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."},
@@ -675,8 +707,8 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -688,12 +720,14 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
+},
+ };
}
-static UniValue getreceivedbylabel(const JSONRPCRequest& request)
+static RPCHelpMan getreceivedbylabel()
{
- RPCHelpMan{"getreceivedbylabel",
+ return 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 \"\"."},
@@ -712,8 +746,8 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -725,12 +759,14 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
+},
+ };
}
-static UniValue getbalance(const JSONRPCRequest& request)
+static RPCHelpMan getbalance()
{
- RPCHelpMan{"getbalance",
+ return RPCHelpMan{"getbalance",
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
"thus affected by options which limit spendability such as -spendzeroconfchange.\n",
@@ -751,8 +787,8 @@ static UniValue getbalance(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -780,17 +816,19 @@ static UniValue getbalance(const JSONRPCRequest& request)
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
+},
+ };
}
-static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
+static RPCHelpMan getunconfirmedbalance()
{
- RPCHelpMan{"getunconfirmedbalance",
+ return RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
RPCResult{RPCResult::Type::NUM, "", "The balance"},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -802,12 +840,14 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
+},
+ };
}
-static UniValue sendmany(const JSONRPCRequest& request)
+static RPCHelpMan sendmany()
{
- RPCHelpMan{"sendmany",
+ return RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
{
@@ -820,9 +860,9 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
- " The fee will be equally deducted from the amount of each selected address.\n"
- " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no addresses are specified here, the sender pays the fee.",
+ "The fee will be equally deducted from the amount of each selected address.\n"
+ "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ "If no addresses are specified here, the sender pays the fee.",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
@@ -831,11 +871,22 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra infomration about the transaction."},
+ },
+ {
+ RPCResult{"if verbose is not set or set to false",
+ RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."
+ },
+ RPCResult{"if verbose is set to true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."},
+ {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ },
+ },
},
- RPCResult{
- RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
- "the number of addresses."
- },
RPCExamples{
"\nSend two amounts to two different addresses:\n"
+ HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\"") +
@@ -846,8 +897,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"" + EXAMPLE_ADDRESS[0] + "\":0.01,\"" + EXAMPLE_ADDRESS[1] + "\":0.02}, 6, \"testing\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -880,13 +931,17 @@ static UniValue sendmany(const JSONRPCRequest& request)
std::vector<CRecipient> recipients;
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
+ bool verbose = request.params[8].isNull() ? false : request.params[8].get_bool();
- return SendMoney(pwallet, coin_control, recipients, std::move(mapValue));
+ return SendMoney(pwallet, coin_control, recipients, std::move(mapValue), verbose);
+},
+ };
}
-static UniValue addmultisigaddress(const JSONRPCRequest& request)
+
+static RPCHelpMan addmultisigaddress()
{
- RPCHelpMan{"addmultisigaddress",
+ return RPCHelpMan{"addmultisigaddress",
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
@@ -916,8 +971,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -963,6 +1018,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
+},
+ };
}
struct tallyitem
@@ -1123,9 +1180,9 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
return ret;
}
-static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+static RPCHelpMan listreceivedbyaddress()
{
- 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."},
@@ -1156,8 +1213,8 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1169,11 +1226,13 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, false);
+},
+ };
}
-static UniValue listreceivedbylabel(const JSONRPCRequest& request)
+static RPCHelpMan listreceivedbylabel()
{
- 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."},
@@ -1197,8 +1256,8 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("listreceivedbylabel", "6 true")
+ HelpExampleRpc("listreceivedbylabel", "6, true, true")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1210,6 +1269,8 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, true);
+},
+ };
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1329,9 +1390,9 @@ static const std::vector<RPCResult> TransactionDescriptionString()
"may be unknown for unconfirmed transactions not in the mempool"}};
}
-UniValue listtransactions(const JSONRPCRequest& request)
+static RPCHelpMan listtransactions()
{
- RPCHelpMan{"listtransactions",
+ return RPCHelpMan{"listtransactions",
"\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
{
@@ -1376,8 +1437,8 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1437,11 +1498,13 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue result{UniValue::VARR};
result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
return result;
+},
+ };
}
-static UniValue listsinceblock(const JSONRPCRequest& request)
+static RPCHelpMan listsinceblock()
{
- RPCHelpMan{"listsinceblock",
+ return RPCHelpMan{"listsinceblock",
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
"If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
"Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
@@ -1450,7 +1513,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
{"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"
- " (not guaranteed to work on pruned nodes)"},
+ "(not guaranteed to work on pruned nodes)"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1484,7 +1547,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
{RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
"Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
, {{RPCResult::Type::ELISION, "", ""},}},
- {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
+ {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
}
},
RPCExamples{
@@ -1492,8 +1555,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
@@ -1567,6 +1630,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
}
uint256 lastblock;
+ target_confirms = std::min(target_confirms, wallet.GetLastBlockHeight() + 1);
CHECK_NONFATAL(wallet.chain().findAncestorByHeight(wallet.GetLastBlockHash(), wallet.GetLastBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
UniValue ret(UniValue::VOBJ);
@@ -1575,11 +1639,13 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
ret.pushKV("lastblock", lastblock.GetHex());
return ret;
+},
+ };
}
-static UniValue gettransaction(const JSONRPCRequest& request)
+static RPCHelpMan gettransaction()
{
- RPCHelpMan{"gettransaction",
+ return RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
@@ -1631,8 +1697,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1685,11 +1751,13 @@ static UniValue gettransaction(const JSONRPCRequest& request)
}
return entry;
+},
+ };
}
-static UniValue abandontransaction(const JSONRPCRequest& request)
+static RPCHelpMan abandontransaction()
{
- RPCHelpMan{"abandontransaction",
+ return RPCHelpMan{"abandontransaction",
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
"for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
@@ -1703,8 +1771,8 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1725,12 +1793,14 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue backupwallet(const JSONRPCRequest& request)
+static RPCHelpMan backupwallet()
{
- RPCHelpMan{"backupwallet",
+ return RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
{"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
@@ -1740,8 +1810,8 @@ static UniValue backupwallet(const JSONRPCRequest& request)
HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1758,12 +1828,14 @@ static UniValue backupwallet(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue keypoolrefill(const JSONRPCRequest& request)
+static RPCHelpMan keypoolrefill()
{
- RPCHelpMan{"keypoolrefill",
+ return RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
{
@@ -1774,8 +1846,8 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1802,12 +1874,14 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue walletpassphrase(const JSONRPCRequest& request)
+static RPCHelpMan walletpassphrase()
{
- RPCHelpMan{"walletpassphrase",
+ return RPCHelpMan{"walletpassphrase",
"\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
"\nNote:\n"
@@ -1826,8 +1900,8 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1896,12 +1970,14 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
}, nSleepTime);
return NullUniValue;
+},
+ };
}
-static UniValue walletpassphrasechange(const JSONRPCRequest& request)
+static RPCHelpMan walletpassphrasechange()
{
- RPCHelpMan{"walletpassphrasechange",
+ return RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
{"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
@@ -1912,8 +1988,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1943,12 +2019,14 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue walletlock(const JSONRPCRequest& request)
+static RPCHelpMan walletlock()
{
- RPCHelpMan{"walletlock",
+ return RPCHelpMan{"walletlock",
"\nRemoves the wallet encryption key from memory, locking the wallet.\n"
"After calling this method, you will need to call walletpassphrase again\n"
"before being able to call any methods which require the wallet to be unlocked.\n",
@@ -1964,8 +2042,8 @@ static UniValue walletlock(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1980,12 +2058,14 @@ static UniValue walletlock(const JSONRPCRequest& request)
pwallet->nRelockTime = 0;
return NullUniValue;
+},
+ };
}
-static UniValue encryptwallet(const JSONRPCRequest& request)
+static RPCHelpMan encryptwallet()
{
- RPCHelpMan{"encryptwallet",
+ return RPCHelpMan{"encryptwallet",
"\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
"After this, any calls that interact with private keys such as sending or signing \n"
"will require the passphrase to be set prior the making these calls.\n"
@@ -2007,8 +2087,8 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2038,15 +2118,18 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
}
return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
+},
+ };
}
-static UniValue lockunspent(const JSONRPCRequest& request)
+static RPCHelpMan lockunspent()
{
- RPCHelpMan{"lockunspent",
+ return RPCHelpMan{"lockunspent",
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
+ "Manually selected coins are automatically unlocked.\n"
"Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops or fails.\n"
"Also see the listunspent call\n",
@@ -2078,8 +2161,8 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2121,7 +2204,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
const uint256 txid(ParseHashO(o, "txid"));
const int nOutput = find_value(o, "vout").get_int();
if (nOutput < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
}
const COutPoint outpt(txid, nOutput);
@@ -2161,11 +2244,13 @@ static UniValue lockunspent(const JSONRPCRequest& request)
}
return true;
+},
+ };
}
-static UniValue listlockunspent(const JSONRPCRequest& request)
+static RPCHelpMan listlockunspent()
{
- RPCHelpMan{"listlockunspent",
+ return RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
{},
@@ -2191,8 +2276,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2213,11 +2298,13 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue settxfee(const JSONRPCRequest& request)
+static RPCHelpMan settxfee()
{
- RPCHelpMan{"settxfee",
+ return RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB 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",
{
@@ -2230,8 +2317,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
HelpExampleCli("settxfee", "0.00001")
+ HelpExampleRpc("settxfee", "0.00001")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2253,11 +2340,13 @@ static UniValue settxfee(const JSONRPCRequest& request)
pwallet->m_pay_tx_fee = tx_fee_rate;
return true;
+},
+ };
}
-static UniValue getbalances(const JSONRPCRequest& request)
+static RPCHelpMan getbalances()
{
- RPCHelpMan{
+ return RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
@@ -2282,8 +2371,8 @@ static UniValue getbalances(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
if (!rpc_wallet) return NullUniValue;
CWallet& wallet = *rpc_wallet;
@@ -2318,11 +2407,13 @@ static UniValue getbalances(const JSONRPCRequest& request)
balances.pushKV("watchonly", balances_watchonly);
}
return balances;
+},
+ };
}
-static UniValue getwalletinfo(const JSONRPCRequest& request)
+static RPCHelpMan getwalletinfo()
{
- RPCHelpMan{"getwalletinfo",
+ return RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
@@ -2331,6 +2422,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
{
{RPCResult::Type::STR, "walletname", "the wallet name"},
{RPCResult::Type::NUM, "walletversion", "the wallet version"},
+ {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
{RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
{RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
{RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
@@ -2355,8 +2447,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
HelpExampleCli("getwalletinfo", "")
+ HelpExampleRpc("getwalletinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2374,6 +2466,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
int64_t kp_oldest = pwallet->GetOldestKeyPoolTime();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
+ obj.pushKV("format", pwallet->GetDatabase().Format());
obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
@@ -2410,11 +2503,13 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
}
obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
return obj;
+},
+ };
}
-static UniValue listwalletdir(const JSONRPCRequest& request)
+static RPCHelpMan listwalletdir()
{
- RPCHelpMan{"listwalletdir",
+ return RPCHelpMan{"listwalletdir",
"Returns a list of wallets in the wallet directory.\n",
{},
RPCResult{
@@ -2433,8 +2528,8 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
HelpExampleCli("listwalletdir", "")
+ HelpExampleRpc("listwalletdir", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue wallets(UniValue::VARR);
for (const auto& path : ListWalletDir()) {
UniValue wallet(UniValue::VOBJ);
@@ -2445,11 +2540,13 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("wallets", wallets);
return result;
+},
+ };
}
-static UniValue listwallets(const JSONRPCRequest& request)
+static RPCHelpMan listwallets()
{
- RPCHelpMan{"listwallets",
+ return RPCHelpMan{"listwallets",
"Returns a list of currently loaded wallets.\n"
"For full information on the wallet, use \"getwalletinfo\"\n",
{},
@@ -2463,8 +2560,8 @@ static UniValue listwallets(const JSONRPCRequest& request)
HelpExampleCli("listwallets", "")
+ HelpExampleRpc("listwallets", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
@@ -2473,16 +2570,19 @@ static UniValue listwallets(const JSONRPCRequest& request)
}
return obj;
+},
+ };
}
-static UniValue loadwallet(const JSONRPCRequest& request)
+static RPCHelpMan loadwallet()
{
- RPCHelpMan{"loadwallet",
+ return RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting bitcoind will be"
- "\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n",
+ "\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."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2495,40 +2595,42 @@ static UniValue loadwallet(const JSONRPCRequest& request)
HelpExampleCli("loadwallet", "\"test.dat\"")
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
WalletContext& context = EnsureWalletContext(request.context);
- WalletLocation location(request.params[0].get_str());
-
- if (!location.Exists()) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
- } else if (fs::is_directory(location.GetPath())) {
- // The given filename is a directory. Check that there's a wallet.dat file.
- fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
- if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
- }
- }
+ const std::string name(request.params[0].get_str());
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, error, warnings);
- if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
+ Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
+ if (!wallet) {
+ // Map bad format to not found, since bad format is returned when the
+ // wallet directory exists, but doesn't contain a data file.
+ RPCErrorCode code = status == DatabaseStatus::FAILED_NOT_FOUND || status == DatabaseStatus::FAILED_BAD_FORMAT ? RPC_WALLET_NOT_FOUND : RPC_WALLET_ERROR;
+ throw JSONRPCError(code, error.original);
+ }
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return obj;
+},
+ };
}
-static UniValue setwalletflag(const JSONRPCRequest& request)
+static RPCHelpMan setwalletflag()
{
std::string flags = "";
for (auto& it : WALLET_FLAG_MAP)
if (it.second & MUTABLE_WALLET_FLAGS)
flags += (flags == "" ? "" : ", ") + it.first;
- RPCHelpMan{"setwalletflag",
+
+ return 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},
@@ -2546,8 +2648,8 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
HelpExampleCli("setwalletflag", "avoid_reuse")
+ HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2585,11 +2687,13 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
-static UniValue createwallet(const JSONRPCRequest& request)
+static RPCHelpMan createwallet()
{
- RPCHelpMan{
+ return RPCHelpMan{
"createwallet",
"\nCreates and loads a new wallet.\n",
{
@@ -2599,6 +2703,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
{"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."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2611,8 +2716,8 @@ static UniValue createwallet(const JSONRPCRequest& request)
HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
WalletContext& context = EnsureWalletContext(request.context);
uint64_t flags = 0;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
@@ -2641,17 +2746,17 @@ static UniValue createwallet(const JSONRPCRequest& request)
warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_create = true;
+ options.create_flags = flags;
+ options.create_passphrase = passphrase;
bilingual_str error;
- std::shared_ptr<CWallet> wallet;
- WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
- switch (status) {
- case WalletCreationStatus::CREATION_FAILED:
- throw JSONRPCError(RPC_WALLET_ERROR, error.original);
- case WalletCreationStatus::ENCRYPTION_FAILED:
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error.original);
- case WalletCreationStatus::SUCCESS:
- break;
- // no default case, so the compiler can warn about missing cases
+ Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool());
+ std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
+ if (!wallet) {
+ RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
+ throw JSONRPCError(code, error.original);
}
UniValue obj(UniValue::VOBJ);
@@ -2659,23 +2764,28 @@ static UniValue createwallet(const JSONRPCRequest& request)
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return obj;
+},
+ };
}
-static UniValue unloadwallet(const JSONRPCRequest& request)
+static RPCHelpMan unloadwallet()
{
- RPCHelpMan{"unloadwallet",
+ return 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 request", "The name of the wallet to unload."},
+ {"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."},
},
- RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCResult{RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
+ }},
RPCExamples{
HelpExampleCli("unloadwallet", "wallet_name")
+ HelpExampleRpc("unloadwallet", "wallet_name")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
if (!request.params[0].isNull()) {
@@ -2693,18 +2803,24 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
// Release the "main" shared pointer and prevent further notifications.
// Note that any attempt to load the same wallet would fail until the wallet
// is destroyed (see CheckUniqueFileid).
- if (!RemoveWallet(wallet)) {
+ std::vector<bilingual_str> warnings;
+ Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ if (!RemoveWallet(wallet, load_on_start, warnings)) {
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
}
UnloadWallet(std::move(wallet));
- return NullUniValue;
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
+ return result;
+},
+ };
}
-static UniValue listunspent(const JSONRPCRequest& request)
+static RPCHelpMan listunspent()
{
- RPCHelpMan{
+ return RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
@@ -2718,7 +2834,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
},
},
{"include_unsafe", RPCArg::Type::BOOL, /* default */ "true", "Include outputs that are not safe to spend\n"
- " See description of \"safe\" attribute below."},
+ "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 + ""},
@@ -2759,8 +2875,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2807,6 +2923,15 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (!request.params[4].isNull()) {
const UniValue& options = request.params[4].get_obj();
+ RPCTypeCheckObj(options,
+ {
+ {"minimumAmount", UniValueType()},
+ {"maximumAmount", UniValueType()},
+ {"minimumSumAmount", UniValueType()},
+ {"maximumCount", UniValueType(UniValue::VNUM)},
+ },
+ true, true);
+
if (options.exists("minimumAmount"))
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
@@ -2912,6 +3037,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
}
return results;
+},
+ };
}
void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl)
@@ -2935,13 +3062,22 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
RPCTypeCheckObj(options,
{
{"add_inputs", UniValueType(UniValue::VBOOL)},
+ {"add_to_wallet", UniValueType(UniValue::VBOOL)},
{"changeAddress", UniValueType(UniValue::VSTR)},
+ {"change_address", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
+ {"change_position", UniValueType(UniValue::VNUM)},
{"change_type", UniValueType(UniValue::VSTR)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
+ {"include_watching", UniValueType(UniValue::VBOOL)},
+ {"inputs", UniValueType(UniValue::VARR)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
+ {"lock_unspents", UniValueType(UniValue::VBOOL)},
+ {"locktime", UniValueType(UniValue::VNUM)},
{"feeRate", UniValueType()}, // will be checked below
+ {"psbt", UniValueType(UniValue::VBOOL)},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
+ {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
{"conf_target", UniValueType(UniValue::VNUM)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
@@ -2952,22 +3088,24 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.m_add_inputs = options["add_inputs"].get_bool();
}
- if (options.exists("changeAddress")) {
- CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
+ if (options.exists("changeAddress") || options.exists("change_address")) {
+ const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
+ CTxDestination dest = DecodeDestination(change_address_str);
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid bitcoin address");
}
coinControl.destChange = dest;
}
- if (options.exists("changePosition"))
- change_position = options["changePosition"].get_int();
+ if (options.exists("changePosition") || options.exists("change_position")) {
+ change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).get_int();
+ }
if (options.exists("change_type")) {
- if (options.exists("changeAddress")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
+ if (options.exists("changeAddress") || options.exists("change_address")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
}
OutputType out_type;
if (!ParseOutputType(options["change_type"].get_str(), out_type)) {
@@ -2976,10 +3114,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.m_change_type.emplace(out_type);
}
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
+ const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, *pwallet);
- if (options.exists("lockUnspents"))
- lockUnspents = options["lockUnspents"].get_bool();
+ if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
+ lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
+ }
if (options.exists("feeRate"))
{
@@ -2993,8 +3133,8 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.fOverrideFeeRate = true;
}
- if (options.exists("subtractFeeFromOutputs"))
- subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
+ if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
+ subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
@@ -3030,9 +3170,9 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
-static UniValue fundrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan fundrawtransaction()
{
- RPCHelpMan{"fundrawtransaction",
+ return RPCHelpMan{"fundrawtransaction",
"\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
"It will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@@ -3058,15 +3198,15 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "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.",
+ "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.",
{
{"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"
- " Allows this transaction to be replaced by a transaction with higher fees"},
+ "Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
@@ -3098,8 +3238,8 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3127,11 +3267,13 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
result.pushKV("changepos", change_position);
return result;
+},
+ };
}
-UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
+RPCHelpMan signrawtransactionwithwallet()
{
- RPCHelpMan{"signrawtransactionwithwallet",
+ return RPCHelpMan{"signrawtransactionwithwallet",
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain." +
@@ -3165,7 +3307,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{
{RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
- {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
+ {RPCResult::Type::ARR, "errors", /* optional */ true, "Script verification errors (if there are any)",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -3182,8 +3324,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
+ HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3191,8 +3333,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
- if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
+ if (!DecodeHexTx(mtx, request.params[0].get_str())) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
}
// Sign the transaction
@@ -3218,63 +3360,79 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
return result;
+},
+ };
}
-static UniValue bumpfee(const JSONRPCRequest& request)
+static RPCHelpMan bumpfee_helper(std::string method_name)
{
- RPCHelpMan{"bumpfee",
- "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
- "An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will 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.\n"
- "All inputs in the original transaction will be included in the replacement transaction.\n"
- "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
- "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
- "The user can specify a confirmation target for estimatesmartfee.\n"
- "Alternatively, the user can specify a fee_rate (" + CURRENCY_UNIT + " per kB) for the new transaction.\n"
- "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
- "returned by getnetworkinfo) to enter the node's mempool.\n",
+ bool want_psbt = method_name == "psbtbumpfee";
+
+ return RPCHelpMan{method_name,
+ "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
+ + std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
+ "An opt-in RBF transaction with the given txid must be in the wallet.\n"
+ "The command will 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.\n"
+ "All inputs in the original transaction will be included in the replacement transaction.\n"
+ "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
+ "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
+ "The user can specify a confirmation target for estimatesmartfee.\n"
+ "Alternatively, the user can specify a fee_rate (" + CURRENCY_UNIT + " per kB) for the new transaction.\n"
+ "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
+ "returned by getnetworkinfo) to enter the node's mempool.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"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 default", "Confirmation target (in blocks)"},
- {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
- " Specify a fee rate instead of relying on the built-in fee estimator.\n"
- "Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
- {"replaceable", RPCArg::Type::BOOL, /* 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)."},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
- " \"" + FeeModes("\"\n\"") + "\""},
- },
- "options"},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "", {
- {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
- {RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
- {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
- {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
- {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
- {
- {RPCResult::Type::STR, "", ""},
- }},
- }
- },
- RPCExamples{
- "\nBump the fee, get the new transaction\'s txid\n" +
- HelpExampleCli("bumpfee", "<txid>")
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
+ {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
+ "Specify a fee rate instead of relying on the built-in fee estimator.\n"
+ "Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
+ {"replaceable", RPCArg::Type::BOOL, /* 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)."},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
- }.Check(request);
-
+ "options"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction." + std::string(want_psbt ? "" : " Only returned when wallet private keys are disabled. (DEPRECATED)")},
+ },
+ 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::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
+ {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ })
+ },
+ RPCExamples{
+ "\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) mutable -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
+ if (!pwallet->chain().rpcEnableDeprecated("bumpfee")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "Using bumpfee with wallets that have private keys disabled is deprecated. Use psbtbumpfee instead or restart bitcoind with -deprecatedrpc=bumpfee. This functionality will be removed in 0.22");
+ }
+ want_psbt = true;
+ }
+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -3359,7 +3517,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
// If wallet private keys are enabled, return the new transaction id,
// otherwise return the base64-encoded unsigned PSBT of the new transaction.
- if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (!want_psbt) {
if (!feebumper::SignTransaction(*pwallet, mtx)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
}
@@ -3390,11 +3548,16 @@ static UniValue bumpfee(const JSONRPCRequest& request)
result.pushKV("errors", result_errors);
return result;
+},
+ };
}
-UniValue rescanblockchain(const JSONRPCRequest& request)
+static RPCHelpMan bumpfee() { return bumpfee_helper("bumpfee"); }
+static RPCHelpMan psbtbumpfee() { return bumpfee_helper("psbtbumpfee"); }
+
+static RPCHelpMan rescanblockchain()
{
- RPCHelpMan{"rescanblockchain",
+ return RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
@@ -3412,8 +3575,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
HelpExampleCli("rescanblockchain", "100000 120000")
+ HelpExampleRpc("rescanblockchain", "100000, 120000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3469,6 +3632,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
response.pushKV("start_height", start_height);
response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
return response;
+},
+ };
}
class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
@@ -3588,9 +3753,9 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
return ret;
}
-UniValue getaddressinfo(const JSONRPCRequest& request)
+RPCHelpMan getaddressinfo()
{
- RPCHelpMan{"getaddressinfo",
+ return RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
{
@@ -3611,7 +3776,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
{RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
{RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
- " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
+ "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
"witness_v0_scripthash, witness_unknown."},
{RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
{RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
@@ -3641,8 +3806,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3706,11 +3871,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
ret.pushKV("labels", std::move(labels));
return ret;
+},
+ };
}
-static UniValue getaddressesbylabel(const JSONRPCRequest& request)
+static RPCHelpMan getaddressesbylabel()
{
- RPCHelpMan{"getaddressesbylabel",
+ return RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
@@ -3728,8 +3895,8 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
HelpExampleCli("getaddressesbylabel", "\"tabby\"")
+ HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3763,11 +3930,13 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue listlabels(const JSONRPCRequest& request)
+static RPCHelpMan listlabels()
{
- RPCHelpMan{"listlabels",
+ return RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
{"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
@@ -3788,8 +3957,8 @@ static UniValue listlabels(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3816,22 +3985,202 @@ static UniValue listlabels(const JSONRPCRequest& request)
}
return ret;
+},
+ };
+}
+
+static RPCHelpMan send()
+{
+ return RPCHelpMan{"send",
+ "\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
+ "\nSend a transaction.\n",
+ {
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "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, "",
+ {
+ {"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 + ""},
+ },
+ },
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ },
+ },
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
+ {"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 default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
+ {"include_watching", RPCArg::Type::BOOL, /* default */ "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",
+ {
+ {"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", "A JSON array of 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.",
+ {
+ {"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"
+ "Allows this transaction to be replaced by a transaction with higher fees"},
+ },
+ "options"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
+ {RPCResult::Type::STR_HEX, "hex", "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::STR, "psbt", "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
+ }
+ },
+ RPCExamples{""
+ "\nSend with a fee rate of 1 satoshi per byte\n"
+ + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 sat/b\n") +
+ "\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
+ + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VSTR,
+ UniValue::VOBJ
+ }, true
+ );
+
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
+ UniValue options = request.params[3];
+ if (options.exists("feeRate") || options.exists("fee_rate") || options.exists("estimate_mode") || options.exists("conf_target")) {
+ if (!request.params[1].isNull() || !request.params[2].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use either conf_target and estimate_mode or the options dictionary to control fee rate");
+ }
+ } else {
+ options.pushKV("conf_target", request.params[1]);
+ options.pushKV("estimate_mode", request.params[2]);
+ }
+ if (!options["conf_target"].isNull() && (options["estimate_mode"].isNull() || (options["estimate_mode"].get_str() == "unset"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Specify estimate_mode");
+ }
+ if (options.exists("changeAddress")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address");
+ }
+ if (options.exists("changePosition")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position");
+ }
+ if (options.exists("includeWatching")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching");
+ }
+ if (options.exists("lockUnspents")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents");
+ }
+ if (options.exists("subtractFeeFromOutputs")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use subtract_fee_from_outputs");
+ }
+
+ const bool psbt_opt_in = options.exists("psbt") && options["psbt"].get_bool();
+
+ CAmount fee;
+ int change_position;
+ bool rbf = pwallet->m_signal_rbf;
+ if (options.exists("replaceable")) {
+ rbf = options["replaceable"].get_bool();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
+ CCoinControl coin_control;
+ // Automatically select coins, unless at least one is manually selected. Can
+ // be overriden by options.add_inputs.
+ coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ FundTransaction(pwallet, rawTx, fee, change_position, options, coin_control);
+
+ bool add_to_wallet = true;
+ if (options.exists("add_to_wallet")) {
+ add_to_wallet = options["add_to_wallet"].get_bool();
+ }
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx(rawTx);
+
+ // Fill transaction with our data and sign
+ bool complete = true;
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
+ if (err != TransactionError::OK) {
+ throw JSONRPCTransactionError(err);
+ }
+
+ CMutableTransaction mtx;
+ complete = FinalizeAndExtractPSBT(psbtx, mtx);
+
+ UniValue result(UniValue::VOBJ);
+
+ if (psbt_opt_in || !complete || !add_to_wallet) {
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
+ }
+
+ if (complete) {
+ std::string err_string;
+ std::string hex = EncodeHexTx(CTransaction(mtx));
+ CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
+ result.pushKV("txid", tx->GetHash().GetHex());
+ if (add_to_wallet && !psbt_opt_in) {
+ pwallet->CommitTransaction(tx, {}, {} /* orderForm */);
+ } else {
+ result.pushKV("hex", hex);
+ }
+ }
+ result.pushKV("complete", complete);
+
+ return result;
+ }
+ };
}
-UniValue sethdseed(const JSONRPCRequest& request)
+static RPCHelpMan sethdseed()
{
- RPCHelpMan{"sethdseed",
+ return RPCHelpMan{"sethdseed",
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
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"
- " 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."},
+ "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"
- " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
+ "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -3840,8 +4189,8 @@ UniValue sethdseed(const JSONRPCRequest& request)
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3886,11 +4235,13 @@ UniValue sethdseed(const JSONRPCRequest& request)
if (flush_key_pool) spk_man.NewKeyPool();
return NullUniValue;
+},
+ };
}
-UniValue walletprocesspsbt(const JSONRPCRequest& request)
+static RPCHelpMan walletprocesspsbt()
{
- RPCHelpMan{"walletprocesspsbt",
+ return RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
HELP_REQUIRING_PASSPHRASE,
@@ -3916,8 +4267,8 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("walletprocesspsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3950,15 +4301,17 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
result.pushKV("complete", complete);
return result;
+},
+ };
}
-UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
+static RPCHelpMan walletcreatefundedpsbt()
{
- RPCHelpMan{"walletcreatefundedpsbt",
+ return RPCHelpMan{"walletcreatefundedpsbt",
"\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator and Updater roles.\n",
{
- {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs. Leave empty to add inputs automatically. See add_inputs option.",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -3972,7 +4325,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.",
+ "accepted as second parameter.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -3997,15 +4350,15 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "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.",
+ "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.",
{
{"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"
- " Allows this transaction to be replaced by a transaction with higher fees"},
+ "Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "fall back to wallet's confirmation target (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"
" \"" + FeeModes("\"\n\"") + "\""},
@@ -4025,8 +4378,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\nCreate a transaction with no inputs\n"
+ HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -4075,11 +4428,13 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
+},
+ };
}
-static UniValue upgradewallet(const JSONRPCRequest& request)
+static RPCHelpMan upgradewallet()
{
- RPCHelpMan{"upgradewallet",
+ return 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.",
{
@@ -4089,9 +4444,9 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("upgradewallet", "169900")
+ HelpExampleRpc("upgradewallet", "169900")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -4111,19 +4466,21 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
return error.original;
+},
+ };
}
-UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue importprivkey(const JSONRPCRequest& request);
-UniValue importaddress(const JSONRPCRequest& request);
-UniValue importpubkey(const JSONRPCRequest& request);
-UniValue dumpwallet(const JSONRPCRequest& request);
-UniValue importwallet(const JSONRPCRequest& request);
-UniValue importprunedfunds(const JSONRPCRequest& request);
-UniValue removeprunedfunds(const JSONRPCRequest& request);
-UniValue importmulti(const JSONRPCRequest& request);
-UniValue importdescriptors(const JSONRPCRequest& request);
+RPCHelpMan abortrescan();
+RPCHelpMan dumpprivkey();
+RPCHelpMan importprivkey();
+RPCHelpMan importaddress();
+RPCHelpMan importpubkey();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
+RPCHelpMan importprunedfunds();
+RPCHelpMan removeprunedfunds();
+RPCHelpMan importmulti();
+RPCHelpMan importdescriptors();
Span<const CRPCCommand> GetWalletRPCCommands()
{
@@ -4137,7 +4494,8 @@ static const CRPCCommand commands[] =
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors"} },
+ { "wallet", "psbtbumpfee", &psbtbumpfee, {"txid", "options"} },
+ { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors", "load_on_startup"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
@@ -4170,19 +4528,20 @@ static const CRPCCommand commands[] =
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "listwalletdir", &listwalletdir, {} },
{ "wallet", "listwallets", &listwallets, {} },
- { "wallet", "loadwallet", &loadwallet, {"filename"} },
+ { "wallet", "loadwallet", &loadwallet, {"filename", "load_on_startup"} },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
- { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
- { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse"} },
+ { "wallet", "send", &send, {"outputs","conf_target","estimate_mode","options"} },
+ { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode","verbose"} },
+ { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse","verbose"} },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
{ "wallet", "setwalletflag", &setwalletflag, {"flag","value"} },
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
- { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
+ { "wallet", "unloadwallet", &unloadwallet, {"wallet_name", "load_on_startup"} },
{ "wallet", "upgradewallet", &upgradewallet, {"version"} },
{ "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
{ "wallet", "walletlock", &walletlock, {} },
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index fb1e91282b..184a16e91d 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -34,6 +34,6 @@ void EnsureWalletIsUnlocked(const CWallet*);
WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
-UniValue getaddressinfo(const JSONRPCRequest& request);
-UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
+RPCHelpMan getaddressinfo();
+RPCHelpMan signrawtransactionwithwallet();
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index af57210f01..225b975067 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -16,14 +16,24 @@ static const char *HEADER_END = "HEADER=END";
static const char *DATA_END = "DATA=END";
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
-bool RecoverDatabaseFile(const fs::path& file_path)
+static bool KeyFilter(const std::string& type)
{
+ return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
+}
+
+bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
+{
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
+ options.verify = false;
+ std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
+ if (!database) return false;
+
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
- bilingual_str open_err;
- if (!env->Open(open_err)) {
- tfm::format(std::cerr, "%s\n", open_err.original);
+ if (!env->Open(error)) {
return false;
}
@@ -39,11 +49,9 @@ bool RecoverDatabaseFile(const fs::path& file_path)
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
newFilename.c_str(), DB_AUTO_COMMIT);
- if (result == 0)
- LogPrintf("Renamed %s to %s\n", filename, newFilename);
- else
+ if (result != 0)
{
- LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
+ error = strprintf(Untranslated("Failed to rename %s to %s"), filename, newFilename);
return false;
}
@@ -60,10 +68,10 @@ bool RecoverDatabaseFile(const fs::path& file_path)
Db db(env->dbenv.get(), 0);
result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
if (result == DB_VERIFY_BAD) {
- LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
+ warnings.push_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
}
if (result != 0 && result != DB_VERIFY_BAD) {
- LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
+ error = strprintf(Untranslated("Salvage: Database salvage failed with result %d."), result);
return false;
}
@@ -87,7 +95,7 @@ bool RecoverDatabaseFile(const fs::path& file_path)
break;
getline(strDump, valueHex);
if (valueHex == DATA_END) {
- LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
+ warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
break;
}
salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
@@ -96,7 +104,7 @@ bool RecoverDatabaseFile(const fs::path& file_path)
bool fSuccess;
if (keyHex != DATA_END) {
- LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
+ warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
fSuccess = false;
} else {
fSuccess = (result == 0);
@@ -104,10 +112,9 @@ bool RecoverDatabaseFile(const fs::path& file_path)
if (salvagedData.empty())
{
- LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
+ error = strprintf(Untranslated("Salvage(aggressive) found no records in %s."), newFilename);
return false;
}
- LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
int ret = pdbCopy->open(nullptr, // Txn pointer
@@ -117,13 +124,13 @@ bool RecoverDatabaseFile(const fs::path& file_path)
DB_CREATE, // Flags
0);
if (ret > 0) {
- LogPrintf("Cannot create database file %s\n", filename);
+ error = strprintf(Untranslated("Cannot create database file %s"), filename);
pdbCopy->close(0);
return false;
}
DbTxn* ptxn = env->TxnBegin();
- CWallet dummyWallet(nullptr, WalletLocation(), CreateDummyWalletDatabase());
+ CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData)
{
/* Filter for only private key type KV pairs to be added to the salvaged wallet */
@@ -134,14 +141,14 @@ bool RecoverDatabaseFile(const fs::path& file_path)
{
// Required in LoadKeyMetadata():
LOCK(dummyWallet.cs_wallet);
- fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr);
+ fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr, KeyFilter);
}
- if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
+ if (!KeyFilter(strType)) {
continue;
}
if (!fReadOK)
{
- LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
+ warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
continue;
}
Dbt datKey(&row.first[0], row.first.size());
diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h
index e361930f5e..5a8538f942 100644
--- a/src/wallet/salvage.h
+++ b/src/wallet/salvage.h
@@ -9,6 +9,8 @@
#include <fs.h>
#include <streams.h>
-bool RecoverDatabaseFile(const fs::path& file_path);
+struct bilingual_str;
+
+bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings);
#endif // BITCOIN_WALLET_SALVAGE_H
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 51715462c5..188289b010 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -96,6 +96,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT:
break;
case TxoutType::PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
@@ -452,7 +453,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, bilingual_str& error)
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
- if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) && CHDChain::VERSION_HD_CHAIN_SPLIT) {
+ if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {
WalletLogPrintf("Upgrading wallet to use HD chain split\n");
m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
split_upgrade = FEATURE_HD_SPLIT > prev_version;
@@ -655,7 +656,7 @@ std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestin
uint256 LegacyScriptPubKeyMan::GetID() const
{
- return UINT256_ONE();
+ return uint256::ONE;
}
/**
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index a96d971734..63c10b7a0d 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -33,7 +33,7 @@ class WalletStorage
public:
virtual ~WalletStorage() = default;
virtual const std::string GetDisplayName() const = 0;
- virtual WalletDatabase& GetDatabase() = 0;
+ virtual WalletDatabase& GetDatabase() const = 0;
virtual bool IsWalletFlagSet(uint64_t) const = 0;
virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
virtual bool CanSupportFeature(enum WalletFeature) const = 0;
@@ -535,7 +535,7 @@ private:
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
- bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
+ bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
new file mode 100644
index 0000000000..6d2fdbe58b
--- /dev/null
+++ b/src/wallet/sqlite.cpp
@@ -0,0 +1,629 @@
+// 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 <wallet/sqlite.h>
+
+#include <chainparams.h>
+#include <crypto/common.h>
+#include <logging.h>
+#include <sync.h>
+#include <util/memory.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <wallet/db.h>
+
+#include <sqlite3.h>
+#include <stdint.h>
+
+static const char* const DATABASE_FILENAME = "wallet.dat";
+static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
+
+static Mutex g_sqlite_mutex;
+static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0;
+
+static void ErrorLogCallback(void* arg, int code, const char* msg)
+{
+ // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option:
+ // "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as
+ // the first parameter to the application-defined logger function whenever that function is
+ // invoked."
+ // Assert that this is the case:
+ assert(arg == nullptr);
+ LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
+}
+
+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())
+{
+ {
+ LOCK(g_sqlite_mutex);
+ LogPrintf("Using SQLite Version %s\n", SQLiteDatabaseVersion());
+ LogPrintf("Using wallet %s\n", m_dir_path);
+
+ if (++g_sqlite_count == 1) {
+ // Setup logging
+ int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup error log: %s\n", sqlite3_errstr(ret)));
+ }
+ // Force serialized threading mode
+ ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to configure serialized threading mode: %s\n", sqlite3_errstr(ret)));
+ }
+ }
+ int ret = sqlite3_initialize(); // This is a no-op if sqlite3 is already initialized
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to initialize SQLite: %s\n", sqlite3_errstr(ret)));
+ }
+ }
+
+ try {
+ Open();
+ } catch (const std::runtime_error&) {
+ // If open fails, cleanup this object and rethrow the exception
+ Cleanup();
+ throw;
+ }
+}
+
+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)));
+ }
+ }
+}
+
+SQLiteDatabase::~SQLiteDatabase()
+{
+ Cleanup();
+}
+
+void SQLiteDatabase::Cleanup() noexcept
+{
+ Close();
+
+ LOCK(g_sqlite_mutex);
+ if (--g_sqlite_count == 0) {
+ int ret = sqlite3_shutdown();
+ if (ret != SQLITE_OK) {
+ LogPrintf("SQLiteDatabase: Failed to shutdown SQLite: %s\n", sqlite3_errstr(ret));
+ }
+ }
+}
+
+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);
+ 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);
+ return false;
+ }
+
+ // 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);
+ 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);
+ if (ret != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ error = strprintf(_("SQLiteDatabase: Failed to prepare statement to verify database: %s"), sqlite3_errstr(ret));
+ return false;
+ }
+ while (true) {
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE) {
+ break;
+ }
+ if (ret != SQLITE_ROW) {
+ error = strprintf(_("SQLiteDatabase: Failed to execute statement to verify database: %s"), sqlite3_errstr(ret));
+ break;
+ }
+ const char* msg = (const char*)sqlite3_column_text(stmt, 0);
+ if (!msg) {
+ error = strprintf(_("SQLiteDatabase: Failed to read database verification error: %s"), sqlite3_errstr(ret));
+ break;
+ }
+ std::string str_msg(msg);
+ if (str_msg == "ok") {
+ continue;
+ }
+ if (error.empty()) {
+ error = _("Failed to verify database") + Untranslated("\n");
+ }
+ error += Untranslated(strprintf("%s\n", str_msg));
+ }
+ sqlite3_finalize(stmt);
+ return error.empty();
+}
+
+void SQLiteDatabase::Open()
+{
+ int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+ if (m_mock) {
+ flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db
+ }
+
+ if (m_db == nullptr) {
+ TryCreateDirectories(m_dir_path);
+ int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
+ }
+ }
+
+ if (sqlite3_db_readonly(m_db, "main") != 0) {
+ throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
+ }
+
+ // 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)));
+ }
+ // 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);
+ 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");
+ }
+ ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));
+ }
+
+ // 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)));
+ }
+
+ // Make the table for our key-value pairs
+ // First check that the main table exists
+ sqlite3_stmt* check_main_stmt{nullptr};
+ ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret)));
+ }
+ ret = sqlite3_step(check_main_stmt);
+ if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret)));
+ }
+ bool table_exists;
+ if (ret == SQLITE_DONE) {
+ table_exists = false;
+ } else if (ret == SQLITE_ROW) {
+ table_exists = true;
+ } else {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to execute statement to check table existence: %s\n", sqlite3_errstr(ret)));
+ }
+
+ // Do the db setup things because the table doesn't exist only when we are creating a new wallet
+ if (!table_exists) {
+ ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
+ }
+
+ // 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)));
+ }
+
+ // 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)));
+ }
+ }
+}
+
+bool SQLiteDatabase::Rewrite(const char* skip)
+{
+ // Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html
+ int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr);
+ return ret == SQLITE_OK;
+}
+
+bool SQLiteDatabase::Backup(const std::string& dest) const
+{
+ sqlite3* db_copy;
+ int res = sqlite3_open(dest.c_str(), &db_copy);
+ if (res != SQLITE_OK) {
+ sqlite3_close(db_copy);
+ return false;
+ }
+ sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
+ if (!backup) {
+ LogPrintf("%s: Unable to begin backup: %s\n", __func__, sqlite3_errmsg(m_db));
+ sqlite3_close(db_copy);
+ return false;
+ }
+ // Specifying -1 will copy all of the pages
+ res = sqlite3_backup_step(backup, -1);
+ if (res != SQLITE_DONE) {
+ LogPrintf("%s: Unable to backup: %s\n", __func__, sqlite3_errstr(res));
+ sqlite3_backup_finish(backup);
+ sqlite3_close(db_copy);
+ return false;
+ }
+ res = sqlite3_backup_finish(backup);
+ sqlite3_close(db_copy);
+ return res == SQLITE_OK;
+}
+
+void SQLiteDatabase::Close()
+{
+ int res = sqlite3_close(m_db);
+ if (res != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));
+ }
+ m_db = nullptr;
+}
+
+std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close)
+{
+ // We ignore flush_on_close because we don't do manual flushing for SQLite
+ return MakeUnique<SQLiteBatch>(*this);
+}
+
+SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)
+ : m_database(database)
+{
+ // Make sure we have a db handle
+ assert(m_database.m_db);
+
+ SetupSQLStatements();
+}
+
+void SQLiteBatch::Close()
+{
+ // If m_db is in a transaction (i.e. not in autocommit mode), then abort the transaction in progress
+ if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
+ if (TxnAbort()) {
+ LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");
+ } else {
+ LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction\n");
+ }
+ }
+
+ // 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));
+ }
+ 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)
+{
+ if (!m_database.m_db) return false;
+ assert(m_read_stmt);
+
+ // Bind: leftmost parameter in statement is index 1
+ int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
+ sqlite3_clear_bindings(m_read_stmt);
+ sqlite3_reset(m_read_stmt);
+ return false;
+ }
+ res = sqlite3_step(m_read_stmt);
+ if (res != SQLITE_ROW) {
+ if (res != SQLITE_DONE) {
+ // SQLITE_DONE means "not found", don't log an error in that case.
+ LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
+ }
+ sqlite3_clear_bindings(m_read_stmt);
+ sqlite3_reset(m_read_stmt);
+ return false;
+ }
+ // Leftmost column in result is index 0
+ const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0));
+ int data_size = sqlite3_column_bytes(m_read_stmt, 0);
+ value.write(data, data_size);
+
+ sqlite3_clear_bindings(m_read_stmt);
+ sqlite3_reset(m_read_stmt);
+ return true;
+}
+
+bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
+{
+ if (!m_database.m_db) return false;
+ assert(m_insert_stmt && m_overwrite_stmt);
+
+ sqlite3_stmt* stmt;
+ if (overwrite) {
+ stmt = m_overwrite_stmt;
+ } else {
+ stmt = m_insert_stmt;
+ }
+
+ // Bind: leftmost parameter in statement is index 1
+ // Insert index 1 is key, 2 is value
+ int res = sqlite3_bind_blob(stmt, 1, key.data(), key.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("%s: Unable to bind key to statement: %s\n", __func__, sqlite3_errstr(res));
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+ return false;
+ }
+ res = sqlite3_bind_blob(stmt, 2, value.data(), value.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("%s: Unable to bind value to statement: %s\n", __func__, sqlite3_errstr(res));
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+ return false;
+ }
+
+ // Execute
+ res = sqlite3_step(stmt);
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+ if (res != SQLITE_DONE) {
+ LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
+ }
+ return res == SQLITE_DONE;
+}
+
+bool SQLiteBatch::EraseKey(CDataStream&& key)
+{
+ if (!m_database.m_db) return false;
+ assert(m_delete_stmt);
+
+ // Bind: leftmost parameter in statement is index 1
+ int res = sqlite3_bind_blob(m_delete_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
+ sqlite3_clear_bindings(m_delete_stmt);
+ sqlite3_reset(m_delete_stmt);
+ return false;
+ }
+
+ // Execute
+ res = sqlite3_step(m_delete_stmt);
+ sqlite3_clear_bindings(m_delete_stmt);
+ sqlite3_reset(m_delete_stmt);
+ if (res != SQLITE_DONE) {
+ LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
+ }
+ return res == SQLITE_DONE;
+}
+
+bool SQLiteBatch::HasKey(CDataStream&& key)
+{
+ if (!m_database.m_db) return false;
+ assert(m_read_stmt);
+
+ // Bind: leftmost parameter in statement is index 1
+ bool ret = false;
+ int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
+ if (res == SQLITE_OK) {
+ res = sqlite3_step(m_read_stmt);
+ if (res == SQLITE_ROW) {
+ ret = true;
+ }
+ }
+
+ sqlite3_clear_bindings(m_read_stmt);
+ sqlite3_reset(m_read_stmt);
+ return ret;
+}
+
+bool SQLiteBatch::StartCursor()
+{
+ assert(!m_cursor_init);
+ if (!m_database.m_db) return false;
+ m_cursor_init = true;
+ return true;
+}
+
+bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& complete)
+{
+ complete = false;
+
+ if (!m_cursor_init) return false;
+
+ int res = sqlite3_step(m_cursor_stmt);
+ if (res == SQLITE_DONE) {
+ complete = true;
+ return true;
+ }
+ if (res != SQLITE_ROW) {
+ LogPrintf("SQLiteBatch::ReadAtCursor: Unable to execute cursor step: %s\n", sqlite3_errstr(res));
+ return false;
+ }
+
+ // Leftmost column in result is index 0
+ const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0));
+ int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0);
+ key.write(key_data, key_data_size);
+ const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1));
+ int value_data_size = sqlite3_column_bytes(m_cursor_stmt, 1);
+ value.write(value_data, value_data_size);
+ return true;
+}
+
+void SQLiteBatch::CloseCursor()
+{
+ sqlite3_reset(m_cursor_stmt);
+ m_cursor_init = false;
+}
+
+bool SQLiteBatch::TxnBegin()
+{
+ if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) == 0) return false;
+ int res = sqlite3_exec(m_database.m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
+ if (res != SQLITE_OK) {
+ LogPrintf("SQLiteBatch: Failed to begin the transaction\n");
+ }
+ return res == SQLITE_OK;
+}
+
+bool SQLiteBatch::TxnCommit()
+{
+ if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
+ int res = sqlite3_exec(m_database.m_db, "COMMIT TRANSACTION", nullptr, nullptr, nullptr);
+ if (res != SQLITE_OK) {
+ LogPrintf("SQLiteBatch: Failed to commit the transaction\n");
+ }
+ return res == SQLITE_OK;
+}
+
+bool SQLiteBatch::TxnAbort()
+{
+ if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
+ int res = sqlite3_exec(m_database.m_db, "ROLLBACK TRANSACTION", nullptr, nullptr, nullptr);
+ if (res != SQLITE_OK) {
+ LogPrintf("SQLiteBatch: Failed to abort the transaction\n");
+ }
+ return res == SQLITE_OK;
+}
+
+bool ExistsSQLiteDatabase(const fs::path& path)
+{
+ const fs::path file = path / DATABASE_FILENAME;
+ return fs::symlink_status(file).type() == fs::regular_file && IsSQLiteFile(file);
+}
+
+std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
+{
+ const fs::path file = path / DATABASE_FILENAME;
+ try {
+ auto db = MakeUnique<SQLiteDatabase>(path, file);
+ if (options.verify && !db->Verify(error)) {
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
+ }
+ return db;
+ } catch (const std::runtime_error& e) {
+ status = DatabaseStatus::FAILED_LOAD;
+ error.original = e.what();
+ return nullptr;
+ }
+}
+
+std::string SQLiteDatabaseVersion()
+{
+ return std::string(sqlite3_libversion());
+}
+
+bool IsSQLiteFile(const fs::path& path)
+{
+ if (!fs::exists(path)) return false;
+
+ // A SQLite Database file is at least 512 bytes.
+ boost::system::error_code ec;
+ auto size = fs::file_size(path, ec);
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (size < 512) return false;
+
+ fsbridge::ifstream file(path, std::ios::binary);
+ if (!file.is_open()) return false;
+
+ // Magic is at beginning and is 16 bytes long
+ char magic[16];
+ file.read(magic, 16);
+
+ // Application id is at offset 68 and 4 bytes long
+ file.seekg(68, std::ios::beg);
+ char app_id[4];
+ file.read(app_id, 4);
+
+ file.close();
+
+ // Check the magic, see https://sqlite.org/fileformat2.html
+ std::string magic_str(magic, 16);
+ if (magic_str != std::string("SQLite format 3", 16)) {
+ return false;
+ }
+
+ // Check the application id matches our network magic
+ return memcmp(Params().MessageStart(), app_id, 4) == 0;
+}
diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h
new file mode 100644
index 0000000000..693a2ef55a
--- /dev/null
+++ b/src/wallet/sqlite.h
@@ -0,0 +1,122 @@
+// 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_WALLET_SQLITE_H
+#define BITCOIN_WALLET_SQLITE_H
+
+#include <wallet/db.h>
+
+#include <sqlite3.h>
+
+struct bilingual_str;
+class SQLiteDatabase;
+
+/** RAII class that provides access to a WalletDatabase */
+class SQLiteBatch : public DatabaseBatch
+{
+private:
+ SQLiteDatabase& m_database;
+
+ bool m_cursor_init = false;
+
+ sqlite3_stmt* m_read_stmt{nullptr};
+ sqlite3_stmt* m_insert_stmt{nullptr};
+ sqlite3_stmt* m_overwrite_stmt{nullptr};
+ sqlite3_stmt* m_delete_stmt{nullptr};
+ sqlite3_stmt* m_cursor_stmt{nullptr};
+
+ void SetupSQLStatements();
+
+ bool ReadKey(CDataStream&& key, CDataStream& value) override;
+ bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
+ bool EraseKey(CDataStream&& key) override;
+ bool HasKey(CDataStream&& key) override;
+
+public:
+ explicit SQLiteBatch(SQLiteDatabase& database);
+ ~SQLiteBatch() override { Close(); }
+
+ /* No-op. See commeng on SQLiteDatabase::Flush */
+ void Flush() override {}
+
+ void Close() override;
+
+ bool StartCursor() override;
+ bool ReadAtCursor(CDataStream& key, CDataStream& value, bool& complete) override;
+ void CloseCursor() override;
+ bool TxnBegin() override;
+ bool TxnCommit() override;
+ bool TxnAbort() override;
+};
+
+/** An instance of this class represents one SQLite3 database.
+ **/
+class SQLiteDatabase : public WalletDatabase
+{
+private:
+ const bool m_mock{false};
+
+ const std::string m_dir_path;
+
+ const std::string m_file_path;
+
+ void Cleanup() noexcept;
+
+public:
+ SQLiteDatabase() = delete;
+
+ /** Create DB handle to real database */
+ SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock = false);
+
+ ~SQLiteDatabase();
+
+ bool Verify(bilingual_str& error);
+
+ /** Open the database if it is not already opened */
+ void Open() override;
+
+ /** Close the database */
+ void Close() override;
+
+ /* These functions are unused */
+ void AddRef() override { assert(false); }
+ void RemoveRef() override { assert(false); }
+
+ /** Rewrite the entire database on disk */
+ bool Rewrite(const char* skip = nullptr) override;
+
+ /** Back up the entire database to a file.
+ */
+ bool Backup(const std::string& dest) const override;
+
+ /** No-ops
+ *
+ * SQLite always flushes everything to the database file after each transaction
+ * (each Read/Write/Erase that we do is its own transaction unless we called
+ * TxnBegin) so there is no need to have Flush or Periodic Flush.
+ *
+ * There is no DB env to reload, so ReloadDbEnv has nothing to do
+ */
+ void Flush() override {}
+ bool PeriodicFlush() override { return false; }
+ void ReloadDbEnv() override {}
+
+ void IncrementUpdateCounter() override { ++nUpdateCounter; }
+
+ std::string Filename() override { return m_file_path; }
+ std::string Format() override { return "sqlite"; }
+
+ /** Make a SQLiteBatch connected to this database */
+ std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
+
+ sqlite3* m_db{nullptr};
+};
+
+bool ExistsSQLiteDatabase(const fs::path& path);
+std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+
+std::string SQLiteDatabaseVersion();
+bool IsSQLiteFile(const fs::path& path);
+
+#endif // BITCOIN_WALLET_SQLITE_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 1deedede4c..f38ccba384 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
+static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that can use BnB when there are preset inputs
empty_wallet();
{
- std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
wallet->SetupLegacyScriptPubKeyMan();
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 35bd965673..c80310045a 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -10,7 +10,7 @@
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- m_chain_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args));
std::string sep;
sep += fs::path::preferred_separator;
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index c95b4f1f6e..f5bade77df 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -6,6 +6,7 @@
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <node/context.h>
#include <test/util/setup_common.h>
@@ -19,7 +20,7 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client;
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client;
};
#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index c228e06009..9b905569fc 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
{
SetWalletDir(m_walletdir_path_cases["default"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
{
SetWalletDir(m_walletdir_path_cases["custom"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
{
ASSERT_DEBUG_LOG("does not exist");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
SetWalletDir(m_walletdir_path_cases["file"]);
{
ASSERT_DEBUG_LOG("is not a directory");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
SetWalletDir(m_walletdir_path_cases["relative"]);
{
ASSERT_DEBUG_LOG("is a relative path");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
{
SetWalletDir(m_walletdir_path_cases["trailing"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{
SetWalletDir(m_walletdir_path_cases["trailing2"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index e416f16044..d5aed99d99 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index 4f12079768..f7c1337b0d 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(CanProvide)
// Set up wallet and keyman variables.
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 44f9eb5ddd..4d6f427618 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,10 +6,10 @@
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
- m_wallet(m_chain.get(), WalletLocation(), CreateMockWalletDatabase())
+ m_wallet(m_chain.get(), "", CreateMockWalletDatabase())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
m_chain_notifications_handler = m_chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
- m_chain_client->registerRpcs();
+ m_wallet_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 99d7cfe921..ba8a5ff1f3 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -21,7 +21,7 @@ struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args));
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7ef06663b5..c42114c394 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -24,9 +24,9 @@
#include <boost/test/unit_test.hpp>
#include <univalue.h>
-extern UniValue importmulti(const JSONRPCRequest& request);
-extern UniValue dumpwallet(const JSONRPCRequest& request);
-extern UniValue importwallet(const JSONRPCRequest& request);
+RPCHelpMan importmulti();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
// Ensure that fee levels defined in the wallet are at least as high
// as the default levels for node policy.
@@ -37,9 +37,12 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
{
+ DatabaseOptions options;
+ DatabaseStatus status;
bilingual_str error;
std::vector<bilingual_str> warnings;
- auto wallet = CWallet::CreateWalletFromFile(chain, WalletLocation(""), error, warnings);
+ auto database = MakeWalletDatabase("", options, status, error);
+ auto wallet = CWallet::Create(chain, "", std::move(database), options.create_flags, error, warnings);
wallet->postInitProcess();
return wallet;
}
@@ -85,7 +88,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -104,7 +107,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -123,14 +126,14 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -149,13 +152,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -186,7 +189,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
@@ -194,7 +197,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
AddWallet(wallet);
@@ -219,7 +222,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(keys);
- UniValue response = importmulti(request);
+ UniValue response = importmulti().HandleRequest(request);
BOOST_CHECK_EQUAL(response.write(),
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
"timestamp %d. There was an error reading a block from time %d, which is after or within %d "
@@ -229,7 +232,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
}
}
@@ -259,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -274,14 +277,14 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
- ::dumpwallet(request);
- RemoveWallet(wallet);
+ ::dumpwallet().HandleRequest(request);
+ RemoveWallet(wallet, nullopt);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
@@ -291,8 +294,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
- ::importwallet(request);
- RemoveWallet(wallet);
+ ::importwallet().HandleRequest(request);
+ RemoveWallet(wallet, nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
@@ -317,7 +320,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back());
@@ -492,7 +495,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -521,8 +524,9 @@ public:
int changePos = -1;
bilingual_str error;
CCoinControl dummy;
+ FeeCalculation fee_calc_out;
{
- BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy));
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
}
wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx;
@@ -610,7 +614,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index cee2f2214c..6b7d05fdf3 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -33,6 +33,8 @@
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <univalue.h>
+
#include <algorithm>
#include <assert.h>
@@ -54,6 +56,42 @@ static 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);
+bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) setting_value.setArray();
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (value.isStr() && value.get_str() == wallet_name) return true;
+ }
+ setting_value.push_back(wallet_name);
+ return chain.updateRwSetting("wallet", setting_value);
+}
+
+bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) return true;
+ util::SettingsValue new_value(util::SettingsValue::VARR);
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
+ }
+ if (new_value.size() == setting_value.size()) return true;
+ return chain.updateRwSetting("wallet", new_value);
+}
+
+static void UpdateWalletSetting(interfaces::Chain& chain,
+ const std::string& wallet_name,
+ Optional<bool> load_on_startup,
+ std::vector<bilingual_str>& warnings)
+{
+ if (load_on_startup == nullopt) return;
+ if (load_on_startup.get() && !AddWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
+ } else if (!load_on_startup.get() && !RemoveWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
+ }
+}
+
bool AddWallet(const std::shared_ptr<CWallet>& wallet)
{
LOCK(cs_wallets);
@@ -65,18 +103,32 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
{
assert(wallet);
+
+ interfaces::Chain& chain = wallet->chain();
+ std::string name = wallet->GetName();
+
// Unregister with the validation interface which also drops shared ponters.
wallet->m_chain_notifications_handler.reset();
LOCK(cs_wallets);
std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i == vpwallets.end()) return false;
vpwallets.erase(i);
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return true;
}
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start)
+{
+ std::vector<bilingual_str> warnings;
+ return RemoveWallet(wallet, load_on_start, warnings);
+}
+
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
LOCK(cs_wallets);
@@ -148,48 +200,56 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
-std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
- if (!CWallet::Verify(chain, location, error, warnings)) {
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
- std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings);
+ 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;
return nullptr;
}
AddWallet(wallet);
wallet->postInitProcess();
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return wallet;
} catch (const std::runtime_error& e) {
error = Untranslated(e.what());
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
}
} // namespace
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
- auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(location.GetName()));
+ auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) {
error = Untranslated("Wallet already being loading.");
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
- auto wallet = LoadWalletInternal(chain, location, error, warnings);
+ auto wallet = LoadWalletInternal(chain, name, load_on_start, options, status, error, warnings);
WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
return wallet;
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
- return LoadWallet(chain, WalletLocation(name), error, warnings);
-}
+ uint64_t wallet_creation_flags = options.create_flags;
+ const SecureString& passphrase = options.create_passphrase;
+
+ if (wallet_creation_flags & WALLET_FLAG_DESCRIPTORS) options.require_format = DatabaseFormat::SQLITE;
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@@ -198,43 +258,42 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
}
- // Check the wallet file location
- WalletLocation location(name);
- if (location.Exists()) {
- error = strprintf(Untranslated("Wallet %s already exists."), location.GetName());
- return WalletCreationStatus::CREATION_FAILED;
- }
-
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
- if (!CWallet::Verify(chain, location, error, warnings)) {
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
}
// Do not allow a passphrase when private keys are disabled
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// Make the wallet
- std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings, wallet_creation_flags);
+ 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;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt.");
- return WalletCreationStatus::ENCRYPTION_FAILED;
+ status = DatabaseStatus::FAILED_ENCRYPT;
+ return nullptr;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
- return WalletCreationStatus::ENCRYPTION_FAILED;
+ status = DatabaseStatus::FAILED_ENCRYPT;
+ return nullptr;
}
// Set a seed for the wallet
@@ -246,7 +305,8 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
error = Untranslated("Unable to generate initial keys");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
}
}
@@ -258,11 +318,13 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
AddWallet(wallet);
wallet->postInitProcess();
- result = wallet;
- return WalletCreationStatus::SUCCESS;
-}
-const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE());
+ // Write the wallet settings
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
+ status = DatabaseStatus::SUCCESS;
+ return wallet;
+}
/** @defgroup mapWallet
*
@@ -276,7 +338,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
- LOCK(cs_wallet);
+ AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
return nullptr;
@@ -731,7 +793,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
- WalletBatch batch(*database, "r+");
+ WalletBatch batch(*database);
bool success = true;
if (!batch.WriteTx(wtx)) {
@@ -803,7 +865,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
{
LOCK(cs_wallet);
- WalletBatch batch(*database, "r+", fFlushOnClose);
+ WalletBatch batch(*database, fFlushOnClose);
uint256 hash = tx->GetHash();
@@ -1002,7 +1064,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK(cs_wallet);
- WalletBatch batch(*database, "r+");
+ WalletBatch batch(*database);
std::set<uint256> todo;
std::set<uint256> done;
@@ -1065,7 +1127,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
return;
// Do not flush the wallet here for performance reasons
- WalletBatch batch(*database, "r+", false);
+ WalletBatch batch(*database, false);
std::set<uint256> todo;
std::set<uint256> done;
@@ -1115,7 +1177,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
MarkInputsDirty(ptx);
}
-void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
+void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
LOCK(cs_wallet);
SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
@@ -1125,7 +1187,7 @@ void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
}
}
-void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
+void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
LOCK(cs_wallet);
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
@@ -1172,7 +1234,7 @@ void CWallet::blockConnected(const CBlock& block, int height)
m_last_block_processed = block_hash;
for (size_t index = 0; index < block.vtx.size(); index++) {
SyncTransaction(block.vtx[index], {CWalletTx::Status::CONFIRMED, height, block_hash, (int)index});
- transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
+ transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK, 0 /* mempool_sequence */);
}
}
@@ -1210,15 +1272,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
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())
{
- LOCK(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]);
- }
+ const CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
return ISMINE_NO;
}
@@ -1243,16 +1303,19 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
isminetype CWallet::IsMine(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey);
}
isminetype CWallet::IsMine(const CTxDestination& dest) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest));
}
isminetype CWallet::IsMine(const CScript& script) const
{
+ AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script));
@@ -1264,6 +1327,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{
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);
}
@@ -1281,13 +1345,12 @@ bool CWallet::IsChange(const CScript& script) const
// 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;
-
- LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) {
return true;
}
@@ -1297,6 +1360,7 @@ bool CWallet::IsChange(const CScript& script) const
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);
@@ -1304,6 +1368,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const
{
+ AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout)
if (IsMine(txout))
return true;
@@ -1362,6 +1427,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const
{
+ LOCK(cs_wallet);
CAmount nChange = 0;
for (const CTxOut& txout : tx.vout)
{
@@ -1597,6 +1663,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut;
}
+ LOCK(pwallet->cs_wallet);
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
@@ -1965,36 +2032,38 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted() const
{
- std::set<uint256> s;
- return IsTrusted(s);
+ std::set<uint256> trusted_parents;
+ LOCK(pwallet->cs_wallet);
+ return pwallet->IsTrusted(*this, trusted_parents);
}
-bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
+bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
{
+ AssertLockHeld(cs_wallet);
// Quick answer in most cases
- if (!pwallet->chain().checkFinalTx(*tx)) return false;
- int nDepth = GetDepthInMainChain();
+ 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 (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) return false;
+ 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 (!InMempool()) return false;
+ if (!wtx.InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool:
- for (const CTxIn& txin : tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
// Transactions not sent by us: not trusted
- const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
+ 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 (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ 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 (!parent->IsTrusted(trusted_parents)) return false;
+ if (!IsTrusted(*parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
@@ -2080,7 +2149,7 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(trusted_parents)};
+ 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)};
@@ -2148,7 +2217,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(trusted_parents);
+ bool safeTx = IsTrusted(wtx, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2284,6 +2353,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
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) {
@@ -2320,27 +2390,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
for (OutputGroup& group : groups) {
if (!group.EligibleForSpending(eligibility_filter)) continue;
- group.fee = 0;
- group.long_term_fee = 0;
- group.effective_value = 0;
- for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
- const CInputCoin& coin = *it;
- CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
- // Only include outputs that are positive effective value (i.e. not dust)
- if (effective_value > 0) {
- group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
- group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
- if (coin_selection_params.m_subtract_fee_outputs) {
- group.effective_value += coin.txout.nValue;
- } else {
- group.effective_value += effective_value;
- }
- ++it;
- } else {
- it = group.Discard(coin);
- }
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ // Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output
+ group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
+ } else {
+ group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
}
- if (group.effective_value > 0) utxo_pool.push_back(group);
+
+ OutputGroup pos_group = group.GetPositiveOnlyGroup();
+ if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
}
// Calculate the fees for things that aren't inputs
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
@@ -2486,23 +2544,6 @@ bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint,
}
// At this point, one input was not fully signed otherwise we would have exited already
- // Find that input and figure out what went wrong.
- for (unsigned int i = 0; i < tx.vin.size(); i++) {
- // Get the prevout
- CTxIn& txin = tx.vin[i];
- auto coin = coins.find(txin.prevout);
- if (coin == coins.end() || coin->second.IsSpent()) {
- input_errors[i] = "Input not found or already spent";
- continue;
- }
-
- // Check if this input is complete
- SignatureData sigdata = DataFromTransaction(tx, i, coin->second.out);
- if (!sigdata.complete) {
- input_errors[i] = "Unable to sign input, missing keys";
- continue;
- }
- }
return false;
}
@@ -2590,7 +2631,8 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
LOCK(cs_wallet);
CTransactionRef tx_new;
- if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, false)) {
+ FeeCalculation fee_calc_out;
+ if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
return false;
}
@@ -2609,10 +2651,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
- if (lockUnspents) {
- LockCoin(txin.prevout);
- }
}
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
+ }
+
}
return true;
@@ -2706,7 +2749,15 @@ OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_typ
return m_default_address_type;
}
-bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign)
+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);
@@ -3049,6 +3100,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
// 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,
@@ -3061,6 +3113,40 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
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;
+ CTransactionRef tx2 = tx;
+ 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;
+ 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);
@@ -3106,7 +3192,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
LOCK(cs_wallet);
fFirstRunRet = false;
- DBErrors nLoadWalletRet = WalletBatch(*database,"cr+").LoadWallet(this);
+ DBErrors nLoadWalletRet = WalletBatch(*database).LoadWallet(this);
if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
if (database->Rewrite("\x04pool"))
@@ -3133,7 +3219,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
AssertLockHeld(cs_wallet);
- DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
+ DBErrors nZapSelectTxRet = WalletBatch(*database).ZapSelectTx(vHashIn, vHashOut);
for (const uint256& hash : vHashOut) {
const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
@@ -3161,28 +3247,10 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
return DBErrors::LOAD_OK;
}
-DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx);
- if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
- {
- if (database->Rewrite("\x04pool"))
- {
- for (const auto& spk_man_pair : m_spk_managers) {
- spk_man_pair.second->RewriteDB();
- }
- }
- }
-
- if (nZapWalletTxRet != DBErrors::LOAD_OK)
- return nZapWalletTxRet;
-
- return DBErrors::LOAD_OK;
-}
-
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
+ bool is_mine;
{
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@@ -3190,8 +3258,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose;
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO,
+ NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
@@ -3206,30 +3275,31 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
- // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
- // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
- // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
- if (IsMine(address)) {
- WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
- return false;
- }
-
+ bool is_mine;
+ WalletBatch batch(*database);
{
LOCK(cs_wallet);
-
+ // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
+ // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
+ // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ if (IsMine(address)) {
+ WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
+ return false;
+ }
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
{
- WalletBatch(*database).EraseDestData(strAddress, item.first);
+ batch.EraseDestData(strAddress, item.first);
}
m_address_book.erase(address);
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
- WalletBatch(*database).ErasePurpose(EncodeDestination(address));
- return WalletBatch(*database).EraseName(EncodeDestination(address));
+ batch.ErasePurpose(EncodeDestination(address));
+ return batch.EraseName(EncodeDestination(address));
}
size_t CWallet::KeypoolCountExternalKeys() const
@@ -3334,7 +3404,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(trusted_parents))
+ if (!IsTrusted(wtx, trusted_parents))
continue;
if (wtx.IsImmatureCoinBase())
@@ -3353,9 +3423,6 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
-
- if (!balances.count(addr))
- balances[addr] = 0;
balances[addr] += n;
}
}
@@ -3716,7 +3783,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
+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:
//
@@ -3724,54 +3791,25 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
- LOCK(cs_wallets);
- const fs::path& wallet_path = location.GetPath();
+ const fs::path& wallet_path = fs::absolute(name, GetWalletDir());
fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
- (path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
+ (path_type == fs::regular_file && fs::path(name).filename() == name))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- location.GetName(), GetWalletDir()));
- return false;
- }
-
- // Make sure that the wallet path doesn't clash with an existing wallet path
- if (IsWalletLoaded(wallet_path)) {
- error_string = Untranslated(strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName()));
- return false;
- }
-
- // Keep same database environment instance across Verify/Recover calls below.
- std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(wallet_path);
-
- try {
- return database->Verify(error_string);
- } catch (const fs::filesystem_error& e) {
- error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
- return false;
+ name, GetWalletDir()));
+ status = DatabaseStatus::FAILED_BAD_PATH;
+ return nullptr;
}
+ return MakeDatabase(wallet_path, options, status, error_string);
}
-std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
+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 = WalletDataFilePath(location.GetPath()).string();
-
- // needed to restore wallet transaction meta data after -zapwallettxes
- std::list<CWalletTx> vWtx;
-
- if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- chain.initMessage(_("Zapping all transactions from wallet...").translated);
-
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, CreateWalletDatabase(location.GetPath()));
- DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
- if (nZapWalletRet != DBErrors::LOAD_OK) {
- error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
- return nullptr;
- }
- }
+ const std::string& walletFile = database->Filename();
chain.initMessage(_("Loading wallet...").translated);
@@ -3779,7 +3817,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
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, location, CreateWalletDatabase(location.GetPath())), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, name, std::move(database)), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
@@ -3878,6 +3916,22 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->m_min_fee = CFeeRate(n);
}
+ if (gArgs.IsArgSet("-maxapsfee")) {
+ const std::string max_aps_fee{gArgs.GetArg("-maxapsfee", "")};
+ CAmount n = 0;
+ if (max_aps_fee == "-1") {
+ n = -1;
+ } else if (!ParseMoney(max_aps_fee, n)) {
+ error = AmountErrMsg("maxapsfee", max_aps_fee);
+ return nullptr;
+ }
+ if (n > HIGH_APS_FEE) {
+ warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") +
+ _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection."));
+ }
+ walletInstance->m_max_aps_fee = n;
+ }
+
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
@@ -4033,30 +4087,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->database->IncrementUpdateCounter();
-
- // Restore wallet transaction metadata after -zapwallettxes=1
- if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")
- {
- WalletBatch batch(*walletInstance->database);
-
- for (const CWalletTx& wtxOld : vWtx)
- {
- uint256 hash = wtxOld.GetHash();
- std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);
- if (mi != walletInstance->mapWallet.end())
- {
- const CWalletTx* copyFrom = &wtxOld;
- CWalletTx* copyTo = &mi->second;
- copyTo->mapValue = copyFrom->mapValue;
- copyTo->vOrderForm = copyFrom->vOrderForm;
- copyTo->nTimeReceived = copyFrom->nTimeReceived;
- copyTo->nTimeSmart = copyFrom->nTimeSmart;
- copyTo->fFromMe = copyFrom->fFromMe;
- copyTo->nOrderPos = copyFrom->nOrderPos;
- batch.WriteTx(*copyTo);
- }
- }
- }
}
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a761caf38c..74de55dcb5 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -50,19 +50,14 @@ struct bilingual_str;
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
-
-enum class WalletCreationStatus {
- SUCCESS,
- CREATION_FAILED,
- ENCRYPTION_FAILED
-};
-
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
+std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
//! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
@@ -72,6 +67,16 @@ static const CAmount DEFAULT_FALLBACK_FEE = 0;
static const CAmount DEFAULT_DISCARD_FEE = 10000;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
+/**
+ * maximum fee increase allowed to do partial spend avoidance, even for nodes with this feature disabled by default
+ *
+ * A value of -1 disables this feature completely.
+ * A value of 0 (current default) means to attempt to do partial spend avoidance, and use its results if the fees remain *unchanged*
+ * A value > 0 means to do partial spend avoidance if the fee difference against a regular coin selection instance is in the range [0..value].
+ */
+static const CAmount DEFAULT_MAX_AVOIDPARTIALSPEND_FEE = 0;
+//! discourage APS fee higher than this amount
+constexpr CAmount HIGH_APS_FEE{COIN / 10000};
//! minimum recommended increment for BIP 125 replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
//! Default for -spendzeroconfchange
@@ -217,7 +222,7 @@ static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
nOrderPos = -1; // TODO: calculate elsewhere
return;
}
- nOrderPos = atoi64(mapValue["n"].c_str());
+ nOrderPos = atoi64(mapValue["n"]);
}
@@ -265,12 +270,12 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet,
class CWalletTx
{
private:
- const CWallet* pwallet;
+ 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 const uint256 ABANDON_HASH;
+ static constexpr const uint256& ABANDON_HASH = uint256::ONE;
public:
/**
@@ -492,7 +497,6 @@ public:
bool InMempool() const;
bool IsTrusted() const;
- bool IsTrusted(std::set<uint256>& trusted_parents) const;
int64_t GetTxTime() const;
@@ -690,8 +694,8 @@ private:
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
- /** Wallet location which includes wallet name (see WalletLocation). */
- WalletLocation m_location;
+ /** Wallet name: relative directory name or "" for default wallet. */
+ std::string m_name;
/** Internal database handle. */
std::unique_ptr<WalletDatabase> database;
@@ -719,6 +723,8 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
+ 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);
+
public:
/*
* Main wallet lock.
@@ -733,7 +739,7 @@ public:
{
return *database;
}
- WalletDatabase& GetDatabase() override { return *database; }
+ WalletDatabase& GetDatabase() const override { return *database; }
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
@@ -743,20 +749,18 @@ public:
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 WalletLocation& GetLocation() const { return m_location; }
-
/** Get a name for this wallet for logging/debugging purposes.
*/
- const std::string& GetName() const { return m_location.GetName(); }
+ const std::string& GetName() const { return m_name; }
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
+ CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
: m_chain(chain),
- m_location(location),
+ m_name(name),
database(std::move(database))
{
}
@@ -793,7 +797,8 @@ public:
/** Interface for accessing chain state. */
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
- const CWalletTx* GetWalletTx(const uint256& hash) const;
+ const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
@@ -895,7 +900,7 @@ public:
CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true);
bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void transactionAddedToMempool(const CTransactionRef& tx) override;
+ void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
void blockConnected(const CBlock& block, int height) override;
void blockDisconnected(const CBlock& block, int height) override;
void updatedBlockTip() override;
@@ -917,7 +922,7 @@ public:
uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
- void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override;
+ void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
struct Balance {
@@ -969,7 +974,7 @@ public:
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
- bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign = true);
+ bool 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 = true);
/**
* Submit the transaction to the node's mempool and then relay to peers.
* Should be called after CreateTransaction unless you want to abort
@@ -1008,6 +1013,7 @@ public:
*/
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
+ 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};
/**
* Default output type for change outputs. When unset, automatically choose type
@@ -1038,20 +1044,20 @@ public:
bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
- isminetype IsMine(const CTxDestination& dest) const;
- isminetype IsMine(const CScript& script) const;
- isminetype IsMine(const CTxIn& txin) const;
+ isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Returns amount of debit if the input matches the
* filter, otherwise returns 0
*/
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
- isminetype IsMine(const CTxOut& txout) const;
+ isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
- bool IsChange(const CTxOut& txout) const;
- bool IsChange(const CScript& script) const;
- CAmount GetChange(const CTxOut& txout) const;
- bool IsMine(const CTransaction& tx) const;
+ bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
@@ -1062,7 +1068,6 @@ public:
void chainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(bool& fFirstRunRet);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
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);
@@ -1140,11 +1145,8 @@ public:
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
- //! Verify wallet naming and perform salvage on the wallet if required
- static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
-
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
+ 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
@@ -1166,7 +1168,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1272,7 +1274,7 @@ public:
void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
- void SetupDescriptorScriptPubKeyMans();
+ void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;
@@ -1327,4 +1329,11 @@ public:
// be IsAllFromMe).
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+
+//! Add wallet name to persistent configuration so it will be loaded on startup.
+bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+
+//! Remove wallet name from persistent configuration so it will not be loaded on startup.
+bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index fa6814d0d3..0092a29cb4 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -13,6 +13,9 @@
#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
+#include <util/translation.h>
+#include <wallet/bdb.h>
+#include <wallet/sqlite.h>
#include <wallet/wallet.h>
#include <atomic>
@@ -263,13 +266,17 @@ public:
static bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
- CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+ CWalletScanState &wss, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
// Unserialize
// Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other
ssKey >> strType;
+ // If we have a filter, check if this matches the filter
+ if (filter_fn && !filter_fn(strType)) {
+ return true;
+ }
if (strType == DBKeys::NAME) {
std::string strAddress;
ssKey >> strAddress;
@@ -668,11 +675,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return true;
}
-bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr)
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn)
{
CWalletScanState dummy_wss;
LOCK(pwallet->cs_wallet);
- return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr);
+ return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr, filter_fn);
}
bool WalletBatch::IsKeyType(const std::string& strType)
@@ -926,23 +933,6 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u
return DBErrors::LOAD_OK;
}
-DBErrors WalletBatch::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- // build list of wallet TXs
- std::vector<uint256> vTxHash;
- DBErrors err = FindWalletTx(vTxHash, vWtx);
- if (err != DBErrors::LOAD_OK)
- return err;
-
- // erase each wallet TX
- for (const uint256& hash : vTxHash) {
- if (!EraseTx(hash))
- return DBErrors::CORRUPT;
- }
-
- return DBErrors::LOAD_OK;
-}
-
void MaybeCompactWalletDB()
{
static std::atomic<bool> fOneThread(false);
@@ -1006,16 +996,63 @@ bool WalletBatch::TxnAbort()
return m_batch->TxnAbort();
}
-bool IsWalletLoaded(const fs::path& wallet_path)
+std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
- return IsBDBWalletLoaded(wallet_path);
-}
+ bool exists;
+ try {
+ exists = fs::symlink_status(path).type() != fs::file_not_found;
+ } catch (const fs::filesystem_error& e) {
+ error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e)));
+ status = DatabaseStatus::FAILED_BAD_PATH;
+ return nullptr;
+ }
-/** Return object for accessing database at specified path. */
-std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path)
-{
- std::string filename;
- return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
+ Optional<DatabaseFormat> format;
+ if (exists) {
+ if (ExistsBerkeleyDatabase(path)) {
+ format = DatabaseFormat::BERKELEY;
+ }
+ if (ExistsSQLiteDatabase(path)) {
+ if (format) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string()));
+ status = DatabaseStatus::FAILED_BAD_FORMAT;
+ return nullptr;
+ }
+ format = DatabaseFormat::SQLITE;
+ }
+ } else if (options.require_existing) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
+ status = DatabaseStatus::FAILED_NOT_FOUND;
+ return nullptr;
+ }
+
+ if (!format && options.require_existing) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string()));
+ status = DatabaseStatus::FAILED_BAD_FORMAT;
+ return nullptr;
+ }
+
+ if (format && options.require_create) {
+ error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string()));
+ status = DatabaseStatus::FAILED_ALREADY_EXISTS;
+ return nullptr;
+ }
+
+ // A db already exists so format is set, but options also specifies the format, so make sure they agree
+ if (format && options.require_format && format != options.require_format) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", path.string()));
+ status = DatabaseStatus::FAILED_BAD_FORMAT;
+ return nullptr;
+ }
+
+ // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
+ if (!format && options.require_format) format = options.require_format;
+
+ if (format && format == DatabaseFormat::SQLITE) {
+ return MakeSQLiteDatabase(path, options, status, error);
+ }
+
+ return MakeBerkeleyDatabase(path, options, status, error);
}
/** Return object for accessing dummy database with no read/write capabilities. */
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 7c5bf7652b..7f1b86e458 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -204,8 +204,8 @@ private:
}
public:
- explicit WalletBatch(WalletDatabase& database, const char* pszMode = "r+", bool _fFlushOnClose = true) :
- m_batch(database.MakeBatch(pszMode, _fFlushOnClose)),
+ explicit WalletBatch(WalletDatabase &database, bool _fFlushOnClose = true) :
+ m_batch(database.MakeBatch(_fFlushOnClose)),
m_database(database)
{
}
@@ -257,14 +257,9 @@ public:
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
- /* verifies the database environment */
- static bool VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr);
- /* verifies the database file */
- static bool VerifyDatabaseFile(const fs::path& wallet_path, bilingual_str& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
@@ -284,14 +279,11 @@ private:
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
void MaybeCompactWalletDB();
-//! Unserialize a given Key-Value pair and load it into the wallet
-bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
-
-/** Return whether a wallet database is currently loaded. */
-bool IsWalletLoaded(const fs::path& wallet_path);
+//! Callback for filtering key types to deserialize in ReadKeyValue
+using KeyFilterFn = std::function<bool(const std::string&)>;
-/** Return object for accessing database at specified path. */
-std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path);
+//! Unserialize a given Key-Value pair and load it into the wallet
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr);
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 9f25b1ae7d..0e18d6a740 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -21,21 +21,9 @@ static void WalletToolReleaseWallet(CWallet* wallet)
delete wallet;
}
-static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
+static void WalletCreate(CWallet* wallet_instance)
{
- if (fs::exists(path)) {
- tfm::format(std::cerr, "Error: File exists already\n");
- return nullptr;
- }
- // dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
LOCK(wallet_instance->cs_wallet);
- bool first_run = true;
- DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
- if (load_wallet_ret != DBErrors::LOAD_OK) {
- tfm::format(std::cerr, "Error creating %s", name);
- return nullptr;
- }
wallet_instance->SetMinVersion(FEATURE_HD_SPLIT);
@@ -46,18 +34,26 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
- return wallet_instance;
}
-static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
+static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create)
{
- if (!fs::exists(path)) {
- tfm::format(std::cerr, "Error: Wallet files does not exist\n");
+ DatabaseOptions options;
+ DatabaseStatus status;
+ if (create) {
+ options.require_create = true;
+ } else {
+ options.require_existing = true;
+ }
+ bilingual_str error;
+ std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
+ if (!database) {
+ tfm::format(std::cerr, "%s\n", error.original);
return nullptr;
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
bool first_run;
@@ -89,6 +85,8 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
}
}
+ if (create) WalletCreate(wallet_instance.get());
+
return wallet_instance;
}
@@ -97,6 +95,9 @@ static void WalletShowInfo(CWallet* wallet_instance)
LOCK(wallet_instance->cs_wallet);
tfm::format(std::cout, "Wallet info\n===========\n");
+ tfm::format(std::cout, "Name: %s\n", wallet_instance->GetName());
+ tfm::format(std::cout, "Format: %s\n", wallet_instance->GetDatabase().Format());
+ tfm::format(std::cout, "Descriptors: %s\n", wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) ? "yes" : "no");
tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
@@ -104,50 +105,35 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
-static bool SalvageWallet(const fs::path& path)
-{
- // Create a Database handle to allow for the db to be initialized before recovery
- std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(path);
-
- // Initialize the environment before recovery
- bilingual_str error_string;
- try {
- database->Verify(error_string);
- } catch (const fs::filesystem_error& e) {
- error_string = Untranslated(strprintf("Error loading wallet. %s", fsbridge::get_filesystem_error_message(e)));
- }
- if (!error_string.original.empty()) {
- tfm::format(std::cerr, "Failed to open wallet for salvage :%s\n", error_string.original);
- return false;
- }
-
- // Perform the recovery
- return RecoverDatabaseFile(path);
-}
-
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
{
fs::path path = fs::absolute(name, GetWalletDir());
if (command == "create") {
- std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
+ std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
}
} else if (command == "info" || command == "salvage") {
- if (!fs::exists(path)) {
- tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
- return false;
- }
-
if (command == "info") {
- std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
+ std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ false);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
} else if (command == "salvage") {
- return SalvageWallet(path);
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
+ bool ret = RecoverDatabaseFile(path, error, warnings);
+ if (!ret) {
+ for (const auto& warning : warnings) {
+ tfm::format(std::cerr, "%s\n", warning.original);
+ }
+ if (!error.empty()) {
+ tfm::format(std::cerr, "%s\n", error.original);
+ }
+ }
+ return ret;
}
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index 8ee3355f02..d0b8d6812a 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -9,8 +9,6 @@
namespace WalletTool {
-std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
-std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
void WalletShowInfo(CWallet* wallet_instance);
bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 8bac0608a9..a2a55f9751 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -7,6 +7,9 @@
#include <logging.h>
#include <util/system.h>
+bool ExistsBerkeleyDatabase(const fs::path& path);
+bool ExistsSQLiteDatabase(const fs::path& path);
+
fs::path GetWalletDir()
{
fs::path path;
@@ -29,31 +32,6 @@ fs::path GetWalletDir()
return path;
}
-static bool IsBerkeleyBtree(const fs::path& path)
-{
- if (!fs::exists(path)) return false;
-
- // A Berkeley DB Btree file has at least 4K.
- // This check also prevents opening lock files.
- boost::system::error_code ec;
- auto size = fs::file_size(path, ec);
- if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
- if (size < 4096) return false;
-
- fsbridge::ifstream file(path, std::ios::binary);
- if (!file.is_open()) return false;
-
- file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
- uint32_t data = 0;
- file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
-
- // Berkeley DB Btree magic bytes, from:
- // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
- // - big endian systems - 00 05 31 62
- // - little endian systems - 62 31 05 00
- return data == 0x00053162 || data == 0x62310500;
-}
-
std::vector<fs::path> ListWalletDir()
{
const fs::path wallet_dir = GetWalletDir();
@@ -71,10 +49,11 @@ std::vector<fs::path> ListWalletDir()
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
const fs::path path = it->path().string().substr(offset);
- if (it->status().type() == fs::directory_file && IsBerkeleyBtree(it->path() / "wallet.dat")) {
+ if (it->status().type() == fs::directory_file &&
+ (ExistsBerkeleyDatabase(it->path()) || ExistsSQLiteDatabase(it->path()))) {
// Found a directory which contains wallet.dat btree file, add it as a wallet.
paths.emplace_back(path);
- } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) {
+ } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) {
if (it->path().filename() == "wallet.dat") {
// Found top-level wallet.dat btree file, add top level directory ""
// as a wallet.
@@ -91,19 +70,3 @@ std::vector<fs::path> ListWalletDir()
return paths;
}
-
-WalletLocation::WalletLocation(const std::string& name)
- : m_name(name)
- , m_path(fs::absolute(name, GetWalletDir()))
-{
-}
-
-bool WalletLocation::Exists() const
-{
- fs::path path = m_path;
- // For the default wallet, check specifically for the wallet.dat file
- if (m_name.empty()) {
- path = fs::absolute("wallet.dat", m_path);
- }
- return fs::symlink_status(path).type() != fs::file_not_found;
-}
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index a4e4fda8a1..afdcb2e18a 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -67,26 +67,6 @@ fs::path GetWalletDir();
//! Get wallets in wallet directory.
std::vector<fs::path> ListWalletDir();
-//! The WalletLocation class provides wallet information.
-class WalletLocation final
-{
- std::string m_name;
- fs::path m_path;
-
-public:
- explicit WalletLocation() {}
- explicit WalletLocation(const std::string& name);
-
- //! Get wallet name.
- const std::string& GetName() const { return m_name; }
-
- //! Get wallet absolute path.
- const fs::path& GetPath() const { return m_path; }
-
- //! Return whether the wallet exists.
- bool Exists() const;
-};
-
/** Descriptor with some wallet metadata */
class WalletDescriptor
{
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index aae760adde..3938f6fd2c 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -4,6 +4,8 @@
#include <zmq/zmqabstractnotifier.h>
+#include <cassert>
+
const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM;
CZMQAbstractNotifier::~CZMQAbstractNotifier()
@@ -20,3 +22,23 @@ bool CZMQAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/
{
return true;
}
+
+bool CZMQAbstractNotifier::NotifyBlockConnect(const CBlockIndex * /*CBlockIndex*/)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyBlockDisconnect(const CBlockIndex * /*CBlockIndex*/)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyTransactionAcceptance(const CTransaction &/*transaction*/, uint64_t mempool_sequence)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyTransactionRemoval(const CTransaction &/*transaction*/, uint64_t mempool_sequence)
+{
+ return true;
+}
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 887dde7b27..dddba8d6b6 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -5,12 +5,16 @@
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
-#include <zmq/zmqconfig.h>
+#include <util/memory.h>
+
+#include <memory>
+#include <string>
class CBlockIndex;
+class CTransaction;
class CZMQAbstractNotifier;
-typedef CZMQAbstractNotifier* (*CZMQNotifierFactory)();
+using CZMQNotifierFactory = std::unique_ptr<CZMQAbstractNotifier> (*)();
class CZMQAbstractNotifier
{
@@ -21,9 +25,9 @@ public:
virtual ~CZMQAbstractNotifier();
template <typename T>
- static CZMQAbstractNotifier* Create()
+ static std::unique_ptr<CZMQAbstractNotifier> Create()
{
- return new T();
+ return MakeUnique<T>();
}
std::string GetType() const { return type; }
@@ -40,7 +44,17 @@ public:
virtual bool Initialize(void *pcontext) = 0;
virtual void Shutdown() = 0;
+ // Notifies of ConnectTip result, i.e., new active tip only
virtual bool NotifyBlock(const CBlockIndex *pindex);
+ // Notifies of every block connection
+ virtual bool NotifyBlockConnect(const CBlockIndex *pindex);
+ // Notifies of every block disconnection
+ virtual bool NotifyBlockDisconnect(const CBlockIndex *pindex);
+ // Notifies of every mempool acceptance
+ virtual bool NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence);
+ // Notifies of every mempool removal, except inclusion in blocks
+ virtual bool NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence);
+ // Notifies of transactions added to mempool or appearing in blocks
virtual bool NotifyTransaction(const CTransaction &transaction);
protected:
diff --git a/src/zmq/zmqconfig.h b/src/zmq/zmqconfig.h
deleted file mode 100644
index 5f0036206d..0000000000
--- a/src/zmq/zmqconfig.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2014-2019 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_ZMQ_ZMQCONFIG_H
-#define BITCOIN_ZMQ_ZMQCONFIG_H
-
-#if defined(HAVE_CONFIG_H)
-#include <config/bitcoin-config.h>
-#endif
-
-#include <stdarg.h>
-
-#if ENABLE_ZMQ
-#include <zmq.h>
-#endif
-
-#include <primitives/transaction.h>
-
-void zmqError(const char *str);
-
-#endif // BITCOIN_ZMQ_ZMQCONFIG_H
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index d55b106e04..a2f994d7df 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -4,15 +4,13 @@
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqpublishnotifier.h>
+#include <zmq/zmqutil.h>
+
+#include <zmq.h>
#include <validation.h>
#include <util/system.h>
-void zmqError(const char *str)
-{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
-}
-
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr)
{
}
@@ -20,61 +18,51 @@ CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr)
CZMQNotificationInterface::~CZMQNotificationInterface()
{
Shutdown();
-
- for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
- {
- delete *i;
- }
}
std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotifiers() const
{
std::list<const CZMQAbstractNotifier*> result;
- for (const auto* n : notifiers) {
- result.push_back(n);
+ for (const auto& n : notifiers) {
+ result.push_back(n.get());
}
return result;
}
CZMQNotificationInterface* CZMQNotificationInterface::Create()
{
- CZMQNotificationInterface* notificationInterface = nullptr;
std::map<std::string, CZMQNotifierFactory> factories;
- std::list<CZMQAbstractNotifier*> notifiers;
-
factories["pubhashblock"] = CZMQAbstractNotifier::Create<CZMQPublishHashBlockNotifier>;
factories["pubhashtx"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionNotifier>;
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
+ factories["pubsequence"] = CZMQAbstractNotifier::Create<CZMQPublishSequenceNotifier>;
+ std::list<std::unique_ptr<CZMQAbstractNotifier>> notifiers;
for (const auto& entry : factories)
{
std::string arg("-zmq" + entry.first);
- if (gArgs.IsArgSet(arg))
- {
- CZMQNotifierFactory factory = entry.second;
- std::string address = gArgs.GetArg(arg, "");
- CZMQAbstractNotifier *notifier = factory();
+ const auto& factory = entry.second;
+ for (const std::string& address : gArgs.GetArgs(arg)) {
+ std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
- notifiers.push_back(notifier);
+ notifiers.push_back(std::move(notifier));
}
}
if (!notifiers.empty())
{
- notificationInterface = new CZMQNotificationInterface();
- notificationInterface->notifiers = notifiers;
+ std::unique_ptr<CZMQNotificationInterface> notificationInterface(new CZMQNotificationInterface());
+ notificationInterface->notifiers = std::move(notifiers);
- if (!notificationInterface->Initialize())
- {
- delete notificationInterface;
- notificationInterface = nullptr;
+ if (notificationInterface->Initialize()) {
+ return notificationInterface.release();
}
}
- return notificationInterface;
+ return nullptr;
}
// Called at startup to conditionally set up ZMQ socket(s)
@@ -95,26 +83,15 @@ bool CZMQNotificationInterface::Initialize()
return false;
}
- std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin();
- for (; i!=notifiers.end(); ++i)
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->Initialize(pcontext))
- {
+ for (auto& notifier : notifiers) {
+ if (notifier->Initialize(pcontext)) {
LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
- }
- else
- {
+ } else {
LogPrint(BCLog::ZMQ, "zmq: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
- break;
+ return false;
}
}
- if (i!=notifiers.end())
- {
- return false;
- }
-
return true;
}
@@ -124,9 +101,7 @@ void CZMQNotificationInterface::Shutdown()
LogPrint(BCLog::ZMQ, "zmq: Shutdown notification interface\n");
if (pcontext)
{
- for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
- {
- CZMQAbstractNotifier *notifier = *i;
+ for (auto& notifier : notifiers) {
LogPrint(BCLog::ZMQ, "zmq: Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
notifier->Shutdown();
}
@@ -136,61 +111,81 @@ void CZMQNotificationInterface::Shutdown()
}
}
-void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
-{
- if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones
- return;
+namespace {
- for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->NotifyBlock(pindexNew))
- {
- i++;
- }
- else
- {
+template <typename Function>
+void TryForEachAndRemoveFailed(std::list<std::unique_ptr<CZMQAbstractNotifier>>& notifiers, const Function& func)
+{
+ for (auto i = notifiers.begin(); i != notifiers.end(); ) {
+ CZMQAbstractNotifier* notifier = i->get();
+ if (func(notifier)) {
+ ++i;
+ } else {
notifier->Shutdown();
i = notifiers.erase(i);
}
}
}
-void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
+} // anonymous namespace
+
+void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
+{
+ if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones
+ return;
+
+ TryForEachAndRemoveFailed(notifiers, [pindexNew](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlock(pindexNew);
+ });
+}
+
+void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx, uint64_t mempool_sequence)
{
- // Used by BlockConnected and BlockDisconnected as well, because they're
- // all the same external callback.
const CTransaction& tx = *ptx;
- for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->NotifyTransaction(tx))
- {
- i++;
- }
- else
- {
- notifier->Shutdown();
- i = notifiers.erase(i);
- }
- }
+ TryForEachAndRemoveFailed(notifiers, [&tx, mempool_sequence](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx) && notifier->NotifyTransactionAcceptance(tx, mempool_sequence);
+ });
+}
+
+void CZMQNotificationInterface::TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
+{
+ // Called for all non-block inclusion reasons
+ const CTransaction& tx = *ptx;
+
+ TryForEachAndRemoveFailed(notifiers, [&tx, mempool_sequence](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransactionRemoval(tx, mempool_sequence);
+ });
}
void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected)
{
for (const CTransactionRef& ptx : pblock->vtx) {
- // Do a normal notify for each transaction added in the block
- TransactionAddedToMempool(ptx);
+ const CTransaction& tx = *ptx;
+ TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx);
+ });
}
+
+ // Next we notify BlockConnect listeners for *all* blocks
+ TryForEachAndRemoveFailed(notifiers, [pindexConnected](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlockConnect(pindexConnected);
+ });
}
void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected)
{
for (const CTransactionRef& ptx : pblock->vtx) {
- // Do a normal notify for each transaction removed in block disconnection
- TransactionAddedToMempool(ptx);
+ const CTransaction& tx = *ptx;
+ TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx);
+ });
}
+
+ // Next we notify BlockDisconnect listeners for *all* blocks
+ TryForEachAndRemoveFailed(notifiers, [pindexDisconnected](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlockDisconnect(pindexDisconnected);
+ });
}
CZMQNotificationInterface* g_zmq_notification_interface = nullptr;
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 60f3b6148a..788a383517 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -7,6 +7,7 @@
#include <validationinterface.h>
#include <list>
+#include <memory>
class CBlockIndex;
class CZMQAbstractNotifier;
@@ -25,7 +26,8 @@ protected:
void Shutdown();
// CValidationInterface
- void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
@@ -34,7 +36,7 @@ private:
CZMQNotificationInterface();
void *pcontext;
- std::list<CZMQAbstractNotifier*> notifiers;
+ std::list<std::unique_ptr<CZMQAbstractNotifier>> notifiers;
};
extern CZMQNotificationInterface* g_zmq_notification_interface;
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 04806903c2..c0207f9dd6 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -2,13 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <zmq/zmqpublishnotifier.h>
+
#include <chain.h>
#include <chainparams.h>
+#include <rpc/server.h>
#include <streams.h>
-#include <zmq/zmqpublishnotifier.h>
-#include <validation.h>
#include <util/system.h>
-#include <rpc/server.h>
+#include <validation.h>
+#include <zmq/zmqutil.h>
+
+#include <zmq.h>
+
+#include <cstdarg>
+#include <cstddef>
+#include <map>
+#include <string>
+#include <utility>
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
@@ -16,6 +26,7 @@ static const char *MSG_HASHBLOCK = "hashblock";
static const char *MSG_HASHTX = "hashtx";
static const char *MSG_RAWBLOCK = "rawblock";
static const char *MSG_RAWTX = "rawtx";
+static const char *MSG_SEQUENCE = "sequence";
// Internal function to send multipart message
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
@@ -86,6 +97,14 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
+ const int so_keepalive_option {1};
+ rc = zmq_setsockopt(psocket, ZMQ_TCP_KEEPALIVE, &so_keepalive_option, sizeof(so_keepalive_option));
+ if (rc != 0) {
+ zmqError("Failed to set SO_KEEPALIVE");
+ zmq_close(psocket);
+ return false;
+ }
+
rc = zmq_bind(psocket, address.c_str());
if (rc != 0)
{
@@ -141,7 +160,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
psocket = nullptr;
}
-bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* data, size_t size)
+bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void* data, size_t size)
{
assert(psocket);
@@ -161,26 +180,26 @@ bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* d
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s\n", hash.GetHex());
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s to %s\n", hash.GetHex(), this->address);
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
- return SendMessage(MSG_HASHBLOCK, data, 32);
+ return SendZmqMessage(MSG_HASHBLOCK, data, 32);
}
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s\n", hash.GetHex());
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s to %s\n", hash.GetHex(), this->address);
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
- return SendMessage(MSG_HASHTX, data, 32);
+ return SendZmqMessage(MSG_HASHTX, data, 32);
}
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
- LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
+ LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
@@ -196,14 +215,62 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
ss << block;
}
- return SendMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
+ return SendZmqMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
}
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s\n", hash.GetHex());
+ LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ss << transaction;
- return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
+ return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
+}
+
+
+// TODO: Dedup this code to take label char, log string
+bool CZMQPublishSequenceNotifier::NotifyBlockConnect(const CBlockIndex *pindex)
+{
+ uint256 hash = pindex->GetBlockHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish sequence block connect %s to %s\n", hash.GetHex(), this->address);
+ char data[sizeof(uint256)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(data) - 1] = 'C'; // Block (C)onnect
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyBlockDisconnect(const CBlockIndex *pindex)
+{
+ uint256 hash = pindex->GetBlockHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish sequence block disconnect %s to %s\n", hash.GetHex(), this->address);
+ char data[sizeof(uint256)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(data) - 1] = 'D'; // Block (D)isconnect
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence)
+{
+ uint256 hash = transaction.GetHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool acceptance %s to %s\n", hash.GetHex(), this->address);
+ unsigned char data[sizeof(uint256)+sizeof(mempool_sequence)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(uint256)] = 'A'; // Mempool (A)cceptance
+ WriteLE64(data+sizeof(uint256)+1, mempool_sequence);
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence)
+{
+ uint256 hash = transaction.GetHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool removal %s to %s\n", hash.GetHex(), this->address);
+ unsigned char data[sizeof(uint256)+sizeof(mempool_sequence)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(uint256)] = 'R'; // Mempool (R)emoval
+ WriteLE64(data+sizeof(uint256)+1, mempool_sequence);
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
}
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index 278fdb94d2..f13ed6f537 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -22,7 +22,7 @@ public:
* data
* message sequence number
*/
- bool SendMessage(const char *command, const void* data, size_t size);
+ bool SendZmqMessage(const char *command, const void* data, size_t size);
bool Initialize(void *pcontext) override;
void Shutdown() override;
@@ -52,4 +52,13 @@ public:
bool NotifyTransaction(const CTransaction &transaction) override;
};
+class CZMQPublishSequenceNotifier : public CZMQAbstractPublishNotifier
+{
+public:
+ bool NotifyBlockConnect(const CBlockIndex *pindex) override;
+ bool NotifyBlockDisconnect(const CBlockIndex *pindex) override;
+ bool NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence) override;
+ bool NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence) override;
+};
+
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index cce6210129..1dd751b493 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -13,9 +13,9 @@
namespace {
-UniValue getzmqnotifications(const JSONRPCRequest& request)
+static RPCHelpMan getzmqnotifications()
{
- RPCHelpMan{"getzmqnotifications",
+ return RPCHelpMan{"getzmqnotifications",
"\nReturns information about the active ZeroMQ notifications.\n",
{},
RPCResult{
@@ -33,8 +33,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
HelpExampleCli("getzmqnotifications", "")
+ HelpExampleRpc("getzmqnotifications", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue result(UniValue::VARR);
if (g_zmq_notification_interface != nullptr) {
for (const auto* n : g_zmq_notification_interface->GetActiveNotifiers()) {
@@ -47,6 +47,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
const CRPCCommand commands[] =
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
new file mode 100644
index 0000000000..f07a4ae9fd
--- /dev/null
+++ b/src/zmq/zmqutil.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <zmq/zmqutil.h>
+
+#include <logging.h>
+
+#include <zmq.h>
+
+void zmqError(const char* str)
+{
+ LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
+}
diff --git a/src/zmq/zmqutil.h b/src/zmq/zmqutil.h
new file mode 100644
index 0000000000..4c1df5d6db
--- /dev/null
+++ b/src/zmq/zmqutil.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_ZMQ_ZMQUTIL_H
+#define BITCOIN_ZMQ_ZMQUTIL_H
+
+void zmqError(const char* str);
+
+#endif // BITCOIN_ZMQ_ZMQUTIL_H
diff --git a/test/functional/README.md b/test/functional/README.md
index aff5f714f2..82b30fed51 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -87,7 +87,9 @@ P2P messages. These can be found in the following source files:
#### Using the P2P interface
-- [messages.py](test_framework/messages.py) contains all the definitions for objects that pass
+- `P2P`s can be used to test specific P2P protocol behavior.
+[p2p.py](test_framework/p2p.py) contains test framework p2p objects and
+[messages.py](test_framework/messages.py) contains all the definitions for objects passed
over the network (`CBlock`, `CTransaction`, etc, along with the network-level
wrappers for them, `msg_block`, `msg_tx`, etc).
@@ -100,8 +102,22 @@ contains the higher level logic for processing P2P payloads and connecting to
the Bitcoin Core node application logic. For custom behaviour, subclass the
P2PInterface object and override the callback methods.
-- Can be used to write tests where specific P2P protocol behavior is tested.
-Examples tests are [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
+`P2PConnection`s can be used as such:
+
+```python
+p2p_conn = node.add_p2p_connection(P2PInterface())
+p2p_conn.send_and_ping(msg)
+```
+
+They can also be referenced by indexing into a `TestNode`'s `p2ps` list, which
+contains the list of test framework `p2p` objects connected to itself
+(it does not include any `TestNode`s):
+
+```python
+node.p2ps[0].sync_with_ping()
+```
+
+More examples can be found in [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
[p2p_compactblocks.py](p2p_compactblocks.py).
#### Prototyping tests
@@ -127,8 +143,8 @@ Base class for functional tests.
#### [util.py](test_framework/util.py)
Generally useful functions.
-#### [mininode.py](test_framework/mininode.py)
-Basic code to support P2P connectivity to a bitcoind.
+#### [p2p.py](test_framework/p2p.py)
+Test objects for interacting with a bitcoind node over the p2p interface.
#### [script.py](test_framework/script.py)
Utilities for manipulating transaction scripts (originally from python-bitcoinlib)
@@ -157,7 +173,7 @@ way is the use the `profile_with_perf` context manager, e.g.
with node.profile_with_perf("send-big-msgs"):
# Perform activity on the node you're interested in profiling, e.g.:
for _ in range(10000):
- node.p2p.send_message(some_large_message)
+ node.p2ps[0].send_message(some_large_message)
```
To see useful textual output, run
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 34e4999329..c28bb7115f 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -16,17 +16,15 @@ from collections import defaultdict
# Avoid wildcard * imports
from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.messages import CInv, MSG_BLOCK
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PInterface,
- mininode_lock,
msg_block,
msg_getdata,
+ p2p_lock,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- wait_until,
)
# P2PInterface is a class containing callbacks to be executed when a P2P
@@ -116,7 +114,7 @@ class ExampleTest(BitcoinTestFramework):
# In this test, we're not connecting node2 to node0 or node1. Calls to
# sync_all() should not include node2, since we're not expecting it to
# sync.
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_all(self.nodes[0:2])
# Use setup_nodes() to customize the node start behaviour (for example if
@@ -137,7 +135,7 @@ class ExampleTest(BitcoinTestFramework):
"""Main test logic"""
# Create P2P connections will wait for a verack to make sure the connection is fully up
- self.nodes[0].add_p2p_connection(BaseNode())
+ peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -167,14 +165,14 @@ class ExampleTest(BitcoinTestFramework):
height = self.nodes[0].getblockcount()
for _ in range(10):
- # Use the mininode and blocktools functionality to manually build a block
+ # Use the blocktools functionality to manually build a block.
# Calling the generate() rpc is easier, but this allows us to exactly
# control the blocks and transactions.
block = create_block(self.tip, create_coinbase(height+1), self.block_time)
block.solve()
block_message = msg_block(block)
# Send message is used to send a P2P message to the node over our P2PInterface
- self.nodes[0].p2p.send_message(block_message)
+ peer_messaging.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -184,7 +182,7 @@ class ExampleTest(BitcoinTestFramework):
self.nodes[1].waitforblockheight(11)
self.log.info("Connect node2 and node1")
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
self.log.info("Wait for node2 to receive all the blocks from node1")
self.sync_all()
@@ -192,26 +190,27 @@ class ExampleTest(BitcoinTestFramework):
self.log.info("Add P2P connection to node2")
self.nodes[0].disconnect_p2ps()
- self.nodes[2].add_p2p_connection(BaseNode())
+ peer_receiving = self.nodes[2].add_p2p_connection(BaseNode())
self.log.info("Test that node2 propagates all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(MSG_BLOCK, block))
- self.nodes[2].p2p.send_message(getdata_request)
+ peer_receiving.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
# P2PInterface objects.
- wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock)
+ peer_receiving.wait_until(lambda: sorted(blocks) == sorted(list(peer_receiving.block_receive_map.keys())), timeout=5)
self.log.info("Check that each block was received only once")
# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
- # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
- with mininode_lock:
- for block in self.nodes[2].p2p.block_receive_map.values():
+ # and synchronization issues. Note p2p.wait_until() acquires this global lock internally when testing the predicate.
+ with p2p_lock:
+ for block in peer_receiving.block_receive_map.values():
assert_equal(block, 1)
+
if __name__ == '__main__':
ExampleTest().main()
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
index 75267de80b..8abfdef3a1 100755
--- a/test/functional/feature_abortnode.py
+++ b/test/functional/feature_abortnode.py
@@ -11,7 +11,7 @@
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import wait_until, get_datadir_path, connect_nodes
+from test_framework.util import get_datadir_path
import os
@@ -36,12 +36,12 @@ class AbortNodeTest(BitcoinTestFramework):
# attempt.
self.nodes[1].generate(3)
with self.nodes[0].assert_debug_log(["Failed to disconnect block"]):
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.nodes[1].generate(1)
# Check that node0 aborted
self.log.info("Waiting for crash")
- wait_until(lambda: self.nodes[0].is_node_stopped(), timeout=200)
+ self.nodes[0].wait_until_stopped(timeout=200)
self.log.info("Node crashed - now verifying restart fails")
self.nodes[0].assert_start_raises_init_error()
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index f19ee12f95..603d7f5d3b 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -42,7 +42,7 @@ from test_framework.messages import (
msg_block,
msg_headers,
)
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.script import (CScript, OP_TRUE)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 07dd0f8f82..21776d85c9 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -6,7 +6,7 @@
Test various backwards compatibility scenarios. Download the previous node binaries:
-contrib/devtools/previous_release.py -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
v0.15.2 is not required by this test, but it is used in wallet_upgradewallet.py.
Due to a hardfork in regtest, it can't be used to sync nodes.
@@ -40,9 +40,10 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # Pre-release: use to receive coins, swap wallets, etc
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.19.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.18.1
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.17.1
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.16.3
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.17.2
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-wallet=wallet.dat"], # v0.16.3
]
+ self.wallet_names = [self.default_wallet_name]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -54,11 +55,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
None,
190100,
180100,
- 170100,
+ 170200,
160300,
])
self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
def run_test(self):
self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index 1253c45418..60492350ee 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -6,7 +6,7 @@
import time
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
+from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS, add_witness_commitment
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -275,6 +275,8 @@ class BIP68Test(BitcoinTestFramework):
# Advance the time on the node so that we can test timelocks
self.nodes[0].setmocktime(cur_time+600)
+ # Save block template now to use for the reorg later
+ tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
self.nodes[0].generate(1)
assert tx2.hash not in self.nodes[0].getrawmempool()
@@ -318,16 +320,15 @@ class BIP68Test(BitcoinTestFramework):
# diagram above).
# This would cause tx2 to be added back to the mempool, which in turn causes
# tx3 to be removed.
- tip = int(self.nodes[0].getblockhash(self.nodes[0].getblockcount()-1), 16)
- height = self.nodes[0].getblockcount()
for i in range(2):
- block = create_block(tip, create_coinbase(height), cur_time)
- block.nVersion = 3
+ block = create_block(tmpl=tmpl, ntime=cur_time)
block.rehash()
block.solve()
tip = block.sha256
- height += 1
assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(ToHex(block)))
+ tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+ tmpl['previousblockhash'] = '%x' % tip
+ tmpl['transactions'] = []
cur_time += 1
mempool = self.nodes[0].getrawmempool()
@@ -375,9 +376,7 @@ class BIP68Test(BitcoinTestFramework):
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
# make a block that violates bip68; ensure that the tip updates
- tip = int(self.nodes[0].getbestblockhash(), 16)
- block = create_block(tip, create_coinbase(self.nodes[0].getblockcount()+1))
- block.nVersion = 3
+ block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
block.vtx.extend([tx1, tx2, tx3])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index c74761869b..19753d73ef 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -26,7 +26,7 @@ from test_framework.messages import (
uint256_from_compact,
uint256_from_str,
)
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.script import (
CScript,
MAX_SCRIPT_ELEMENT_SIZE,
@@ -53,7 +53,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from data import invalid_txs
-# Use this class for tests that require behavior other than normal "mininode" behavior.
+# Use this class for tests that require behavior other than normal p2p behavior.
# For now, it is used to serialize a bloated varint (b64).
class CBrokenBlock(CBlock):
def initialize(self, base_block):
@@ -1386,14 +1386,14 @@ class FullBlockTest(BitcoinTestFramework):
"""Add a P2P connection to the node.
Helper to connect and wait for version handshake."""
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
# We need to wait for the initial getheaders from the peer before we
# start populating our blockstore. If we don't, then we may run ahead
# to the next subtest before we receive the getheaders. We'd then send
# an INV for the next block and receive two getheaders - one for the
# IBD and one for the INV. We'd respond to both and could get
# unexpectedly disconnected if the DoS score for that error is 50.
- self.nodes[0].p2p.wait_for_getheaders(timeout=timeout)
+ self.helper_peer.wait_for_getheaders(timeout=timeout)
def reconnect_p2p(self, timeout=60):
"""Tear down and bootstrap the P2P connection to the node.
@@ -1407,7 +1407,7 @@ class FullBlockTest(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
if reconnect:
self.reconnect_p2p(timeout=timeout)
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index fd0330924d..aad255c4a9 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -10,7 +10,7 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height
from test_framework.blocktools import create_coinbase, create_block, create_transaction
from test_framework.messages import CTransaction, msg_block, ToHex
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP, CScriptNum
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -75,7 +75,7 @@ class BIP65Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_cltv_info(is_active=False)
@@ -99,7 +99,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -111,9 +111,9 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -136,9 +136,9 @@ class BIP65Test(BitcoinTestFramework):
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)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ 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)
@@ -150,7 +150,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 34e856c1ba..60533e196f 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -14,6 +14,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
self.supports_cli = False
+ self.wallet_names = []
def test_config_file_parser(self):
# Assume node is stopped
@@ -78,6 +79,12 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
+ def test_invalid_command_line_options(self):
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>.',
+ extra_args=['-proxy'],
+ )
+
def test_log_buffer(self):
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0\n']):
self.start_node(0, extra_args=['-noconnect=0'])
@@ -146,6 +153,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.test_networkactive()
self.test_config_file_parser()
+ self.test_invalid_command_line_options()
# Remove the -datadir argument so it doesn't override the config file
self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index dfb3683143..39e8bca751 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -44,7 +44,7 @@ import time
from test_framework.blocktools import create_coinbase, create_block, create_transaction
from test_framework.messages import ToHex, CTransaction
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.script import (
CScript,
OP_CHECKSEQUENCEVERIFY,
@@ -182,10 +182,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
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
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 05fdacd451..3f7efdbded 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -9,7 +9,7 @@ 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.messages import msg_block
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -59,7 +59,7 @@ class BIP66Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_dersig_info(is_active=False)
@@ -84,7 +84,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -97,9 +97,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000002)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -123,9 +123,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)'.format(block.vtx[-1].hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ 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)
@@ -134,7 +134,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 702a1d9995..8a8a0c7614 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -13,7 +13,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
- connect_nodes,
satoshi_round,
)
@@ -232,9 +231,9 @@ class EstimateFeeTest(BitcoinTestFramework):
# so the estimates would not be affected by the splitting transactions
self.start_node(1)
self.start_node(2)
- connect_nodes(self.nodes[1], 0)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[2], 1)
+ self.connect_nodes(1, 0)
+ self.connect_nodes(0, 2)
+ self.connect_nodes(2, 1)
self.sync_all()
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index b56ffe179f..7de9a589be 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -15,7 +15,7 @@ class FilelockTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=None)
- self.nodes[0].start([])
+ self.nodes[0].start()
self.nodes[0].wait_for_rpc_connection()
def run_test(self):
@@ -27,10 +27,11 @@ class FilelockTest(BitcoinTestFramework):
self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
if self.is_wallet_compiled():
+ self.nodes[0].createwallet(self.default_wallet_name)
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
expected_msg = "Error: Error initializing wallet database environment"
- self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + self.default_wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
if __name__ == '__main__':
FilelockTest().main()
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 0dc2839191..d0a94658ff 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -14,7 +14,7 @@ from collections import defaultdict
import time
from test_framework.messages import CInv, MSG_BLOCK, msg_getdata
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, mine_large_block
@@ -145,16 +145,16 @@ class MaxUploadTest(BitcoinTestFramework):
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
- self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)]
for i in range(20):
- self.nodes[0].p2p.send_and_ping(getdata_request)
- assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
+ peer.send_and_ping(getdata_request)
+ assert_equal(peer.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
- self.nodes[0].p2p.send_and_ping(getdata_request)
+ peer.send_and_ping(getdata_request)
self.log.info("Peer still connected after trying to download old block (download permission)")
peer_info = self.nodes[0].getpeerinfo()
diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py
index dbff6f15f2..abf87e8f0c 100755
--- a/test/functional/feature_minchainwork.py
+++ b/test/functional/feature_minchainwork.py
@@ -18,7 +18,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded.
import time
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import connect_nodes, assert_equal
+from test_framework.util import assert_equal
# 2 hashes required per regtest block (with no difficulty adjustment)
REGTEST_WORK_PER_BLOCK = 2
@@ -39,7 +39,7 @@ class MinimumChainWorkTest(BitcoinTestFramework):
# block relay to inbound peers.
self.setup_nodes()
for i in range(self.num_nodes-1):
- connect_nodes(self.nodes[i+1], i)
+ self.connect_nodes(i+1, i)
def run_test(self):
# Start building a chain on node0. node2 shouldn't be able to sync until node1's
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index dd4c318cee..cf102f321c 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -9,9 +9,6 @@ from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, keyhash_to_p2pkh
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
- connect_nodes,
- disconnect_nodes,
hex_str_to_bytes,
)
@@ -19,7 +16,7 @@ from test_framework.util import (
# Windows disallow control characters (0-31) and /\?%:|"<>
FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128
-FILE_CHAR_BLOCKLIST = '/\\?%*:|"<>' if os.name == 'nt' else '/'
+FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/'
def notify_outputname(walletname, txid):
@@ -32,7 +29,7 @@ class NotificationsTest(BitcoinTestFramework):
self.setup_clean_chain = True
def setup_network(self):
- self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHAR_BLOCKLIST)
+ self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHARS_DISALLOWED)
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify")
@@ -46,8 +43,8 @@ class NotificationsTest(BitcoinTestFramework):
"-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))],
["-blockversion=211",
"-rescan",
- "-wallet={}".format(self.wallet),
"-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s')))]]
+ self.wallet_names = [self.default_wallet_name, self.wallet]
super().setup_network()
def run_test(self):
@@ -56,7 +53,7 @@ class NotificationsTest(BitcoinTestFramework):
blocks = self.nodes[1].generatetoaddress(block_count, self.nodes[1].getnewaddress() if self.is_wallet_compiled() else ADDRESS_BCRT1_UNSPENDABLE)
# wait at most 10 seconds for expected number of files before reading the content
- wait_until(lambda: len(os.listdir(self.blocknotify_dir)) == block_count, timeout=10)
+ self.wait_until(lambda: len(os.listdir(self.blocknotify_dir)) == block_count, timeout=10)
# directory content should equal the generated blocks hashes
assert_equal(sorted(blocks), sorted(os.listdir(self.blocknotify_dir)))
@@ -64,7 +61,7 @@ class NotificationsTest(BitcoinTestFramework):
if self.is_wallet_compiled():
self.log.info("test -walletnotify")
# wait at most 10 seconds for expected number of files before reading the content
- wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
+ self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
@@ -76,9 +73,9 @@ class NotificationsTest(BitcoinTestFramework):
self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications
self.start_node(1)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
- wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
+ self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
@@ -127,12 +124,12 @@ class NotificationsTest(BitcoinTestFramework):
# Bump tx2 as bump2 and generate a block on node 0 while
# disconnected, then reconnect and check for notifications on node 1
# about newly confirmed bump2 and newly conflicted tx2.
- disconnect_nodes(self.nodes[0], 1)
+ self.disconnect_nodes(0, 1)
bump2 = self.nodes[0].bumpfee(tx2)["txid"]
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
assert_equal(self.nodes[0].gettransaction(bump2)["confirmations"], 1)
assert_equal(tx2 in self.nodes[1].getrawmempool(), True)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks()
self.expect_wallet_notify([bump2, tx2])
assert_equal(self.nodes[1].gettransaction(bump2)["confirmations"], 1)
@@ -140,7 +137,7 @@ class NotificationsTest(BitcoinTestFramework):
# TODO: add test for `-alertnotify` large fork notifications
def expect_wallet_notify(self, tx_ids):
- wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10)
+ self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10)
assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id in tx_ids), sorted(os.listdir(self.walletnotify_dir)))
for tx_file in os.listdir(self.walletnotify_dir):
os.remove(os.path.join(self.walletnotify_dir, tx_file))
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index ff55cb76d9..d1196a4bbd 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -14,7 +14,7 @@ Generate 427 more blocks.
"""
import time
-from test_framework.blocktools import create_coinbase, create_block, create_transaction, add_witness_commitment
+from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS, create_block, create_transaction, add_witness_commitment
from test_framework.messages import CTransaction
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
@@ -37,14 +37,15 @@ def trueDummy(tx):
class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 1
+ # Need two nodes only so GBT doesn't complain that it's not connected
+ self.num_nodes = 2
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 = [[
'-segwitheight=432',
'-addresstype=legacy',
- ]]
+ ]] * 2
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -61,7 +62,6 @@ class NULLDUMMYTest(BitcoinTestFramework):
coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
self.nodes[0].generate(427) # Block 429
self.lastblockhash = self.nodes[0].getbestblockhash()
- self.tip = int("0x" + self.lastblockhash, 0)
self.lastblockheight = 429
self.lastblocktime = int(time.time()) + 429
@@ -102,8 +102,10 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.block_submit(self.nodes[0], test6txs, True, True)
def block_submit(self, node, txs, witness=False, accept=False):
- block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1)
- block.nVersion = 4
+ tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+ assert_equal(tmpl['previousblockhash'], self.lastblockhash)
+ assert_equal(tmpl['height'], self.lastblockheight + 1)
+ block = create_block(tmpl=tmpl, ntime=self.lastblocktime + 1)
for tx in txs:
tx.rehash()
block.vtx.append(tx)
@@ -114,7 +116,6 @@ class NULLDUMMYTest(BitcoinTestFramework):
assert_equal(None if accept else 'block-validation-failed', node.submitblock(block.serialize().hex()))
if (accept):
assert_equal(node.getbestblockhash(), block.hash)
- self.tip = block.sha256
self.lastblockhash = block.hash
self.lastblocktime += 1
self.lastblockheight += 1
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index be323d355e..dfae58e860 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -18,8 +18,9 @@ Test plan:
- proxy on IPv6
- Create various proxies (as threads)
-- Create bitcoinds that connect to them
-- Manipulate the bitcoinds using addnode (onetry) an observe effects
+- Create nodes that connect to them
+- Manipulate the peer connections using addnode (onetry) and observe effects
+- Test the getpeerinfo `network` field for the peer
addnode connect to IPv4
addnode connect to IPv6
@@ -40,6 +41,12 @@ from test_framework.util import (
from test_framework.netutil import test_ipv6_local
RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
+# From GetNetworkName() in netbase.cpp:
+NET_UNROUTABLE = ""
+NET_IPV4 = "ipv4"
+NET_IPV6 = "ipv6"
+NET_ONION = "onion"
+
class ProxyTest(BitcoinTestFramework):
def set_test_params(self):
@@ -90,10 +97,16 @@ class ProxyTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=args)
self.start_nodes()
+ def network_test(self, node, addr, network):
+ for peer in node.getpeerinfo():
+ if peer["addr"] == addr:
+ assert_equal(peer["network"], network)
+
def node_test(self, node, proxies, auth, test_onion=True):
rv = []
- # Test: outgoing IPv4 connection through node
- node.addnode("15.61.23.23:1234", "onetry")
+ addr = "15.61.23.23:1234"
+ self.log.debug("Test: outgoing IPv4 connection through node for address {}".format(addr))
+ node.addnode(addr, "onetry")
cmd = proxies[0].queue.get()
assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
@@ -104,10 +117,12 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)
rv.append(cmd)
+ self.network_test(node, addr, network=NET_IPV4)
if self.have_ipv6:
- # Test: outgoing IPv6 connection through node
- node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
+ addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
+ self.log.debug("Test: outgoing IPv6 connection through node for address {}".format(addr))
+ node.addnode(addr, "onetry")
cmd = proxies[1].queue.get()
assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
@@ -118,10 +133,12 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)
rv.append(cmd)
+ self.network_test(node, addr, network=NET_IPV6)
if test_onion:
- # Test: outgoing onion connection through node
- node.addnode("bitcoinostk4e4re.onion:8333", "onetry")
+ addr = "bitcoinostk4e4re.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)
@@ -131,9 +148,11 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)
rv.append(cmd)
+ self.network_test(node, addr, network=NET_ONION)
- # Test: outgoing DNS name connection through node
- node.addnode("node.noumenon:8333", "onetry")
+ addr = "node.noumenon:8333"
+ self.log.debug("Test: outgoing DNS name connection through node for address {}".format(addr))
+ node.addnode(addr, "onetry")
cmd = proxies[3].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
@@ -143,6 +162,7 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(cmd.username, None)
assert_equal(cmd.password, None)
rv.append(cmd)
+ self.network_test(node, addr, network=NET_UNROUTABLE)
return rv
@@ -197,5 +217,6 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n3[net]['proxy_randomize_credentials'], False)
assert_equal(n3['onion']['reachable'], False)
+
if __name__ == '__main__':
ProxyTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 02fa88f7c8..f09bffe2d4 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -18,9 +18,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- connect_nodes,
- disconnect_nodes,
- wait_until,
)
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
@@ -103,18 +100,17 @@ class PruneTest(BitcoinTestFramework):
self.prunedir = os.path.join(self.nodes[2].datadir, self.chain, 'blocks', '')
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[0], 3)
- connect_nodes(self.nodes[0], 4)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
+ self.connect_nodes(0, 3)
+ self.connect_nodes(0, 4)
self.sync_blocks(self.nodes[0:5])
def setup_nodes(self):
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
- for n in self.nodes:
- n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=False)
+ self.import_deterministic_coinbase_privkeys()
def create_big_chain(self):
# Start by creating some coinbases we can spend later
@@ -136,7 +132,7 @@ class PruneTest(BitcoinTestFramework):
mine_large_blocks(self.nodes[0], 25)
# Wait for blk00000.dat to be pruned
- wait_until(lambda: not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), timeout=30)
+ self.wait_until(lambda: not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), timeout=30)
self.log.info("Success")
usage = calc_usage(self.prunedir)
@@ -150,8 +146,8 @@ class PruneTest(BitcoinTestFramework):
for _ in range(12):
# Disconnect node 0 so it can mine a longer reorg chain without knowing about node 1's soon-to-be-stale chain
# Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects
- disconnect_nodes(self.nodes[0], 1)
- disconnect_nodes(self.nodes[0], 2)
+ self.disconnect_nodes(0, 1)
+ self.disconnect_nodes(0, 2)
# Mine 24 blocks in node 1
mine_large_blocks(self.nodes[1], 24)
@@ -159,8 +155,8 @@ class PruneTest(BitcoinTestFramework):
mine_large_blocks(self.nodes[0], 25)
# Create connections in the order so both nodes can see the reorg at the same time
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
self.sync_blocks(self.nodes[0:3])
self.log.info("Usage can be over target because of high stale rate: %d" % calc_usage(self.prunedir))
@@ -189,15 +185,15 @@ class PruneTest(BitcoinTestFramework):
self.log.info("New best height: %d" % self.nodes[1].getblockcount())
# Disconnect node1 and generate the new chain
- disconnect_nodes(self.nodes[0], 1)
- disconnect_nodes(self.nodes[1], 2)
+ self.disconnect_nodes(0, 1)
+ self.disconnect_nodes(1, 2)
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
self.log.info("Reconnect nodes")
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
self.sync_blocks(self.nodes[0:3], timeout=120)
self.log.info("Verify height on node 2: %d" % self.nodes[2].getblockcount())
@@ -250,7 +246,7 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
# Wait for Node 2 to reorg to proper height
- wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
+ self.wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
assert_equal(self.nodes[2].getbestblockhash(), goalbesthash)
# Verify we can now have the data for a block previously pruned
assert_equal(self.nodes[2].getblock(self.forkhash)["height"], self.forkheight)
@@ -338,7 +334,7 @@ class PruneTest(BitcoinTestFramework):
# check that wallet loads successfully when restarting a pruned node after IBD.
# this was reported to fail in #7494.
self.log.info("Syncing node 5 to test wallet")
- connect_nodes(self.nodes[0], 5)
+ self.connect_nodes(0, 5)
nds = [self.nodes[0], self.nodes[5]]
self.sync_blocks(nds, wait=5, timeout=300)
self.restart_node(5, extra_args=["-prune=550"]) # restart to trigger rescan
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 0842972779..7bd2fc7847 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -22,7 +22,6 @@ from test_framework.util import (
assert_equal,
assert_is_hex_string,
assert_raises_rpc_error,
- connect_nodes,
hex_str_to_bytes,
try_rpc,
)
@@ -61,14 +60,12 @@ class SegWitTest(BitcoinTestFramework):
],
[
"-acceptnonstdtxn=1",
- "-blockversion=4",
"-rpcserialversion=1",
"-segwitheight=432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
- "-blockversion=536870915",
"-segwitheight=432",
"-addresstype=legacy",
],
@@ -80,7 +77,7 @@ class SegWitTest(BitcoinTestFramework):
def setup_network(self):
super().setup_network()
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
self.sync_all()
def success_mine(self, node, txid, sign, redeem_script=""):
diff --git a/test/functional/feature_settings.py b/test/functional/feature_settings.py
index c565854bb0..5a0236401d 100755
--- a/test/functional/feature_settings.py
+++ b/test/functional/feature_settings.py
@@ -17,6 +17,7 @@ class SettingsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.wallet_names = []
def run_test(self):
node, = self.nodes
@@ -30,19 +31,25 @@ class SettingsTest(BitcoinTestFramework):
# Assert settings are parsed and logged
with settings.open("w") as fp:
- json.dump({"string": "string", "num": 5, "bool": True, "null": None, "list": [6,7]}, fp)
+ json.dump({"string": "string", "num": 5, "bool": True, "null": None, "list": [6, 7]}, fp)
with node.assert_debug_log(expected_msgs=[
+ 'Ignoring unknown rw_settings value bool',
+ 'Ignoring unknown rw_settings value list',
+ 'Ignoring unknown rw_settings value null',
+ 'Ignoring unknown rw_settings value num',
+ 'Ignoring unknown rw_settings value string',
'Setting file arg: string = "string"',
'Setting file arg: num = 5',
'Setting file arg: bool = true',
'Setting file arg: null = null',
- 'Setting file arg: list = [6,7]']):
+ 'Setting file arg: list = [6,7]',
+ ]):
self.start_node(0)
self.stop_node(0)
# Assert settings are unchanged after shutdown
with settings.open() as fp:
- assert_equal(json.load(fp), {"string": "string", "num": 5, "bool": True, "null": None, "list": [6,7]})
+ assert_equal(json.load(fp), {"string": "string", "num": 5, "bool": True, "null": None, "list": [6, 7]})
# Test invalid json
with settings.open("w") as fp:
diff --git a/test/functional/feature_shutdown.py b/test/functional/feature_shutdown.py
index d782d3b1d8..a76e0f1b50 100755
--- a/test/functional/feature_shutdown.py
+++ b/test/functional/feature_shutdown.py
@@ -5,7 +5,7 @@
"""Test bitcoind shutdown."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, get_rpc_proxy, wait_until
+from test_framework.util import assert_equal, get_rpc_proxy
from threading import Thread
def test_long_call(node):
@@ -25,7 +25,7 @@ class ShutdownTest(BitcoinTestFramework):
node.getblockcount()
Thread(target=test_long_call, args=(node,)).start()
# Wait until the server is executing the above `waitfornewblock`.
- wait_until(lambda: len(self.nodes[0].getrpcinfo()['active_commands']) == 2)
+ self.wait_until(lambda: len(self.nodes[0].getrpcinfo()['active_commands']) == 2)
# Wait 1 second after requesting shutdown but not before the `stop` call
# finishes. This is to ensure event loop waits for current connections
# to close.
diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py
new file mode 100755
index 0000000000..96c581dede
--- /dev/null
+++ b/test/functional/feature_signet.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# 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.
+"""Test basic signet functionality"""
+
+from decimal import Decimal
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+signet_blocks = [
+ '00000020f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000f575c83235984e7dc4afc1f30944c170462e84437ab6f2d52e16878a79e4678bd1914d5fae77031eccf4070001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025151feffffff0200f2052a010000001600149243f727dd5343293eb83174324019ec16c2630f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205e423a8754336ca99dbe16509b877ef1bf98d008836c725005b3c787c41ebe46022047246e4467ad7cc7f1ad98662afcaf14c115e0095a227c7b05c5182591c23e7e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020533b53ded9bff4adc94101d32400a144c54edc5ed492a3b26c63b2d686000000b38fef50592017cfafbcab88eb3d9cf50b2c801711cad8299495d26df5e54812e7914d5fae77031ecfdd0b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025251feffffff0200f2052a01000000160014fd09839740f0e0b4fc6d5e2527e4022aa9b89dfa0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022031d64a1692cdad1fc0ced69838169fe19ae01be524d831b95fcf5ea4e6541c3c02204f9dea0801df8b4d0cd0857c62ab35c6c25cc47c930630dc7fe723531daa3e9b01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000202960f3752f0bfa8858a3e333294aedc7808025e868c9dc03e71d88bb320000007765fcd3d5b4966beb338bba2675dc2cf2ad28d4ad1d83bdb6f286e7e27ac1f807924d5fae77031e81d60b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025351feffffff0200f2052a010000001600141e5fb426042692ae0e87c070e78c39307a5661c20000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205de93694763a42954865bcf1540cb82958bc62d0ec4eee02070fb7937cd037f4022067f333753bce47b10bc25eb6e1f311482e994c862a7e0b2d41ab1c8679fd1b1101000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020b06443a13ae1d3d50faef5ecad38c6818194dc46abca3e972e2aacdae800000069a5829097e80fee00ac49a56ea9f82d741a6af84d32b3bc455cf31871e2a8ac27924d5fae77031e9c91050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025451feffffff0200f2052a0100000016001430db2f8225dcf7751361ab38735de08190318cb70000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402200936f5f9872f6df5dd242026ad52241a68423f7f682e79169a8d85a374eab9b802202cd2979c48b321b3453e65e8f92460db3fca93cbea8539b450c959f4fbe630c601000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000207ed403758a4f228a1939418a155e2ebd4ae6b26e5ffd0ae433123f7694010000542e80b609c5bc58af5bdf492e26d4f60cd43a3966c2e063c50444c29b3757a636924d5fae77031ee8601d0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025551feffffff0200f2052a01000000160014edc207e014df34fa3885dff97d1129d356e1186a0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022021a3656609f85a66a2c5672ed9322c2158d57251040d2716ed202a1fe14f0c12022057d68bc6611f7a9424a7e00bbf3e27e6ae6b096f60bac624a094bc97a59aa1ff01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000205bea0a88d1422c3df08d766ad72df95084d0700e6f873b75dd4e986c7703000002b57516d33ed60c2bdd9f93d6d5614083324c837e68e5ba6e04287a7285633585924d5fae77031ed171960001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025651feffffff0200f2052a010000001600143ae612599cf96f2442ce572633e0251116eaa52f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022059a7c54de76bfdbb1dd44c78ea2dbd2bb4e97f4abad38965f41e76433e56423c022054bf17f04fe17415c0141f60eebd2b839200f574d8ad8d55a0917b92b0eb913401000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020daf3b60d374b19476461f97540498dcfa2eb7016238ec6b1d022f82fb60100007a7ae65b53cb988c2ec92d2384996713821d5645ffe61c9acea60da75cd5edfa1a944d5fae77031e9dbb050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025751feffffff0200f2052a01000000160014ef2dceae02e35f8137de76768ae3345d99ca68860000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202b3f946d6447f9bf17d00f3696cede7ee70b785495e5498274ee682a493befd5022045fc0bcf9332243168b5d35507175f9f374a8eba2336873885d12aada67ea5f601000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020457cc5f3c2e1a5655bc20e20e48d33e1b7ea68786c614032b5c518f0b6000000541f36942d82c6e7248275ff15c8933487fbe1819c67a9ecc0f4b70bb7e6cf672a944d5fae77031e8f39860001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025851feffffff0200f2052a0100000016001472a27906947c06d034b38ba2fa13c6391a4832790000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202d62805ce60cbd60591f97f949b5ea5bd7e2307bcde343e6ea8394da92758e72022053a25370b0aa20da100189b7899a8f8675a0fdc60e38ece6b8a4f98edd94569e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020a2eb61eb4f3831baa3a3363e1b42db4462663f756f07423e81ed30322102000077224de7dea0f8d0ec22b1d2e2e255f0a987b96fe7200e1a2e6373f48a2f5b7894954d5fae77031e36867e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025951feffffff0200f2052a01000000160014aa0ad9f26801258382e0734dceec03a4a75f60240000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402206fa0d59990eed369bd7375767c9a6c9369fae209152b8674e520da270605528c0220749eed3b12dbe3f583f505d21803e4aef59c8e24c5831951eafa4f15a8f92c4e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020a868e8514be5e46dabd6a122132f423f36a43b716a40c394e2a8d063e1010000f4c6c717e99d800c699c25a2006a75a0c5c09f432a936f385e6fce139cdbd1a5e9964d5fae77031e7d026e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025a51feffffff0200f2052a01000000160014aaa671c82b138e3b8f510cd801e5f2bd0aa305940000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022042309f4c3c7a1a2ac8c24f890f962df1c0086cec10be0868087cfc427520cb2702201dafee8911c269b7e786e242045bb57cef3f5b0f177010c6159abae42f646cc501000120000000000000000000000000000000000000000000000000000000000000000000000000',
+]
+
+
+class SignetBasicTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.chain = "signet"
+ self.num_nodes = 6
+ self.setup_clean_chain = True
+ shared_args1 = ["-signetchallenge=51"] # OP_TRUE
+ shared_args2 = [] # default challenge
+ # we use the exact same challenge except we do it as a 2-of-2, which means it should fail
+ shared_args3 = ["-signetchallenge=522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"]
+
+ self.extra_args = [
+ shared_args1, shared_args1,
+ shared_args2, shared_args2,
+ shared_args3, shared_args3,
+ ]
+
+ def run_test(self):
+ self.log.info("basic tests using OP_TRUE challenge")
+
+ self.log.info('getmininginfo')
+ mining_info = self.nodes[0].getmininginfo()
+ assert_equal(mining_info['blocks'], 0)
+ assert_equal(mining_info['chain'], 'signet')
+ assert 'currentblocktx' not in mining_info
+ assert 'currentblockweight' not in mining_info
+ assert_equal(mining_info['networkhashps'], Decimal('0'))
+ assert_equal(mining_info['pooledtx'], 0)
+
+ self.nodes[0].generate(1)
+
+ self.log.info("pregenerated signet blocks check")
+
+ height = 0
+ for block in signet_blocks:
+ assert_equal(self.nodes[2].submitblock(block), None)
+ height += 1
+ assert_equal(self.nodes[2].getblockcount(), height)
+
+ self.log.info("pregenerated signet blocks check (incompatible solution)")
+
+ assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
+
+ self.log.info("test that signet logs the network magic on node start")
+ with self.nodes[0].assert_debug_log(["Signet derived magic (message start)"]):
+ self.restart_node(0)
+
+
+if __name__ == '__main__':
+ SignetBasicTest().main()
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
new file mode 100755
index 0000000000..7b534c1c2f
--- /dev/null
+++ b/test/functional/feature_taproot.py
@@ -0,0 +1,1458 @@
+#!/usr/bin/env python3
+# 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.
+# Test Taproot softfork (BIPs 340-342)
+
+from test_framework.blocktools import (
+ create_coinbase,
+ create_block,
+ add_witness_commitment,
+ MAX_BLOCK_SIGOPS_WEIGHT,
+ WITNESS_SCALE_FACTOR,
+)
+from test_framework.messages import (
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+ ToHex,
+)
+from test_framework.script import (
+ ANNEX_TAG,
+ CScript,
+ CScriptNum,
+ CScriptOp,
+ LEAF_VERSION_TAPSCRIPT,
+ LegacySignatureHash,
+ LOCKTIME_THRESHOLD,
+ MAX_SCRIPT_ELEMENT_SIZE,
+ OP_0,
+ OP_1,
+ OP_2,
+ OP_3,
+ OP_4,
+ OP_5,
+ OP_6,
+ OP_7,
+ OP_8,
+ OP_9,
+ OP_10,
+ OP_11,
+ OP_12,
+ OP_16,
+ OP_2DROP,
+ OP_2DUP,
+ OP_CHECKMULTISIG,
+ OP_CHECKMULTISIGVERIFY,
+ OP_CHECKSIG,
+ OP_CHECKSIGADD,
+ OP_CHECKSIGVERIFY,
+ OP_CODESEPARATOR,
+ OP_DROP,
+ OP_DUP,
+ OP_ELSE,
+ OP_ENDIF,
+ OP_EQUAL,
+ OP_EQUALVERIFY,
+ OP_HASH160,
+ OP_IF,
+ OP_NOP,
+ OP_NOT,
+ OP_NOTIF,
+ OP_PUSHDATA1,
+ OP_RETURN,
+ OP_SWAP,
+ OP_VERIFY,
+ SIGHASH_DEFAULT,
+ SIGHASH_ALL,
+ SIGHASH_NONE,
+ SIGHASH_SINGLE,
+ SIGHASH_ANYONECANPAY,
+ SegwitV0SignatureHash,
+ TaprootSignatureHash,
+ is_op_success,
+ taproot_construct,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error, assert_equal
+from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey
+from test_framework.address import (
+ hash160,
+ sha256,
+)
+from collections import OrderedDict, namedtuple
+from io import BytesIO
+import json
+import hashlib
+import os
+import random
+
+# === Framework for building spending transactions. ===
+#
+# The computation is represented as a "context" dict, whose entries store potentially-unevaluated expressions that
+# refer to lower-level ones. By overwriting these expression, many aspects - both high and low level - of the signing
+# process can be overridden.
+#
+# Specifically, a context object is a dict that maps names to compositions of:
+# - values
+# - lists of values
+# - callables which, when fed the context object as argument, produce any of these
+#
+# The DEFAULT_CONTEXT object specifies a standard signing process, with many overridable knobs.
+#
+# The get(ctx, name) function can evaluate a name, and cache its result in the context.
+# getter(name) can be used to construct a callable that evaluates name. For example:
+#
+# ctx1 = {**DEFAULT_CONTEXT, inputs=[getter("sign"), b'\x01']}
+#
+# creates a context where the script inputs are a signature plus the bytes 0x01.
+#
+# override(expr, name1=expr1, name2=expr2, ...) can be used to cause an expression to be evaluated in a selectively
+# modified context. For example:
+#
+# ctx2 = {**DEFAULT_CONTEXT, sighash=override(default_sighash, hashtype=SIGHASH_DEFAULT)}
+#
+# creates a context ctx2 where the sighash is modified to use hashtype=SIGHASH_DEFAULT. This differs from
+#
+# ctx3 = {**DEFAULT_CONTEXT, hashtype=SIGHASH_DEFAULT}
+#
+# in that ctx3 will globally use hashtype=SIGHASH_DEFAULT (including in the hashtype byte appended to the signature)
+# while ctx2 only uses the modified hashtype inside the sighash calculation.
+
+def deep_eval(ctx, expr):
+ """Recursively replace any callables c in expr (including inside lists) with c(ctx)."""
+ while callable(expr):
+ expr = expr(ctx)
+ if isinstance(expr, list):
+ expr = [deep_eval(ctx, x) for x in expr]
+ return expr
+
+# Data type to represent fully-evaluated expressions in a context dict (so we can avoid reevaluating them).
+Final = namedtuple("Final", "value")
+
+def get(ctx, name):
+ """Evaluate name in context ctx."""
+ assert name in ctx, "Missing '%s' in context" % name
+ expr = ctx[name]
+ if not isinstance(expr, Final):
+ # Evaluate and cache the result.
+ expr = Final(deep_eval(ctx, expr))
+ ctx[name] = expr
+ return expr.value
+
+def getter(name):
+ """Return a callable that evaluates name in its passed context."""
+ return lambda ctx: get(ctx, name)
+
+def override(expr, **kwargs):
+ """Return a callable that evaluates expr in a modified context."""
+ return lambda ctx: deep_eval({**ctx, **kwargs}, expr)
+
+# === Implementations for the various default expressions in DEFAULT_CONTEXT ===
+
+def default_hashtype(ctx):
+ """Default expression for "hashtype": SIGHASH_DEFAULT for taproot, SIGHASH_ALL otherwise."""
+ mode = get(ctx, "mode")
+ if mode == "taproot":
+ return SIGHASH_DEFAULT
+ else:
+ return SIGHASH_ALL
+
+def default_tapleaf(ctx):
+ """Default expression for "tapleaf": looking up leaf in tap[2]."""
+ return get(ctx, "tap").leaves[get(ctx, "leaf")]
+
+def default_script_taproot(ctx):
+ """Default expression for "script_taproot": tapleaf.script."""
+ return get(ctx, "tapleaf").script
+
+def default_leafversion(ctx):
+ """Default expression for "leafversion": tapleaf.version"""
+ return get(ctx, "tapleaf").version
+
+def default_negflag(ctx):
+ """Default expression for "negflag": tap.negflag."""
+ return get(ctx, "tap").negflag
+
+def default_pubkey_inner(ctx):
+ """Default expression for "pubkey_inner": tap.inner_pubkey."""
+ return get(ctx, "tap").inner_pubkey
+
+def default_merklebranch(ctx):
+ """Default expression for "merklebranch": tapleaf.merklebranch."""
+ return get(ctx, "tapleaf").merklebranch
+
+def default_controlblock(ctx):
+ """Default expression for "controlblock": combine leafversion, negflag, pubkey_inner, merklebranch."""
+ return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_inner") + get(ctx, "merklebranch")
+
+def default_sighash(ctx):
+ """Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash."""
+ tx = get(ctx, "tx")
+ idx = get(ctx, "idx")
+ hashtype = get(ctx, "hashtype_actual")
+ mode = get(ctx, "mode")
+ if mode == "taproot":
+ # BIP341 signature hash
+ utxos = get(ctx, "utxos")
+ annex = get(ctx, "annex")
+ if get(ctx, "leaf") is not None:
+ codeseppos = get(ctx, "codeseppos")
+ leaf_ver = get(ctx, "leafversion")
+ script = get(ctx, "script_taproot")
+ return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex)
+ else:
+ return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
+ elif mode == "witv0":
+ # BIP143 signature hash
+ scriptcode = get(ctx, "scriptcode")
+ utxos = get(ctx, "utxos")
+ return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue)
+ else:
+ # Pre-segwit signature hash
+ scriptcode = get(ctx, "scriptcode")
+ return LegacySignatureHash(scriptcode, tx, idx, hashtype)[0]
+
+def default_tweak(ctx):
+ """Default expression for "tweak": None if a leaf is specified, tap[0] otherwise."""
+ if get(ctx, "leaf") is None:
+ return get(ctx, "tap").tweak
+ return None
+
+def default_key_tweaked(ctx):
+ """Default expression for "key_tweaked": key if tweak is None, tweaked with it otherwise."""
+ key = get(ctx, "key")
+ tweak = get(ctx, "tweak")
+ if tweak is None:
+ return key
+ else:
+ return tweak_add_privkey(key, tweak)
+
+def default_signature(ctx):
+ """Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
+ sighash = get(ctx, "sighash")
+ if get(ctx, "mode") == "taproot":
+ key = get(ctx, "key_tweaked")
+ flip_r = get(ctx, "flag_flip_r")
+ flip_p = get(ctx, "flag_flip_p")
+ return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p)
+ else:
+ key = get(ctx, "key")
+ return key.sign_ecdsa(sighash)
+
+def default_hashtype_actual(ctx):
+ """Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
+ hashtype = get(ctx, "hashtype")
+ mode = get(ctx, "mode")
+ if mode != "taproot":
+ return hashtype
+ idx = get(ctx, "idx")
+ tx = get(ctx, "tx")
+ if hashtype & 3 == SIGHASH_SINGLE and idx >= len(tx.vout):
+ return (hashtype & ~3) | SIGHASH_NONE
+ return hashtype
+
+def default_bytes_hashtype(ctx):
+ """Default expression for "bytes_hashtype": bytes([hashtype_actual]) if not 0, b"" otherwise."""
+ return bytes([x for x in [get(ctx, "hashtype_actual")] if x != 0])
+
+def default_sign(ctx):
+ """Default expression for "sign": concatenation of signature and bytes_hashtype."""
+ return get(ctx, "signature") + get(ctx, "bytes_hashtype")
+
+def default_inputs_keypath(ctx):
+ """Default expression for "inputs_keypath": a signature."""
+ return [get(ctx, "sign")]
+
+def default_witness_taproot(ctx):
+ """Default expression for "witness_taproot", consisting of inputs, script, control block, and annex as needed."""
+ annex = get(ctx, "annex")
+ suffix_annex = []
+ if annex is not None:
+ suffix_annex = [annex]
+ if get(ctx, "leaf") is None:
+ return get(ctx, "inputs_keypath") + suffix_annex
+ else:
+ return get(ctx, "inputs") + [bytes(get(ctx, "script_taproot")), get(ctx, "controlblock")] + suffix_annex
+
+def default_witness_witv0(ctx):
+ """Default expression for "witness_witv0", consisting of inputs and witness script, as needed."""
+ script = get(ctx, "script_witv0")
+ inputs = get(ctx, "inputs")
+ if script is None:
+ return inputs
+ else:
+ return inputs + [script]
+
+def default_witness(ctx):
+ """Default expression for "witness", delegating to "witness_taproot" or "witness_witv0" as needed."""
+ mode = get(ctx, "mode")
+ if mode == "taproot":
+ return get(ctx, "witness_taproot")
+ elif mode == "witv0":
+ return get(ctx, "witness_witv0")
+ else:
+ return []
+
+def default_scriptsig(ctx):
+ """Default expression for "scriptsig", consisting of inputs and redeemscript, as needed."""
+ scriptsig = []
+ mode = get(ctx, "mode")
+ if mode == "legacy":
+ scriptsig = get(ctx, "inputs")
+ redeemscript = get(ctx, "script_p2sh")
+ if redeemscript is not None:
+ scriptsig += [bytes(redeemscript)]
+ return scriptsig
+
+# The default context object.
+DEFAULT_CONTEXT = {
+ # == The main expressions to evaluate. Only override these for unusual or invalid spends. ==
+ # The overall witness stack, as a list of bytes objects.
+ "witness": default_witness,
+ # The overall scriptsig, as a list of CScript objects (to be concatenated) and bytes objects (to be pushed)
+ "scriptsig": default_scriptsig,
+
+ # == Expressions you'll generally only override for intentionally invalid spends. ==
+ # The witness stack for spending a taproot output.
+ "witness_taproot": default_witness_taproot,
+ # The witness stack for spending a P2WPKH/P2WSH output.
+ "witness_witv0": default_witness_witv0,
+ # The script inputs for a taproot key path spend.
+ "inputs_keypath": default_inputs_keypath,
+ # The actual hashtype to use (usually equal to hashtype, but in taproot SIGHASH_SINGLE is not always allowed).
+ "hashtype_actual": default_hashtype_actual,
+ # The bytes object for a full signature (including hashtype byte, if needed).
+ "bytes_hashtype": default_bytes_hashtype,
+ # A full script signature (bytes including hashtype, if needed)
+ "sign": default_sign,
+ # An ECDSA or Schnorr signature (excluding hashtype byte).
+ "signature": default_signature,
+ # The 32-byte tweaked key (equal to key for script path spends, or key+tweak for key path spends).
+ "key_tweaked": default_key_tweaked,
+ # The tweak to use (None for script path spends, the actual tweak for key path spends).
+ "tweak": default_tweak,
+ # The sighash value (32 bytes)
+ "sighash": default_sighash,
+ # The information about the chosen script path spend (TaprootLeafInfo object).
+ "tapleaf": default_tapleaf,
+ # The script to push, and include in the sighash, for a taproot script path spend.
+ "script_taproot": default_script_taproot,
+ # The inner pubkey for a taproot script path spend (32 bytes).
+ "pubkey_inner": default_pubkey_inner,
+ # The negation flag of the inner pubkey for a taproot script path spend.
+ "negflag": default_negflag,
+ # The leaf version to include in the sighash (this does not affect the one in the control block).
+ "leafversion": default_leafversion,
+ # The Merkle path to include in the control block for a script path spend.
+ "merklebranch": default_merklebranch,
+ # The control block to push for a taproot script path spend.
+ "controlblock": default_controlblock,
+ # Whether to produce signatures with invalid P sign (Schnorr signatures only).
+ "flag_flip_p": False,
+ # Whether to produce signatures with invalid R sign (Schnorr signatures only).
+ "flag_flip_r": False,
+
+ # == Parameters that can be changed without invalidating, but do have a default: ==
+ # The hashtype (as an integer).
+ "hashtype": default_hashtype,
+ # The annex (only when mode=="taproot").
+ "annex": None,
+ # The codeseparator position (only when mode=="taproot").
+ "codeseppos": -1,
+ # The redeemscript to add to the scriptSig (if P2SH; None implies not P2SH).
+ "script_p2sh": None,
+ # The script to add to the witness in (if P2WSH; None implies P2WPKH)
+ "script_witv0": None,
+ # The leaf to use in taproot spends (if script path spend; None implies key path spend).
+ "leaf": None,
+ # The input arguments to provide to the executed script
+ "inputs": [],
+
+ # == Parameters to be set before evaluation: ==
+ # - mode: what spending style to use ("taproot", "witv0", or "legacy").
+ # - key: the (untweaked) private key to sign with (ECKey object for ECDSA, 32 bytes for Schnorr).
+ # - tap: the TaprootInfo object (see taproot_construct; needed in mode=="taproot").
+ # - tx: the transaction to sign.
+ # - utxos: the UTXOs being spent (needed in mode=="witv0" and mode=="taproot").
+ # - idx: the input position being signed.
+ # - scriptcode: the scriptcode to include in legacy and witv0 sighashes.
+}
+
+def flatten(lst):
+ ret = []
+ for elem in lst:
+ if isinstance(elem, list):
+ ret += flatten(elem)
+ else:
+ ret.append(elem)
+ return ret
+
+def spend(tx, idx, utxos, **kwargs):
+ """Sign transaction input idx of tx, provided utxos is the list of outputs being spent.
+
+ Additional arguments may be provided that override any aspect of the signing process.
+ See DEFAULT_CONTEXT above for what can be overridden, and what must be provided.
+ """
+
+ ctx = {**DEFAULT_CONTEXT, "tx":tx, "idx":idx, "utxos":utxos, **kwargs}
+
+ def to_script(elem):
+ """If fed a CScript, return it; if fed bytes, return a CScript that pushes it."""
+ if isinstance(elem, CScript):
+ return elem
+ else:
+ return CScript([elem])
+
+ scriptsig_list = flatten(get(ctx, "scriptsig"))
+ scriptsig = CScript(b"".join(bytes(to_script(elem)) for elem in scriptsig_list))
+ witness_stack = flatten(get(ctx, "witness"))
+ return (scriptsig, witness_stack)
+
+
+# === Spender objects ===
+#
+# Each spender is a tuple of:
+# - A scriptPubKey which is to be spent from (CScript)
+# - A comment describing the test (string)
+# - Whether the spending (on itself) is expected to be standard (bool)
+# - A tx-signing lambda returning (scriptsig, witness_stack), taking as inputs:
+# - A transaction to sign (CTransaction)
+# - An input position (int)
+# - The spent UTXOs by this transaction (list of CTxOut)
+# - Whether to produce a valid spend (bool)
+# - A string with an expected error message for failure case if known
+# - The (pre-taproot) sigops weight consumed by a successful spend
+# - Whether this spend cannot fail
+# - Whether this test demands being placed in a txin with no corresponding txout (for testing SIGHASH_SINGLE behavior)
+
+Spender = namedtuple("Spender", "script,comment,is_standard,sat_function,err_msg,sigops_weight,no_fail,need_vin_vout_mismatch")
+
+def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=False, spk_mutate_pre_p2sh=None, failure=None, standard=True, err_msg=None, sigops_weight=0, need_vin_vout_mismatch=False, **kwargs):
+ """Helper for constructing Spender objects using the context signing framework.
+
+ * tap: a TaprootInfo object (see taproot_construct), for Taproot spends (cannot be combined with pkh, witv0, or script)
+ * witv0: boolean indicating the use of witness v0 spending (needs one of script or pkh)
+ * script: the actual script executed (for bare/P2WSH/P2SH spending)
+ * pkh: the public key for P2PKH or P2WPKH spending
+ * p2sh: whether the output is P2SH wrapper (this is supported even for Taproot, where it makes the output unencumbered)
+ * spk_mutate_pre_psh: a callable to be applied to the script (before potentially P2SH-wrapping it)
+ * failure: a dict of entries to override in the context when intentionally failing to spend (if None, no_fail will be set)
+ * standard: whether the (valid version of) spending is expected to be standard
+ * err_msg: a string with an expected error message for failure (or None, if not cared about)
+ * sigops_weight: the pre-taproot sigops weight consumed by a successful spend
+ """
+
+ conf = dict()
+
+ # Compute scriptPubKey and set useful defaults based on the inputs.
+ if witv0:
+ assert tap is None
+ conf["mode"] = "witv0"
+ if pkh is not None:
+ # P2WPKH
+ assert script is None
+ pubkeyhash = hash160(pkh)
+ spk = CScript([OP_0, pubkeyhash])
+ conf["scriptcode"] = CScript([OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG])
+ conf["script_witv0"] = None
+ conf["inputs"] = [getter("sign"), pkh]
+ elif script is not None:
+ # P2WSH
+ spk = CScript([OP_0, sha256(script)])
+ conf["scriptcode"] = script
+ conf["script_witv0"] = script
+ else:
+ assert False
+ elif tap is None:
+ conf["mode"] = "legacy"
+ if pkh is not None:
+ # P2PKH
+ assert script is None
+ pubkeyhash = hash160(pkh)
+ spk = CScript([OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG])
+ conf["scriptcode"] = spk
+ conf["inputs"] = [getter("sign"), pkh]
+ elif script is not None:
+ # bare
+ spk = script
+ conf["scriptcode"] = script
+ else:
+ assert False
+ else:
+ assert script is None
+ conf["mode"] = "taproot"
+ conf["tap"] = tap
+ spk = tap.scriptPubKey
+
+ if spk_mutate_pre_p2sh is not None:
+ spk = spk_mutate_pre_p2sh(spk)
+
+ if p2sh:
+ # P2SH wrapper can be combined with anything else
+ conf["script_p2sh"] = spk
+ spk = CScript([OP_HASH160, hash160(spk), OP_EQUAL])
+
+ conf = {**conf, **kwargs}
+
+ def sat_fn(tx, idx, utxos, valid):
+ if valid:
+ return spend(tx, idx, utxos, **conf)
+ else:
+ assert failure is not None
+ return spend(tx, idx, utxos, **{**conf, **failure})
+
+ return Spender(script=spk, comment=comment, is_standard=standard, sat_function=sat_fn, err_msg=err_msg, sigops_weight=sigops_weight, no_fail=failure is None, need_vin_vout_mismatch=need_vin_vout_mismatch)
+
+def add_spender(spenders, *args, **kwargs):
+ """Make a spender using make_spender, and add it to spenders."""
+ spenders.append(make_spender(*args, **kwargs))
+
+# === Helpers for the test ===
+
+def random_checksig_style(pubkey):
+ """Creates a random CHECKSIG* tapscript that would succeed with only the valid signature on witness stack."""
+ return bytes(CScript([pubkey, OP_CHECKSIG]))
+ opcode = random.choice([OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKSIGADD])
+ if (opcode == OP_CHECKSIGVERIFY):
+ ret = CScript([pubkey, opcode, OP_1])
+ elif (opcode == OP_CHECKSIGADD):
+ num = random.choice([0, 0x7fffffff, -0x7fffffff])
+ ret = CScript([num, pubkey, opcode, num + 1, OP_EQUAL])
+ else:
+ ret = CScript([pubkey, opcode])
+ return bytes(ret)
+
+def random_bytes(n):
+ """Return a random bytes object of length n."""
+ return bytes(random.getrandbits(8) for i in range(n))
+
+def bitflipper(expr):
+ """Return a callable that evaluates expr and returns it with a random bitflip."""
+ def fn(ctx):
+ sub = deep_eval(ctx, expr)
+ assert isinstance(sub, bytes)
+ return (int.from_bytes(sub, 'little') ^ (1 << random.randrange(len(sub) * 8))).to_bytes(len(sub), 'little')
+ return fn
+
+def zero_appender(expr):
+ """Return a callable that evaluates expr and returns it with a zero added."""
+ return lambda ctx: deep_eval(ctx, expr) + b"\x00"
+
+def byte_popper(expr):
+ """Return a callable that evaluates expr and returns it with its last byte removed."""
+ return lambda ctx: deep_eval(ctx, expr)[:-1]
+
+# Expected error strings
+
+ERR_SIG_SIZE = {"err_msg": "Invalid Schnorr signature size"}
+ERR_SIG_HASHTYPE = {"err_msg": "Invalid Schnorr signature hash type"}
+ERR_SIG_SCHNORR = {"err_msg": "Invalid Schnorr signature"}
+ERR_OP_RETURN = {"err_msg": "OP_RETURN was encountered"}
+ERR_CONTROLBLOCK_SIZE = {"err_msg": "Invalid Taproot control block size"}
+ERR_WITNESS_PROGRAM_MISMATCH = {"err_msg": "Witness program hash mismatch"}
+ERR_PUSH_LIMIT = {"err_msg": "Push value size limit exceeded"}
+ERR_DISABLED_OPCODE = {"err_msg": "Attempted to use a disabled opcode"}
+ERR_TAPSCRIPT_CHECKMULTISIG = {"err_msg": "OP_CHECKMULTISIG(VERIFY) is not available in tapscript"}
+ERR_MINIMALIF = {"err_msg": "OP_IF/NOTIF argument must be minimal in tapscript"}
+ERR_UNKNOWN_PUBKEY = {"err_msg": "Public key is neither compressed or uncompressed"}
+ERR_STACK_SIZE = {"err_msg": "Stack size limit exceeded"}
+ERR_CLEANSTACK = {"err_msg": "Stack size must be exactly one after execution"}
+ERR_STACK_EMPTY = {"err_msg": "Operation not valid with the current stack size"}
+ERR_SIGOPS_RATIO = {"err_msg": "Too much signature validation relative to witness weight"}
+ERR_UNDECODABLE = {"err_msg": "Opcode missing or not understood"}
+ERR_NO_SUCCESS = {"err_msg": "Script evaluated without error but finished with a false/empty top stack element"}
+ERR_EMPTY_WITNESS = {"err_msg": "Witness program was passed an empty witness"}
+ERR_CHECKSIGVERIFY = {"err_msg": "Script failed an OP_CHECKSIGVERIFY operation"}
+
+VALID_SIGHASHES_ECDSA = [
+ SIGHASH_ALL,
+ SIGHASH_NONE,
+ SIGHASH_SINGLE,
+ SIGHASH_ANYONECANPAY + SIGHASH_ALL,
+ SIGHASH_ANYONECANPAY + SIGHASH_NONE,
+ SIGHASH_ANYONECANPAY + SIGHASH_SINGLE
+]
+
+VALID_SIGHASHES_TAPROOT = [SIGHASH_DEFAULT] + VALID_SIGHASHES_ECDSA
+
+VALID_SIGHASHES_TAPROOT_SINGLE = [
+ SIGHASH_SINGLE,
+ SIGHASH_ANYONECANPAY + SIGHASH_SINGLE
+]
+
+VALID_SIGHASHES_TAPROOT_NO_SINGLE = [h for h in VALID_SIGHASHES_TAPROOT if h not in VALID_SIGHASHES_TAPROOT_SINGLE]
+
+SIGHASH_BITFLIP = {"failure": {"sighash": bitflipper(default_sighash)}}
+SIG_POP_BYTE = {"failure": {"sign": byte_popper(default_sign)}}
+SINGLE_SIG = {"inputs": [getter("sign")]}
+SIG_ADD_ZERO = {"failure": {"sign": zero_appender(default_sign)}}
+
+DUST_LIMIT = 600
+MIN_FEE = 50000
+
+# === Actual test cases ===
+
+
+def spenders_taproot_active():
+ """Return a list of Spenders for testing post-Taproot activation behavior."""
+
+ secs = [generate_privkey() for _ in range(8)]
+ pubs = [compute_xonly_pubkey(sec)[0] for sec in secs]
+
+ spenders = []
+
+ # == Tests for BIP340 signature validation. ==
+ # These are primarily tested through the test vectors implemented in libsecp256k1, and in src/tests/key_tests.cpp.
+ # Some things are tested programmatically as well here.
+
+ tap = taproot_construct(pubs[0])
+ # Test with key with bit flipped.
+ add_spender(spenders, "sig/key", tap=tap, key=secs[0], failure={"key_tweaked": bitflipper(default_key_tweaked)}, **ERR_SIG_SCHNORR)
+ # Test with sighash with bit flipped.
+ add_spender(spenders, "sig/sighash", tap=tap, key=secs[0], failure={"sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR)
+ # Test with invalid R sign.
+ add_spender(spenders, "sig/flip_r", tap=tap, key=secs[0], failure={"flag_flip_r": True}, **ERR_SIG_SCHNORR)
+ # Test with invalid P sign.
+ add_spender(spenders, "sig/flip_p", tap=tap, key=secs[0], failure={"flag_flip_p": True}, **ERR_SIG_SCHNORR)
+ # Test with signature with bit flipped.
+ add_spender(spenders, "sig/bitflip", tap=tap, key=secs[0], failure={"signature": bitflipper(default_signature)}, **ERR_SIG_SCHNORR)
+
+ # == Tests for signature hashing ==
+
+ # Run all tests once with no annex, and once with a valid random annex.
+ for annex in [None, lambda _: bytes([ANNEX_TAG]) + random_bytes(random.randrange(0, 250))]:
+ # Non-empty annex is non-standard
+ no_annex = annex is None
+
+ # Sighash mutation tests (test all sighash combinations)
+ for hashtype in VALID_SIGHASHES_TAPROOT:
+ common = {"annex": annex, "hashtype": hashtype, "standard": no_annex}
+
+ # Pure pubkey
+ tap = taproot_construct(pubs[0])
+ add_spender(spenders, "sighash/purepk", tap=tap, key=secs[0], **common, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+
+ # Pubkey/P2PK script combination
+ scripts = [("s0", CScript(random_checksig_style(pubs[1])))]
+ tap = taproot_construct(pubs[0], scripts)
+ add_spender(spenders, "sighash/keypath_hashtype_%x" % hashtype, tap=tap, key=secs[0], **common, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/scriptpath_hashtype_%x" % hashtype, tap=tap, leaf="s0", key=secs[1], **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+
+ # Test SIGHASH_SINGLE behavior in combination with mismatching outputs
+ if hashtype in VALID_SIGHASHES_TAPROOT_SINGLE:
+ add_spender(spenders, "sighash/keypath_hashtype_mis_%x" % hashtype, tap=tap, key=secs[0], annex=annex, standard=no_annex, hashtype_actual=random.choice(VALID_SIGHASHES_TAPROOT_NO_SINGLE), failure={"hashtype_actual": hashtype}, **ERR_SIG_HASHTYPE, need_vin_vout_mismatch=True)
+ add_spender(spenders, "sighash/scriptpath_hashtype_mis_%x" % hashtype, tap=tap, leaf="s0", key=secs[1], annex=annex, standard=no_annex, hashtype_actual=random.choice(VALID_SIGHASHES_TAPROOT_NO_SINGLE), **SINGLE_SIG, failure={"hashtype_actual": hashtype}, **ERR_SIG_HASHTYPE, need_vin_vout_mismatch=True)
+
+ # Test OP_CODESEPARATOR impact on sighashing.
+ hashtype = lambda _: random.choice(VALID_SIGHASHES_TAPROOT)
+ common = {"annex": annex, "hashtype": hashtype, "standard": no_annex}
+ scripts = [
+ ("pk_codesep", CScript(random_checksig_style(pubs[1]) + bytes([OP_CODESEPARATOR]))), # codesep after checksig
+ ("codesep_pk", CScript(bytes([OP_CODESEPARATOR]) + random_checksig_style(pubs[1]))), # codesep before checksig
+ ("branched_codesep", CScript([random_bytes(random.randrange(511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
+ ]
+ random.shuffle(scripts)
+ tap = taproot_construct(pubs[0], scripts)
+ add_spender(spenders, "sighash/pk_codesep", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/codesep_pk", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/branched_codesep/left", tap=tap, leaf="branched_codesep", key=secs[0], codeseppos=3, **common, inputs=[getter("sign"), b'\x01'], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/branched_codesep/right", tap=tap, leaf="branched_codesep", key=secs[1], codeseppos=6, **common, inputs=[getter("sign"), b''], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+
+ # Reusing the scripts above, test that various features affect the sighash.
+ add_spender(spenders, "sighash/annex", tap=tap, leaf="pk_codesep", key=secs[1], hashtype=hashtype, standard=False, **SINGLE_SIG, annex=bytes([ANNEX_TAG]), failure={"sighash": override(default_sighash, annex=None)}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/script", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, script_taproot=tap.leaves["codesep_pk"].script)}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/leafver", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, leafversion=random.choice([x & 0xFE for x in range(0x100) if x & 0xFE != 0xC0]))}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, failure={"sighash": override(default_sighash, leaf=None)}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/keypath", tap=tap, key=secs[0], **common, failure={"sighash": override(default_sighash, leaf="pk_codesep")}, **ERR_SIG_SCHNORR)
+
+ # Test that invalid hashtypes don't work, both in key path and script path spends
+ hashtype = lambda _: random.choice(VALID_SIGHASHES_TAPROOT)
+ for invalid_hashtype in [x for x in range(0x100) if x not in VALID_SIGHASHES_TAPROOT]:
+ add_spender(spenders, "sighash/keypath_unk_hashtype_%x" % invalid_hashtype, tap=tap, key=secs[0], hashtype=hashtype, failure={"hashtype": invalid_hashtype}, **ERR_SIG_HASHTYPE)
+ add_spender(spenders, "sighash/scriptpath_unk_hashtype_%x" % invalid_hashtype, tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=hashtype, failure={"hashtype": invalid_hashtype}, **ERR_SIG_HASHTYPE)
+
+ # Test that hashtype 0 cannot have a hashtype byte, and 1 must have one.
+ add_spender(spenders, "sighash/hashtype0_byte_keypath", tap=tap, key=secs[0], hashtype=SIGHASH_DEFAULT, failure={"bytes_hashtype": bytes([SIGHASH_DEFAULT])}, **ERR_SIG_HASHTYPE)
+ add_spender(spenders, "sighash/hashtype0_byte_scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=SIGHASH_DEFAULT, failure={"bytes_hashtype": bytes([SIGHASH_DEFAULT])}, **ERR_SIG_HASHTYPE)
+ add_spender(spenders, "sighash/hashtype1_byte_keypath", tap=tap, key=secs[0], hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/hashtype1_byte_scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR)
+ # Test that hashtype 0 and hashtype 1 cannot be transmuted into each other.
+ add_spender(spenders, "sighash/hashtype0to1_keypath", tap=tap, key=secs[0], hashtype=SIGHASH_DEFAULT, failure={"bytes_hashtype": bytes([SIGHASH_ALL])}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/hashtype0to1_scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=SIGHASH_DEFAULT, failure={"bytes_hashtype": bytes([SIGHASH_ALL])}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/hashtype1to0_keypath", tap=tap, key=secs[0], hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "sighash/hashtype1to0_scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR)
+
+ # Test aspects of signatures with unusual lengths
+ for hashtype in [SIGHASH_DEFAULT, random.choice(VALID_SIGHASHES_TAPROOT)]:
+ scripts = [
+ ("csv", CScript([pubs[2], OP_CHECKSIGVERIFY, OP_1])),
+ ("cs_pos", CScript([pubs[2], OP_CHECKSIG])),
+ ("csa_pos", CScript([OP_0, pubs[2], OP_CHECKSIGADD, OP_1, OP_EQUAL])),
+ ("cs_neg", CScript([pubs[2], OP_CHECKSIG, OP_NOT])),
+ ("csa_neg", CScript([OP_2, pubs[2], OP_CHECKSIGADD, OP_2, OP_EQUAL]))
+ ]
+ random.shuffle(scripts)
+ tap = taproot_construct(pubs[3], scripts)
+ # Empty signatures
+ add_spender(spenders, "siglen/empty_keypath", tap=tap, key=secs[3], hashtype=hashtype, failure={"sign": b""}, **ERR_SIG_SIZE)
+ add_spender(spenders, "siglen/empty_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_CHECKSIGVERIFY)
+ add_spender(spenders, "siglen/empty_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS)
+ add_spender(spenders, "siglen/empty_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS)
+ add_spender(spenders, "siglen/empty_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random_bytes(random.randrange(1, 63))}, **ERR_SIG_SIZE)
+ add_spender(spenders, "siglen/empty_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random_bytes(random.randrange(66, 100))}, **ERR_SIG_SIZE)
+ # Appending a zero byte to signatures invalidates them
+ add_spender(spenders, "siglen/padzero_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ add_spender(spenders, "siglen/padzero_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ add_spender(spenders, "siglen/padzero_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ add_spender(spenders, "siglen/padzero_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ add_spender(spenders, "siglen/padzero_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ add_spender(spenders, "siglen/padzero_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
+ # Removing the last byte from signatures invalidates them
+ add_spender(spenders, "siglen/popbyte_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ add_spender(spenders, "siglen/popbyte_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ add_spender(spenders, "siglen/popbyte_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ add_spender(spenders, "siglen/popbyte_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ add_spender(spenders, "siglen/popbyte_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ add_spender(spenders, "siglen/popbyte_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR))
+ # Verify that an invalid signature is not allowed, not even when the CHECKSIG* is expected to fail.
+ add_spender(spenders, "siglen/invalid_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "siglen/invalid_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR)
+
+ # == Test that BIP341 spending only applies to witness version 1, program length 32, no P2SH ==
+
+ for p2sh in [False, True]:
+ for witver in range(1, 17):
+ for witlen in [20, 31, 32, 33]:
+ def mutate(spk):
+ prog = spk[2:]
+ assert len(prog) == 32
+ if witlen < 32:
+ prog = prog[0:witlen]
+ elif witlen > 32:
+ prog += bytes([0 for _ in range(witlen - 32)])
+ return CScript([CScriptOp.encode_op_n(witver), prog])
+ scripts = [("s0", CScript([pubs[0], OP_CHECKSIG])), ("dummy", CScript([OP_RETURN]))]
+ tap = taproot_construct(pubs[1], scripts)
+ if not p2sh and witver == 1 and witlen == 32:
+ add_spender(spenders, "applic/keypath", p2sh=p2sh, spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[1], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "applic/scriptpath", p2sh=p2sh, leaf="s0", spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[0], **SINGLE_SIG, failure={"leaf": "dummy"}, **ERR_OP_RETURN)
+ else:
+ add_spender(spenders, "applic/keypath", p2sh=p2sh, spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[1], standard=False)
+ add_spender(spenders, "applic/scriptpath", p2sh=p2sh, leaf="s0", spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[0], **SINGLE_SIG, standard=False)
+
+ # == Test various aspects of BIP341 spending paths ==
+
+ # A set of functions that compute the hashing partner in a Merkle tree, designed to exercise
+ # edge cases. This relies on the taproot_construct feature that a lambda can be passed in
+ # instead of a subtree, to compute the partner to be hashed with.
+ PARTNER_MERKLE_FN = [
+ # Combine with itself
+ lambda h: h,
+ # Combine with hash 0
+ lambda h: bytes([0 for _ in range(32)]),
+ # Combine with hash 2^256-1
+ lambda h: bytes([0xff for _ in range(32)]),
+ # Combine with itself-1 (BE)
+ lambda h: (int.from_bytes(h, 'big') - 1).to_bytes(32, 'big'),
+ # Combine with itself+1 (BE)
+ lambda h: (int.from_bytes(h, 'big') + 1).to_bytes(32, 'big'),
+ # Combine with itself-1 (LE)
+ lambda h: (int.from_bytes(h, 'little') - 1).to_bytes(32, 'big'),
+ # Combine with itself+1 (LE)
+ lambda h: (int.from_bytes(h, 'little') + 1).to_bytes(32, 'little'),
+ # Combine with random bitflipped version of self.
+ lambda h: (int.from_bytes(h, 'little') ^ (1 << random.randrange(256))).to_bytes(32, 'little')
+ ]
+ # Start with a tree of that has depth 1 for "128deep" and depth 2 for "129deep".
+ scripts = [("128deep", CScript([pubs[0], OP_CHECKSIG])), [("129deep", CScript([pubs[0], OP_CHECKSIG])), random.choice(PARTNER_MERKLE_FN)]]
+ # Add 127 nodes on top of that tree, so that "128deep" and "129deep" end up at their designated depths.
+ for _ in range(127):
+ scripts = [scripts, random.choice(PARTNER_MERKLE_FN)]
+ tap = taproot_construct(pubs[0], scripts)
+ # Test that spends with a depth of 128 work, but 129 doesn't (even with a tree with weird Merkle branches in it).
+ add_spender(spenders, "spendpath/merklelimit", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"leaf": "129deep"}, **ERR_CONTROLBLOCK_SIZE)
+ # Test that flipping the negation bit invalidates spends.
+ add_spender(spenders, "spendpath/negflag", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"negflag": lambda ctx: 1 - default_negflag(ctx)}, **ERR_WITNESS_PROGRAM_MISMATCH)
+ # Test that bitflips in the Merkle branch invalidate it.
+ add_spender(spenders, "spendpath/bitflipmerkle", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"merklebranch": bitflipper(default_merklebranch)}, **ERR_WITNESS_PROGRAM_MISMATCH)
+ # Test that bitflips in the inner pubkey invalidate it.
+ add_spender(spenders, "spendpath/bitflippubkey", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"pubkey_inner": bitflipper(default_pubkey_inner)}, **ERR_WITNESS_PROGRAM_MISMATCH)
+ # Test that empty witnesses are invalid.
+ add_spender(spenders, "spendpath/emptywit", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"witness": []}, **ERR_EMPTY_WITNESS)
+ # Test that adding garbage to the control block invalidates it.
+ add_spender(spenders, "spendpath/padlongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random_bytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
+ # Test that truncating the control block invalidates it.
+ add_spender(spenders, "spendpath/trunclongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:random.randrange(1, 32)]}, **ERR_CONTROLBLOCK_SIZE)
+
+ scripts = [("s", CScript([pubs[0], OP_CHECKSIG]))]
+ tap = taproot_construct(pubs[1], scripts)
+ # Test that adding garbage to the control block invalidates it.
+ add_spender(spenders, "spendpath/padshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random_bytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
+ # Test that truncating the control block invalidates it.
+ add_spender(spenders, "spendpath/truncshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:random.randrange(1, 32)]}, **ERR_CONTROLBLOCK_SIZE)
+ # Test that truncating the control block to 1 byte ("-1 Merkle length") invalidates it
+ add_spender(spenders, "spendpath/trunc1shortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:1]}, **ERR_CONTROLBLOCK_SIZE)
+
+ # == Test BIP342 edge cases ==
+
+ csa_low_val = random.randrange(0, 17) # Within range for OP_n
+ csa_low_result = csa_low_val + 1
+
+ csa_high_val = random.randrange(17, 100) if random.getrandbits(1) else random.randrange(-100, -1) # Outside OP_n range
+ csa_high_result = csa_high_val + 1
+
+ OVERSIZE_NUMBER = 2**31
+ assert_equal(len(CScriptNum.encode(CScriptNum(OVERSIZE_NUMBER))), 6)
+ assert_equal(len(CScriptNum.encode(CScriptNum(OVERSIZE_NUMBER-1))), 5)
+
+ big_choices = []
+ big_scriptops = []
+ for i in range(1000):
+ r = random.randrange(len(pubs))
+ big_choices.append(r)
+ big_scriptops += [pubs[r], OP_CHECKSIGVERIFY]
+
+
+ def big_spend_inputs(ctx):
+ """Helper function to construct the script input for t33/t34 below."""
+ # Instead of signing 999 times, precompute signatures for every (key, hashtype) combination
+ sigs = {}
+ for ht in VALID_SIGHASHES_TAPROOT:
+ for k in range(len(pubs)):
+ sigs[(k, ht)] = override(default_sign, hashtype=ht, key=secs[k])(ctx)
+ num = get(ctx, "num")
+ return [sigs[(big_choices[i], random.choice(VALID_SIGHASHES_TAPROOT))] for i in range(num - 1, -1, -1)]
+
+ # Various BIP342 features
+ scripts = [
+ # 0) drop stack element and OP_CHECKSIG
+ ("t0", CScript([OP_DROP, pubs[1], OP_CHECKSIG])),
+ # 1) normal OP_CHECKSIG
+ ("t1", CScript([pubs[1], OP_CHECKSIG])),
+ # 2) normal OP_CHECKSIGVERIFY
+ ("t2", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_1])),
+ # 3) Hypothetical OP_CHECKMULTISIG script that takes a single sig as input
+ ("t3", CScript([OP_0, OP_SWAP, OP_1, pubs[1], OP_1, OP_CHECKMULTISIG])),
+ # 4) Hypothetical OP_CHECKMULTISIGVERIFY script that takes a single sig as input
+ ("t4", CScript([OP_0, OP_SWAP, OP_1, pubs[1], OP_1, OP_CHECKMULTISIGVERIFY, OP_1])),
+ # 5) OP_IF script that needs a true input
+ ("t5", CScript([OP_IF, pubs[1], OP_CHECKSIG, OP_ELSE, OP_RETURN, OP_ENDIF])),
+ # 6) OP_NOTIF script that needs a true input
+ ("t6", CScript([OP_NOTIF, OP_RETURN, OP_ELSE, pubs[1], OP_CHECKSIG, OP_ENDIF])),
+ # 7) OP_CHECKSIG with an empty key
+ ("t7", CScript([OP_0, OP_CHECKSIG])),
+ # 8) OP_CHECKSIGVERIFY with an empty key
+ ("t8", CScript([OP_0, OP_CHECKSIGVERIFY, OP_1])),
+ # 9) normal OP_CHECKSIGADD that also ensures return value is correct
+ ("t9", CScript([csa_low_val, pubs[1], OP_CHECKSIGADD, csa_low_result, OP_EQUAL])),
+ # 10) OP_CHECKSIGADD with empty key
+ ("t10", CScript([csa_low_val, OP_0, OP_CHECKSIGADD, csa_low_result, OP_EQUAL])),
+ # 11) OP_CHECKSIGADD with missing counter stack element
+ ("t11", CScript([pubs[1], OP_CHECKSIGADD, OP_1, OP_EQUAL])),
+ # 12) OP_CHECKSIG that needs invalid signature
+ ("t12", CScript([pubs[1], OP_CHECKSIGVERIFY, pubs[0], OP_CHECKSIG, OP_NOT])),
+ # 13) OP_CHECKSIG with empty key that needs invalid signature
+ ("t13", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_0, OP_CHECKSIG, OP_NOT])),
+ # 14) OP_CHECKSIGADD that needs invalid signature
+ ("t14", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_0, pubs[0], OP_CHECKSIGADD, OP_NOT])),
+ # 15) OP_CHECKSIGADD with empty key that needs invalid signature
+ ("t15", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_0, OP_0, OP_CHECKSIGADD, OP_NOT])),
+ # 16) OP_CHECKSIG with unknown pubkey type
+ ("t16", CScript([OP_1, OP_CHECKSIG])),
+ # 17) OP_CHECKSIGADD with unknown pubkey type
+ ("t17", CScript([OP_0, OP_1, OP_CHECKSIGADD])),
+ # 18) OP_CHECKSIGVERIFY with unknown pubkey type
+ ("t18", CScript([OP_1, OP_CHECKSIGVERIFY, OP_1])),
+ # 19) script longer than 10000 bytes and over 201 non-push opcodes
+ ("t19", CScript([OP_0, OP_0, OP_2DROP] * 10001 + [pubs[1], OP_CHECKSIG])),
+ # 20) OP_CHECKSIGVERIFY with empty key
+ ("t20", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_0, OP_0, OP_CHECKSIGVERIFY, OP_1])),
+ # 21) Script that grows the stack to 1000 elements
+ ("t21", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_1] + [OP_DUP] * 999 + [OP_DROP] * 999)),
+ # 22) Script that grows the stack to 1001 elements
+ ("t22", CScript([pubs[1], OP_CHECKSIGVERIFY, OP_1] + [OP_DUP] * 1000 + [OP_DROP] * 1000)),
+ # 23) Script that expects an input stack of 1000 elements
+ ("t23", CScript([OP_DROP] * 999 + [pubs[1], OP_CHECKSIG])),
+ # 24) Script that expects an input stack of 1001 elements
+ ("t24", CScript([OP_DROP] * 1000 + [pubs[1], OP_CHECKSIG])),
+ # 25) Script that pushes a MAX_SCRIPT_ELEMENT_SIZE-bytes element
+ ("t25", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE), OP_DROP, pubs[1], OP_CHECKSIG])),
+ # 26) Script that pushes a (MAX_SCRIPT_ELEMENT_SIZE+1)-bytes element
+ ("t26", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, pubs[1], OP_CHECKSIG])),
+ # 27) CHECKSIGADD that must fail because numeric argument number is >4 bytes
+ ("t27", CScript([CScriptNum(OVERSIZE_NUMBER), pubs[1], OP_CHECKSIGADD])),
+ # 28) Pushes random CScriptNum value, checks OP_CHECKSIGADD result
+ ("t28", CScript([csa_high_val, pubs[1], OP_CHECKSIGADD, csa_high_result, OP_EQUAL])),
+ # 29) CHECKSIGADD that succeeds with proper sig because numeric argument number is <=4 bytes
+ ("t29", CScript([CScriptNum(OVERSIZE_NUMBER-1), pubs[1], OP_CHECKSIGADD])),
+ # 30) Variant of t1 with "normal" 33-byte pubkey
+ ("t30", CScript([b'\x03' + pubs[1], OP_CHECKSIG])),
+ # 31) Variant of t2 with "normal" 33-byte pubkey
+ ("t31", CScript([b'\x02' + pubs[1], OP_CHECKSIGVERIFY, OP_1])),
+ # 32) Variant of t28 with "normal" 33-byte pubkey
+ ("t32", CScript([csa_high_val, b'\x03' + pubs[1], OP_CHECKSIGADD, csa_high_result, OP_EQUAL])),
+ # 33) 999-of-999 multisig
+ ("t33", CScript(big_scriptops[:1998] + [OP_1])),
+ # 34) 1000-of-1000 multisig
+ ("t34", CScript(big_scriptops[:2000] + [OP_1])),
+ # 35) Variant of t9 that uses a non-minimally encoded input arg
+ ("t35", CScript([bytes([csa_low_val]), pubs[1], OP_CHECKSIGADD, csa_low_result, OP_EQUAL])),
+ # 36) Empty script
+ ("t36", CScript([])),
+ ]
+ # Add many dummies to test huge trees
+ for j in range(100000):
+ scripts.append((None, CScript([OP_RETURN, random.randrange(100000)])))
+ random.shuffle(scripts)
+ tap = taproot_construct(pubs[0], scripts)
+ common = {
+ "hashtype": hashtype,
+ "key": secs[1],
+ "tap": tap,
+ }
+ # Test that MAX_SCRIPT_ELEMENT_SIZE byte stack element inputs are valid, but not one more (and 80 bytes is standard but 81 is not).
+ add_spender(spenders, "tapscript/inputmaxlimit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random_bytes(MAX_SCRIPT_ELEMENT_SIZE)], failure={"inputs": [getter("sign"), random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1)]}, **ERR_PUSH_LIMIT)
+ add_spender(spenders, "tapscript/input80limit", leaf="t0", **common, inputs=[getter("sign"), random_bytes(80)])
+ add_spender(spenders, "tapscript/input81limit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random_bytes(81)])
+ # Test that OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY cause failure, but OP_CHECKSIG and OP_CHECKSIGVERIFY work.
+ add_spender(spenders, "tapscript/disabled_checkmultisig", leaf="t1", **common, **SINGLE_SIG, failure={"leaf": "t3"}, **ERR_TAPSCRIPT_CHECKMULTISIG)
+ add_spender(spenders, "tapscript/disabled_checkmultisigverify", leaf="t2", **common, **SINGLE_SIG, failure={"leaf": "t4"}, **ERR_TAPSCRIPT_CHECKMULTISIG)
+ # Test that OP_IF and OP_NOTIF do not accept non-0x01 as truth value (the MINIMALIF rule is consensus in Tapscript)
+ add_spender(spenders, "tapscript/minimalif", leaf="t5", **common, inputs=[getter("sign"), b'\x01'], failure={"inputs": [getter("sign"), b'\x02']}, **ERR_MINIMALIF)
+ add_spender(spenders, "tapscript/minimalnotif", leaf="t6", **common, inputs=[getter("sign"), b'\x01'], failure={"inputs": [getter("sign"), b'\x03']}, **ERR_MINIMALIF)
+ add_spender(spenders, "tapscript/minimalif", leaf="t5", **common, inputs=[getter("sign"), b'\x01'], failure={"inputs": [getter("sign"), b'\x0001']}, **ERR_MINIMALIF)
+ add_spender(spenders, "tapscript/minimalnotif", leaf="t6", **common, inputs=[getter("sign"), b'\x01'], failure={"inputs": [getter("sign"), b'\x0100']}, **ERR_MINIMALIF)
+ # Test that 1-byte public keys (which are unknown) are acceptable but nonstandard with unrelated signatures, but 0-byte public keys are not valid.
+ add_spender(spenders, "tapscript/unkpk/checksig", leaf="t16", standard=False, **common, **SINGLE_SIG, failure={"leaf": "t7"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/unkpk/checksigadd", leaf="t17", standard=False, **common, **SINGLE_SIG, failure={"leaf": "t10"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/unkpk/checksigverify", leaf="t18", standard=False, **common, **SINGLE_SIG, failure={"leaf": "t8"}, **ERR_UNKNOWN_PUBKEY)
+ # Test that 33-byte public keys (which are unknown) are acceptable but nonstandard with valid signatures, but normal pubkeys are not valid in that case.
+ add_spender(spenders, "tapscript/oldpk/checksig", leaf="t30", standard=False, **common, **SINGLE_SIG, sighash=bitflipper(default_sighash), failure={"leaf": "t1"}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "tapscript/oldpk/checksigadd", leaf="t31", standard=False, **common, **SINGLE_SIG, sighash=bitflipper(default_sighash), failure={"leaf": "t2"}, **ERR_SIG_SCHNORR)
+ add_spender(spenders, "tapscript/oldpk/checksigverify", leaf="t32", standard=False, **common, **SINGLE_SIG, sighash=bitflipper(default_sighash), failure={"leaf": "t28"}, **ERR_SIG_SCHNORR)
+ # Test that 0-byte public keys are not acceptable.
+ add_spender(spenders, "tapscript/emptypk/checksig", leaf="t1", **SINGLE_SIG, **common, failure={"leaf": "t7"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/emptypk/checksigverify", leaf="t2", **SINGLE_SIG, **common, failure={"leaf": "t8"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/emptypk/checksigadd", leaf="t9", **SINGLE_SIG, **common, failure={"leaf": "t10"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/emptypk/checksigadd", leaf="t35", standard=False, **SINGLE_SIG, **common, failure={"leaf": "t10"}, **ERR_UNKNOWN_PUBKEY)
+ # Test that OP_CHECKSIGADD results are as expected
+ add_spender(spenders, "tapscript/checksigaddresults", leaf="t28", **SINGLE_SIG, **common, failure={"leaf": "t27"}, err_msg="unknown error")
+ add_spender(spenders, "tapscript/checksigaddoversize", leaf="t29", **SINGLE_SIG, **common, failure={"leaf": "t27"}, err_msg="unknown error")
+ # Test that OP_CHECKSIGADD requires 3 stack elements.
+ add_spender(spenders, "tapscript/checksigadd3args", leaf="t9", **SINGLE_SIG, **common, failure={"leaf": "t11"}, **ERR_STACK_EMPTY)
+ # Test that empty signatures do not cause script failure in OP_CHECKSIG and OP_CHECKSIGADD (but do fail with empty pubkey, and do fail OP_CHECKSIGVERIFY)
+ add_spender(spenders, "tapscript/emptysigs/checksig", leaf="t12", **common, inputs=[b'', getter("sign")], failure={"leaf": "t13"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/emptysigs/nochecksigverify", leaf="t12", **common, inputs=[b'', getter("sign")], failure={"leaf": "t20"}, **ERR_UNKNOWN_PUBKEY)
+ add_spender(spenders, "tapscript/emptysigs/checksigadd", leaf="t14", **common, inputs=[b'', getter("sign")], failure={"leaf": "t15"}, **ERR_UNKNOWN_PUBKEY)
+ # Test that scripts over 10000 bytes (and over 201 non-push ops) are acceptable.
+ add_spender(spenders, "tapscript/no10000limit", leaf="t19", **SINGLE_SIG, **common)
+ # Test that a stack size of 1000 elements is permitted, but 1001 isn't.
+ add_spender(spenders, "tapscript/1000stack", leaf="t21", **SINGLE_SIG, **common, failure={"leaf": "t22"}, **ERR_STACK_SIZE)
+ # Test that an input stack size of 1000 elements is permitted, but 1001 isn't.
+ add_spender(spenders, "tapscript/1000inputs", leaf="t23", **common, inputs=[getter("sign")] + [b'' for _ in range(999)], failure={"leaf": "t24", "inputs": [getter("sign")] + [b'' for _ in range(1000)]}, **ERR_STACK_SIZE)
+ # Test that pushing a MAX_SCRIPT_ELEMENT_SIZE byte stack element is valid, but one longer is not.
+ add_spender(spenders, "tapscript/pushmaxlimit", leaf="t25", **common, **SINGLE_SIG, failure={"leaf": "t26"}, **ERR_PUSH_LIMIT)
+ # Test that 999-of-999 multisig works (but 1000-of-1000 triggers stack size limits)
+ add_spender(spenders, "tapscript/bigmulti", leaf="t33", **common, inputs=big_spend_inputs, num=999, failure={"leaf": "t34", "num": 1000}, **ERR_STACK_SIZE)
+ # Test that the CLEANSTACK rule is consensus critical in tapscript
+ add_spender(spenders, "tapscript/cleanstack", leaf="t36", tap=tap, inputs=[b'\x01'], failure={"inputs": [b'\x01', b'\x01']}, **ERR_CLEANSTACK)
+
+ # == Test for sigops ratio limit ==
+
+ # Given a number n, and a public key pk, functions that produce a (CScript, sigops). Each script takes as
+ # input a valid signature with the passed pk followed by a dummy push of bytes that are to be dropped, and
+ # will execute sigops signature checks.
+ SIGOPS_RATIO_SCRIPTS = [
+ # n OP_CHECKSIGVERFIYs and 1 OP_CHECKSIG.
+ lambda n, pk: (CScript([OP_DROP, pk] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_CHECKSIG]), n + 1),
+ # n OP_CHECKSIGVERIFYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIGVERIFY.
+ lambda n, pk: (CScript([OP_DROP, pk, OP_0, OP_IF, OP_2DUP, OP_CHECKSIGVERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_2, OP_SWAP, OP_CHECKSIGADD, OP_3, OP_EQUAL]), n + 1),
+ # n OP_CHECKSIGVERIFYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIG.
+ lambda n, pk: (CScript([random_bytes(220), OP_2DROP, pk, OP_1, OP_NOTIF, OP_2DUP, OP_CHECKSIG, OP_VERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_4, OP_SWAP, OP_CHECKSIGADD, OP_5, OP_EQUAL]), n + 1),
+ # n OP_CHECKSIGVERFIYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIGADD.
+ lambda n, pk: (CScript([OP_DROP, pk, OP_1, OP_IF, OP_ELSE, OP_2DUP, OP_6, OP_SWAP, OP_CHECKSIGADD, OP_7, OP_EQUALVERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_8, OP_SWAP, OP_CHECKSIGADD, OP_9, OP_EQUAL]), n + 1),
+ # n+1 OP_CHECKSIGs, but also one OP_CHECKSIG with an empty signature.
+ lambda n, pk: (CScript([OP_DROP, OP_0, pk, OP_CHECKSIG, OP_NOT, OP_VERIFY, pk] + [OP_2DUP, OP_CHECKSIG, OP_VERIFY] * n + [OP_CHECKSIG]), n + 1),
+ # n OP_CHECKSIGADDs and 1 OP_CHECKSIG, but also an OP_CHECKSIGADD with an empty signature.
+ lambda n, pk: (CScript([OP_DROP, OP_0, OP_10, pk, OP_CHECKSIGADD, OP_10, OP_EQUALVERIFY, pk] + [OP_2DUP, OP_16, OP_SWAP, OP_CHECKSIGADD, b'\x11', OP_EQUALVERIFY] * n + [OP_CHECKSIG]), n + 1),
+ ]
+ for annex in [None, bytes([ANNEX_TAG]) + random_bytes(random.randrange(1000))]:
+ for hashtype in [SIGHASH_DEFAULT, SIGHASH_ALL]:
+ for pubkey in [pubs[1], random_bytes(random.choice([x for x in range(2, 81) if x != 32]))]:
+ for fn_num, fn in enumerate(SIGOPS_RATIO_SCRIPTS):
+ merkledepth = random.randrange(129)
+
+
+ def predict_sigops_ratio(n, dummy_size):
+ """Predict whether spending fn(n, pubkey) with dummy_size will pass the ratio test."""
+ script, sigops = fn(n, pubkey)
+ # Predict the size of the witness for a given choice of n
+ stacklen_size = 1
+ sig_size = 64 + (hashtype != SIGHASH_DEFAULT)
+ siglen_size = 1
+ dummylen_size = 1 + 2 * (dummy_size >= 253)
+ script_size = len(script)
+ scriptlen_size = 1 + 2 * (script_size >= 253)
+ control_size = 33 + 32 * merkledepth
+ controllen_size = 1 + 2 * (control_size >= 253)
+ annex_size = 0 if annex is None else len(annex)
+ annexlen_size = 0 if annex is None else 1 + 2 * (annex_size >= 253)
+ witsize = stacklen_size + sig_size + siglen_size + dummy_size + dummylen_size + script_size + scriptlen_size + control_size + controllen_size + annex_size + annexlen_size
+ # sigops ratio test
+ return witsize + 50 >= 50 * sigops
+ # Make sure n is high enough that with empty dummy, the script is not valid
+ n = 0
+ while predict_sigops_ratio(n, 0):
+ n += 1
+ # But allow picking a bit higher still
+ n += random.randrange(5)
+ # Now pick dummy size *just* large enough that the overall construction passes
+ dummylen = 0
+ while not predict_sigops_ratio(n, dummylen):
+ dummylen += 1
+ scripts = [("s", fn(n, pubkey)[0])]
+ for _ in range(merkledepth):
+ scripts = [scripts, random.choice(PARTNER_MERKLE_FN)]
+ tap = taproot_construct(pubs[0], scripts)
+ standard = annex is None and dummylen <= 80 and len(pubkey) == 32
+ add_spender(spenders, "tapscript/sigopsratio_%i" % fn_num, tap=tap, leaf="s", annex=annex, hashtype=hashtype, key=secs[1], inputs=[getter("sign"), random_bytes(dummylen)], standard=standard, failure={"inputs": [getter("sign"), random_bytes(dummylen - 1)]}, **ERR_SIGOPS_RATIO)
+
+ # Future leaf versions
+ for leafver in range(0, 0x100, 2):
+ if leafver == LEAF_VERSION_TAPSCRIPT or leafver == ANNEX_TAG:
+ # Skip the defined LEAF_VERSION_TAPSCRIPT, and the ANNEX_TAG which is not usable as leaf version
+ continue
+ scripts = [
+ ("bare_c0", CScript([OP_NOP])),
+ ("bare_unkver", CScript([OP_NOP]), leafver),
+ ("return_c0", CScript([OP_RETURN])),
+ ("return_unkver", CScript([OP_RETURN]), leafver),
+ ("undecodable_c0", CScript([OP_PUSHDATA1])),
+ ("undecodable_unkver", CScript([OP_PUSHDATA1]), leafver),
+ ("bigpush_c0", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP])),
+ ("bigpush_unkver", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP]), leafver),
+ ("1001push_c0", CScript([OP_0] * 1001)),
+ ("1001push_unkver", CScript([OP_0] * 1001), leafver),
+ ]
+ random.shuffle(scripts)
+ tap = taproot_construct(pubs[0], scripts)
+ add_spender(spenders, "unkver/bare", standard=False, tap=tap, leaf="bare_unkver", failure={"leaf": "bare_c0"}, **ERR_CLEANSTACK)
+ add_spender(spenders, "unkver/return", standard=False, tap=tap, leaf="return_unkver", failure={"leaf": "return_c0"}, **ERR_OP_RETURN)
+ add_spender(spenders, "unkver/undecodable", standard=False, tap=tap, leaf="undecodable_unkver", failure={"leaf": "undecodable_c0"}, **ERR_UNDECODABLE)
+ add_spender(spenders, "unkver/bigpush", standard=False, tap=tap, leaf="bigpush_unkver", failure={"leaf": "bigpush_c0"}, **ERR_PUSH_LIMIT)
+ add_spender(spenders, "unkver/1001push", standard=False, tap=tap, leaf="1001push_unkver", failure={"leaf": "1001push_c0"}, **ERR_STACK_SIZE)
+ add_spender(spenders, "unkver/1001inputs", standard=False, tap=tap, leaf="bare_unkver", inputs=[b'']*1001, failure={"leaf": "bare_c0"}, **ERR_STACK_SIZE)
+
+ # OP_SUCCESSx tests.
+ hashtype = lambda _: random.choice(VALID_SIGHASHES_TAPROOT)
+ for opval in range(76, 0x100):
+ opcode = CScriptOp(opval)
+ if not is_op_success(opcode):
+ continue
+ scripts = [
+ ("bare_success", CScript([opcode])),
+ ("bare_nop", CScript([OP_NOP])),
+ ("unexecif_success", CScript([OP_0, OP_IF, opcode, OP_ENDIF])),
+ ("unexecif_nop", CScript([OP_0, OP_IF, OP_NOP, OP_ENDIF])),
+ ("return_success", CScript([OP_RETURN, opcode])),
+ ("return_nop", CScript([OP_RETURN, OP_NOP])),
+ ("undecodable_success", CScript([opcode, OP_PUSHDATA1])),
+ ("undecodable_nop", CScript([OP_NOP, OP_PUSHDATA1])),
+ ("undecodable_bypassed_success", CScript([OP_PUSHDATA1, OP_2, opcode])),
+ ("bigpush_success", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, opcode])),
+ ("bigpush_nop", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, OP_NOP])),
+ ("1001push_success", CScript([OP_0] * 1001 + [opcode])),
+ ("1001push_nop", CScript([OP_0] * 1001 + [OP_NOP])),
+ ]
+ random.shuffle(scripts)
+ tap = taproot_construct(pubs[0], scripts)
+ add_spender(spenders, "opsuccess/bare", standard=False, tap=tap, leaf="bare_success", failure={"leaf": "bare_nop"}, **ERR_CLEANSTACK)
+ add_spender(spenders, "opsuccess/unexecif", standard=False, tap=tap, leaf="unexecif_success", failure={"leaf": "unexecif_nop"}, **ERR_CLEANSTACK)
+ add_spender(spenders, "opsuccess/return", standard=False, tap=tap, leaf="return_success", failure={"leaf": "return_nop"}, **ERR_OP_RETURN)
+ add_spender(spenders, "opsuccess/undecodable", standard=False, tap=tap, leaf="undecodable_success", failure={"leaf": "undecodable_nop"}, **ERR_UNDECODABLE)
+ add_spender(spenders, "opsuccess/undecodable_bypass", standard=False, tap=tap, leaf="undecodable_success", failure={"leaf": "undecodable_bypassed_success"}, **ERR_UNDECODABLE)
+ add_spender(spenders, "opsuccess/bigpush", standard=False, tap=tap, leaf="bigpush_success", failure={"leaf": "bigpush_nop"}, **ERR_PUSH_LIMIT)
+ add_spender(spenders, "opsuccess/1001push", standard=False, tap=tap, leaf="1001push_success", failure={"leaf": "1001push_nop"}, **ERR_STACK_SIZE)
+ add_spender(spenders, "opsuccess/1001inputs", standard=False, tap=tap, leaf="bare_success", inputs=[b'']*1001, failure={"leaf": "bare_nop"}, **ERR_STACK_SIZE)
+
+ # Non-OP_SUCCESSx (verify that those aren't accidentally treated as OP_SUCCESSx)
+ for opval in range(0, 0x100):
+ opcode = CScriptOp(opval)
+ if is_op_success(opcode):
+ continue
+ scripts = [
+ ("normal", CScript([OP_RETURN, opcode] + [OP_NOP] * 75)),
+ ("op_success", CScript([OP_RETURN, CScriptOp(0x50)]))
+ ]
+ tap = taproot_construct(pubs[0], scripts)
+ add_spender(spenders, "alwaysvalid/notsuccessx", tap=tap, leaf="op_success", inputs=[], standard=False, failure={"leaf": "normal"}) # err_msg differs based on opcode
+
+ # == Legacy tests ==
+
+ # Also add a few legacy spends into the mix, so that transactions which combine taproot and pre-taproot spends get tested too.
+ for compressed in [False, True]:
+ eckey1 = ECKey()
+ eckey1.set(generate_privkey(), compressed)
+ pubkey1 = eckey1.get_pubkey().get_bytes()
+ eckey2 = ECKey()
+ eckey2.set(generate_privkey(), compressed)
+ for p2sh in [False, True]:
+ for witv0 in [False, True]:
+ for hashtype in VALID_SIGHASHES_ECDSA + [random.randrange(0x04, 0x80), random.randrange(0x84, 0x100)]:
+ standard = (hashtype in VALID_SIGHASHES_ECDSA) and (compressed or not witv0)
+ add_spender(spenders, "legacy/pk-wrongkey", hashtype=hashtype, p2sh=p2sh, witv0=witv0, standard=standard, script=CScript([pubkey1, OP_CHECKSIG]), **SINGLE_SIG, key=eckey1, failure={"key": eckey2}, sigops_weight=4-3*witv0, **ERR_NO_SUCCESS)
+ add_spender(spenders, "legacy/pkh-sighashflip", hashtype=hashtype, p2sh=p2sh, witv0=witv0, standard=standard, pkh=pubkey1, key=eckey1, **SIGHASH_BITFLIP, sigops_weight=4-3*witv0, **ERR_NO_SUCCESS)
+
+ # Verify that OP_CHECKSIGADD wasn't accidentally added to pre-taproot validation logic.
+ for p2sh in [False, True]:
+ for witv0 in [False, True]:
+ for hashtype in VALID_SIGHASHES_ECDSA + [random.randrange(0x04, 0x80), random.randrange(0x84, 0x100)]:
+ standard = hashtype in VALID_SIGHASHES_ECDSA and (p2sh or witv0)
+ add_spender(spenders, "compat/nocsa", hashtype=hashtype, p2sh=p2sh, witv0=witv0, standard=standard, script=CScript([OP_IF, OP_11, pubkey1, OP_CHECKSIGADD, OP_12, OP_EQUAL, OP_ELSE, pubkey1, OP_CHECKSIG, OP_ENDIF]), key=eckey1, sigops_weight=4-3*witv0, inputs=[getter("sign"), b''], failure={"inputs": [getter("sign"), b'\x01']}, **ERR_UNDECODABLE)
+
+ return spenders
+
+def spenders_taproot_inactive():
+ """Spenders for testing that pre-activation Taproot rules don't apply."""
+
+ spenders = []
+
+ sec = generate_privkey()
+ pub, _ = compute_xonly_pubkey(sec)
+ scripts = [
+ ("pk", CScript([pub, OP_CHECKSIG])),
+ ("future_leaf", CScript([pub, OP_CHECKSIG]), 0xc2),
+ ("op_success", CScript([pub, OP_CHECKSIG, OP_0, OP_IF, CScriptOp(0x50), OP_ENDIF])),
+ ]
+ tap = taproot_construct(pub, scripts)
+
+ # Test that keypath spending is valid & standard if compliant, but valid and nonstandard otherwise.
+ add_spender(spenders, "inactive/keypath_valid", key=sec, tap=tap)
+ add_spender(spenders, "inactive/keypath_invalidsig", key=sec, tap=tap, standard=False, sighash=bitflipper(default_sighash))
+ add_spender(spenders, "inactive/keypath_empty", key=sec, tap=tap, standard=False, witness=[])
+
+ # Same for scriptpath spending (but using future features like annex, leaf versions, or OP_SUCCESS is nonstandard).
+ add_spender(spenders, "inactive/scriptpath_valid", key=sec, tap=tap, leaf="pk", inputs=[getter("sign")])
+ add_spender(spenders, "inactive/scriptpath_invalidsig", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
+ add_spender(spenders, "inactive/scriptpath_invalidcb", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], controlblock=bitflipper(default_controlblock))
+ add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])
+ add_spender(spenders, "inactive/scriptpath_invalid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
+ add_spender(spenders, "inactive/scriptpath_valid_opsuccess", key=sec, tap=tap, leaf="op_success", standard=False, inputs=[getter("sign")])
+ add_spender(spenders, "inactive/scriptpath_valid_opsuccess", key=sec, tap=tap, leaf="op_success", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
+
+ return spenders
+
+# Consensus validation flags to use in dumps for tests with "legacy/" or "inactive/" prefix.
+LEGACY_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY"
+# Consensus validation flags to use in dumps for all other tests.
+TAPROOT_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY,TAPROOT"
+
+def dump_json_test(tx, input_utxos, idx, success, failure):
+ spender = input_utxos[idx].spender
+ # Determine flags to dump
+ flags = LEGACY_FLAGS if spender.comment.startswith("legacy/") or spender.comment.startswith("inactive/") else TAPROOT_FLAGS
+
+ fields = [
+ ("tx", tx.serialize().hex()),
+ ("prevouts", [x.output.serialize().hex() for x in input_utxos]),
+ ("index", idx),
+ ("flags", flags),
+ ("comment", spender.comment)
+ ]
+
+ # The "final" field indicates that a spend should be always valid, even with more validation flags enabled
+ # than the listed ones. Use standardness as a proxy for this (which gives a conservative underestimate).
+ if spender.is_standard:
+ fields.append(("final", True))
+
+ def dump_witness(wit):
+ return OrderedDict([("scriptSig", wit[0].hex()), ("witness", [x.hex() for x in wit[1]])])
+ if success is not None:
+ fields.append(("success", dump_witness(success)))
+ if failure is not None:
+ fields.append(("failure", dump_witness(failure)))
+
+ # Write the dump to $TEST_DUMP_DIR/x/xyz... where x,y,z,... are the SHA1 sum of the dump (which makes the
+ # file naming scheme compatible with fuzzing infrastructure).
+ dump = json.dumps(OrderedDict(fields)) + ",\n"
+ sha1 = hashlib.sha1(dump.encode("utf-8")).hexdigest()
+ dirname = os.environ.get("TEST_DUMP_DIR", ".") + ("/%s" % sha1[0])
+ os.makedirs(dirname, exist_ok=True)
+ with open(dirname + ("/%s" % sha1), 'w', encoding="utf8") as f:
+ f.write(dump)
+
+# Data type to keep track of UTXOs, where they were created, and how to spend them.
+UTXOData = namedtuple('UTXOData', 'outpoint,output,spender')
+
+class TaprootTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ parser.add_argument("--dumptests", dest="dump_tests", default=False, action="store_true",
+ help="Dump generated test cases to directory set by TEST_DUMP_DIR environment variable")
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ # Node 0 has Taproot inactive, Node 1 active.
+ self.extra_args = [["-whitelist=127.0.0.1", "-par=1", "-vbparams=taproot:1:1"], ["-whitelist=127.0.0.1", "-par=1"]]
+
+ def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):
+
+ # Deplete block of any non-tapscript sigops using a single additional 0-value coinbase output.
+ # It is not impossible to fit enough tapscript sigops to hit the old 80k limit without
+ # busting txin-level limits. We simply have to account for the p2pk outputs in all
+ # transactions.
+ extra_output_script = CScript([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR))
+
+ block = create_block(self.tip, create_coinbase(self.lastblockheight + 1, pubkey=cb_pubkey, extra_output_script=extra_output_script, fees=fees), self.lastblocktime + 1)
+ block.nVersion = 4
+ for tx in txs:
+ tx.rehash()
+ block.vtx.append(tx)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ witness and add_witness_commitment(block)
+ block.rehash()
+ block.solve()
+ block_response = node.submitblock(block.serialize(True).hex())
+ if err_msg is not None:
+ assert block_response is not None and err_msg in block_response, "Missing error message '%s' from block response '%s': %s" % (err_msg, "(None)" if block_response is None else block_response, msg)
+ if (accept):
+ assert node.getbestblockhash() == block.hash, "Failed to accept: %s (response: %s)" % (msg, block_response)
+ self.tip = block.sha256
+ self.lastblockhash = block.hash
+ self.lastblocktime += 1
+ self.lastblockheight += 1
+ else:
+ assert node.getbestblockhash() == self.lastblockhash, "Failed to reject: " + msg
+
+ def test_spenders(self, node, spenders, input_counts):
+ """Run randomized tests with a number of "spenders".
+
+ Steps:
+ 1) Generate an appropriate UTXO for each spender to test spend conditions
+ 2) Generate 100 random addresses of all wallet types: pkh/sh_wpkh/wpkh
+ 3) Select random number of inputs from (1)
+ 4) Select random number of addresses from (2) as outputs
+
+ Each spender embodies a test; in a large randomized test, it is verified
+ that toggling the valid argument to each lambda toggles the validity of
+ the transaction. This is accomplished by constructing transactions consisting
+ of all valid inputs, except one invalid one.
+ """
+
+ # Construct a bunch of sPKs that send coins back to the host wallet
+ self.log.info("- Constructing addresses for returning coins")
+ host_spks = []
+ host_pubkeys = []
+ for i in range(16):
+ addr = node.getnewaddress(address_type=random.choice(["legacy", "p2sh-segwit", "bech32"]))
+ info = node.getaddressinfo(addr)
+ spk = bytes.fromhex(info['scriptPubKey'])
+ host_spks.append(spk)
+ host_pubkeys.append(bytes.fromhex(info['pubkey']))
+
+ # Initialize variables used by block_submit().
+ self.lastblockhash = node.getbestblockhash()
+ self.tip = int(self.lastblockhash, 16)
+ block = node.getblock(self.lastblockhash)
+ self.lastblockheight = block['height']
+ self.lastblocktime = block['time']
+
+ # Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and
+ # one change output at the end. The transaction is constructed on the Python side to enable
+ # having multiple outputs to the same address and outputs with no assigned address. The wallet
+ # is then asked to sign it through signrawtransactionwithwallet, and then added to a block on the
+ # Python side (to bypass standardness rules).
+ self.log.info("- Creating test UTXOs...")
+ random.shuffle(spenders)
+ normal_utxos = []
+ mismatching_utxos = [] # UTXOs with input that requires mismatching output position
+ done = 0
+ while done < len(spenders):
+ # Compute how many UTXOs to create with this transaction
+ count_this_tx = min(len(spenders) - done, (len(spenders) + 4) // 5, 10000)
+
+ fund_tx = CTransaction()
+ # Add the 50 highest-value inputs
+ unspents = node.listunspent()
+ random.shuffle(unspents)
+ unspents.sort(key=lambda x: int(x["amount"] * 100000000), reverse=True)
+ if len(unspents) > 50:
+ unspents = unspents[:50]
+ random.shuffle(unspents)
+ balance = 0
+ for unspent in unspents:
+ balance += int(unspent["amount"] * 100000000)
+ txid = int(unspent["txid"], 16)
+ fund_tx.vin.append(CTxIn(COutPoint(txid, int(unspent["vout"])), CScript()))
+ # Add outputs
+ cur_progress = done / len(spenders)
+ next_progress = (done + count_this_tx) / len(spenders)
+ change_goal = (1.0 - 0.6 * next_progress) / (1.0 - 0.6 * cur_progress) * balance
+ self.log.debug("Create %i UTXOs in a transaction spending %i inputs worth %.8f (sending ~%.8f to change)" % (count_this_tx, len(unspents), balance * 0.00000001, change_goal * 0.00000001))
+ for i in range(count_this_tx):
+ avg = (balance - change_goal) / (count_this_tx - i)
+ amount = int(random.randrange(int(avg*0.85 + 0.5), int(avg*1.15 + 0.5)) + 0.5)
+ balance -= amount
+ fund_tx.vout.append(CTxOut(amount, spenders[done + i].script))
+ # Add change
+ fund_tx.vout.append(CTxOut(balance - 10000, random.choice(host_spks)))
+ # Ask the wallet to sign
+ ss = BytesIO(bytes.fromhex(node.signrawtransactionwithwallet(ToHex(fund_tx))["hex"]))
+ fund_tx.deserialize(ss)
+ # Construct UTXOData entries
+ fund_tx.rehash()
+ for i in range(count_this_tx):
+ utxodata = UTXOData(outpoint=COutPoint(fund_tx.sha256, i), output=fund_tx.vout[i], spender=spenders[done])
+ if utxodata.spender.need_vin_vout_mismatch:
+ mismatching_utxos.append(utxodata)
+ else:
+ normal_utxos.append(utxodata)
+ done += 1
+ # Mine into a block
+ self.block_submit(node, [fund_tx], "Funding tx", None, random.choice(host_pubkeys), 10000, MAX_BLOCK_SIGOPS_WEIGHT, True, True)
+
+ # Consume groups of choice(input_coins) from utxos in a tx, testing the spenders.
+ self.log.info("- Running %i spending tests" % done)
+ random.shuffle(normal_utxos)
+ random.shuffle(mismatching_utxos)
+ assert done == len(normal_utxos) + len(mismatching_utxos)
+
+ left = done
+ while left:
+ # Construct CTransaction with random nVersion, nLocktime
+ tx = CTransaction()
+ tx.nVersion = random.choice([1, 2, random.randint(-0x80000000, 0x7fffffff)])
+ min_sequence = (tx.nVersion != 1 and tx.nVersion != 0) * 0x80000000 # The minimum sequence number to disable relative locktime
+ if random.choice([True, False]):
+ tx.nLockTime = random.randrange(LOCKTIME_THRESHOLD, self.lastblocktime - 7200) # all absolute locktimes in the past
+ else:
+ tx.nLockTime = random.randrange(self.lastblockheight + 1) # all block heights in the past
+
+ # Decide how many UTXOs to test with.
+ acceptable = [n for n in input_counts if n <= left and (left - n > max(input_counts) or (left - n) in [0] + input_counts)]
+ num_inputs = random.choice(acceptable)
+
+ # If we have UTXOs that require mismatching inputs/outputs left, include exactly one of those
+ # unless there is only one normal UTXO left (as tests with mismatching UTXOs require at least one
+ # normal UTXO to go in the first position), and we don't want to run out of normal UTXOs.
+ input_utxos = []
+ while len(mismatching_utxos) and (len(input_utxos) == 0 or len(normal_utxos) == 1):
+ input_utxos.append(mismatching_utxos.pop())
+ left -= 1
+
+ # Top up until we hit num_inputs (but include at least one normal UTXO always).
+ for _ in range(max(1, num_inputs - len(input_utxos))):
+ input_utxos.append(normal_utxos.pop())
+ left -= 1
+
+ # The first input cannot require a mismatching output (as there is at least one output).
+ while True:
+ random.shuffle(input_utxos)
+ if not input_utxos[0].spender.need_vin_vout_mismatch:
+ break
+ first_mismatch_input = None
+ for i in range(len(input_utxos)):
+ if input_utxos[i].spender.need_vin_vout_mismatch:
+ first_mismatch_input = i
+ assert first_mismatch_input is None or first_mismatch_input > 0
+
+ # Decide fee, and add CTxIns to tx.
+ amount = sum(utxo.output.nValue for utxo in input_utxos)
+ fee = min(random.randrange(MIN_FEE * 2, MIN_FEE * 4), amount - DUST_LIMIT) # 10000-20000 sat fee
+ in_value = amount - fee
+ tx.vin = [CTxIn(outpoint=utxo.outpoint, nSequence=random.randint(min_sequence, 0xffffffff)) for utxo in input_utxos]
+ tx.wit.vtxinwit = [CTxInWitness() for _ in range(len(input_utxos))]
+ sigops_weight = sum(utxo.spender.sigops_weight for utxo in input_utxos)
+ self.log.debug("Test: %s" % (", ".join(utxo.spender.comment for utxo in input_utxos)))
+
+ # Add 1 to 4 random outputs (but constrained by inputs that require mismatching outputs)
+ num_outputs = random.choice(range(1, 1 + min(4, 4 if first_mismatch_input is None else first_mismatch_input)))
+ assert in_value >= 0 and fee - num_outputs * DUST_LIMIT >= MIN_FEE
+ for i in range(num_outputs):
+ tx.vout.append(CTxOut())
+ if in_value <= DUST_LIMIT:
+ tx.vout[-1].nValue = DUST_LIMIT
+ elif i < num_outputs - 1:
+ tx.vout[-1].nValue = in_value
+ else:
+ tx.vout[-1].nValue = random.randint(DUST_LIMIT, in_value)
+ in_value -= tx.vout[-1].nValue
+ tx.vout[-1].scriptPubKey = random.choice(host_spks)
+ sigops_weight += CScript(tx.vout[-1].scriptPubKey).GetSigOpCount(False) * WITNESS_SCALE_FACTOR
+ fee += in_value
+ assert fee >= 0
+
+ # Select coinbase pubkey
+ cb_pubkey = random.choice(host_pubkeys)
+ sigops_weight += 1 * WITNESS_SCALE_FACTOR
+
+ # Precompute one satisfying and one failing scriptSig/witness for each input.
+ input_data = []
+ for i in range(len(input_utxos)):
+ fn = input_utxos[i].spender.sat_function
+ fail = None
+ success = fn(tx, i, [utxo.output for utxo in input_utxos], True)
+ if not input_utxos[i].spender.no_fail:
+ fail = fn(tx, i, [utxo.output for utxo in input_utxos], False)
+ input_data.append((fail, success))
+ if self.options.dump_tests:
+ dump_json_test(tx, input_utxos, i, success, fail)
+
+ # Sign each input incorrectly once on each complete signing pass, except the very last.
+ for fail_input in list(range(len(input_utxos))) + [None]:
+ # Skip trying to fail at spending something that can't be made to fail.
+ if fail_input is not None and input_utxos[fail_input].spender.no_fail:
+ continue
+ # Expected message with each input failure, may be None(which is ignored)
+ expected_fail_msg = None if fail_input is None else input_utxos[fail_input].spender.err_msg
+ # Fill inputs/witnesses
+ for i in range(len(input_utxos)):
+ tx.vin[i].scriptSig = input_data[i][i != fail_input][0]
+ tx.wit.vtxinwit[i].scriptWitness.stack = input_data[i][i != fail_input][1]
+ # Submit to mempool to check standardness
+ is_standard_tx = fail_input is None and all(utxo.spender.is_standard for utxo in input_utxos) and tx.nVersion >= 1 and tx.nVersion <= 2
+ tx.rehash()
+ msg = ','.join(utxo.spender.comment + ("*" if n == fail_input else "") for n, utxo in enumerate(input_utxos))
+ if is_standard_tx:
+ node.sendrawtransaction(tx.serialize().hex(), 0)
+ assert node.getmempoolentry(tx.hash) is not None, "Failed to accept into mempool: " + msg
+ else:
+ assert_raises_rpc_error(-26, None, node.sendrawtransaction, tx.serialize().hex(), 0)
+ # Submit in a block
+ self.block_submit(node, [tx], msg, witness=True, accept=fail_input is None, cb_pubkey=cb_pubkey, fees=fee, sigops_weight=sigops_weight, err_msg=expected_fail_msg)
+
+ if (len(spenders) - left) // 200 > (len(spenders) - left - len(input_utxos)) // 200:
+ self.log.info(" - %i tests done" % (len(spenders) - left))
+
+ assert left == 0
+ assert len(normal_utxos) == 0
+ assert len(mismatching_utxos) == 0
+ self.log.info(" - Done")
+
+ def run_test(self):
+ self.connect_nodes(0, 1)
+
+ # 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.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
+
+ # Transfer % of funds to pre-taproot node.
+ addr = self.nodes[0].getnewaddress()
+ self.nodes[1].sendtoaddress(address=addr, amount=int(self.nodes[1].getbalance() * 70000000) / 100000000)
+ self.nodes[1].generate(1)
+ self.sync_blocks()
+
+ # Pre-taproot activation tests.
+ self.log.info("Pre-activation tests...")
+ self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[1, 2, 2, 2, 2, 3])
+
+
+if __name__ == '__main__':
+ TaprootTest().main()
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 0713925141..2e4f4796b0 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -12,9 +12,8 @@ import re
from test_framework.blocktools import create_block, create_coinbase
from test_framework.messages import msg_block
-from test_framework.mininode import P2PInterface, mininode_lock
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import wait_until
VB_PERIOD = 144 # versionbits period length for regtest
VB_THRESHOLD = 108 # versionbits activation threshold for regtest
@@ -62,7 +61,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
node_deterministic_address = node.get_deterministic_priv_key().address
# Mine one period worth of blocks
@@ -70,7 +69,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.")
# Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address)
# Check that we're not getting any versionbit-related errors in get*info()
@@ -78,7 +77,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
@@ -91,14 +90,14 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Generating one block guarantees that we'll get out of IBD
node.generatetoaddress(1, node_deterministic_address)
- wait_until(lambda: not node.getblockchaininfo()['initialblockdownload'], timeout=10, lock=mininode_lock)
+ self.wait_until(lambda: not node.getblockchaininfo()['initialblockdownload'])
# Generating one more block will be enough to generate an error.
node.generatetoaddress(1, node_deterministic_address)
# Check that get*info() shows the versionbits unknown rules warning
assert WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"]
assert WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"]
# Check that the alert file shows the versionbits unknown rules warning
- wait_until(lambda: self.versionbits_in_alert_file(), timeout=60)
+ self.wait_until(lambda: self.versionbits_in_alert_file())
if __name__ == '__main__':
VersionBitsWarningTest().main()
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 80003aca0d..1257dff1ae 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -71,7 +71,14 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
assert_equal(cli_get_info['headers'], blockchain_info['headers'])
assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
- assert_equal(cli_get_info['connections'], network_info['connections'])
+ assert_equal(
+ cli_get_info['connections'],
+ {
+ 'in': network_info['connections_in'],
+ 'out': network_info['connections_out'],
+ 'total': network_info['connections']
+ }
+ )
assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
assert_equal(cli_get_info['chain'], blockchain_info['chain'])
@@ -88,7 +95,7 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(self.nodes[0].cli.getwalletinfo(), wallet_info)
# Setup to test -getinfo, -generate, and -rpcwallet= with multiple wallets.
- wallets = ['', 'Encrypted', 'secret']
+ wallets = [self.default_wallet_name, 'Encrypted', 'secret']
amounts = [BALANCE + Decimal('9.999928'), Decimal(9), Decimal(31)]
self.nodes[0].createwallet(wallet_name=wallets[1])
self.nodes[0].createwallet(wallet_name=wallets[2])
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index ac2e73fc5c..9c877aaeae 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -45,7 +45,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
# work fine.
{"method": "invalidmethod", "id": 2},
# Another call that should succeed.
- {"method": "getbestblockhash", "id": 3},
+ {"method": "getblockhash", "id": 3, "params": [0]},
])
result_by_id = {}
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 89c55f31f3..d675ae174c 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,13 +5,23 @@
"""Test the ZMQ notification interface."""
import struct
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction, hash256
-from test_framework.util import assert_equal, connect_nodes
+from test_framework.messages import CTransaction, hash256, FromHex
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
from io import BytesIO
from time import sleep
+# Test may be skipped and not have zmq installed
+try:
+ import zmq
+except ImportError:
+ pass
+
def hash256_reversed(byte_str):
return hash256(byte_str)[::-1]
@@ -21,7 +31,6 @@ class ZMQSubscriber:
self.socket = socket
self.topic = topic
- import zmq
self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
def receive(self):
@@ -33,6 +42,22 @@ class ZMQSubscriber:
self.sequence += 1
return body
+ def receive_sequence(self):
+ topic, body, seq = self.socket.recv_multipart()
+ # Topic should match the subscriber topic.
+ assert_equal(topic, self.topic)
+ # Sequence should be incremental.
+ assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ self.sequence += 1
+ hash = body[:32].hex()
+ label = chr(body[32])
+ mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
+ if mempool_sequence is not None:
+ assert label == "A" or label == "R"
+ else:
+ assert label == "D" or label == "C"
+ return (hash, label, mempool_sequence)
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
@@ -43,39 +68,43 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_bitcoind_zmq()
def run_test(self):
- import zmq
self.ctx = zmq.Context()
try:
self.test_basic()
+ self.test_sequence()
+ self.test_mempool_sync()
self.test_reorg()
+ self.test_multiple_interfaces()
finally:
# Destroy the ZMQ context.
self.log.debug("Destroying ZMQ context")
self.ctx.destroy(linger=None)
def test_basic(self):
- # All messages are received in the same socket which means
- # that this test fails if the publishing order changes.
- # Note that the publishing order is not defined in the documentation and
- # is subject to change.
- import zmq
# Invalid zmq arguments don't take down the node, see #17185.
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
address = 'tcp://127.0.0.1:28332'
- socket = self.ctx.socket(zmq.SUB)
- socket.set(zmq.RCVTIMEO, 60000)
+ sockets = []
+ subs = []
+ services = [b"hashblock", b"hashtx", b"rawblock", b"rawtx"]
+ for service in services:
+ sockets.append(self.ctx.socket(zmq.SUB))
+ sockets[-1].set(zmq.RCVTIMEO, 60000)
+ subs.append(ZMQSubscriber(sockets[-1], service))
# Subscribe to all available topics.
- hashblock = ZMQSubscriber(socket, b"hashblock")
- hashtx = ZMQSubscriber(socket, b"hashtx")
- rawblock = ZMQSubscriber(socket, b"rawblock")
- rawtx = ZMQSubscriber(socket, b"rawtx")
+ hashblock = subs[0]
+ hashtx = subs[1]
+ rawblock = subs[2]
+ rawtx = subs[3]
self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]])
- connect_nodes(self.nodes[0], 1)
- socket.connect(address)
+ self.connect_nodes(0, 1)
+ for socket in sockets:
+ socket.connect(address)
+
# Relax so that the subscriber is ready before publishing zmq messages
sleep(0.2)
@@ -96,15 +125,16 @@ class ZMQTest (BitcoinTestFramework):
tx.calc_sha256()
assert_equal(tx.hash, txid.hex())
+ # Should receive the generated raw block.
+ block = rawblock.receive()
+ assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
+
# Should receive the generated block hash.
hash = hashblock.receive().hex()
assert_equal(genhashes[x], hash)
# The block should only have the coinbase txid.
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
- # Should receive the generated raw block.
- block = rawblock.receive()
- assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
if self.is_wallet_compiled():
self.log.info("Wait for tx from second node")
@@ -119,6 +149,13 @@ class ZMQTest (BitcoinTestFramework):
hex = rawtx.receive()
assert_equal(payment_txid, hash256_reversed(hex).hex())
+ # Mining the block with this tx should result in second notification
+ # after coinbase tx notification
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ hashtx.receive()
+ txid = hashtx.receive()
+ assert_equal(payment_txid, txid.hex())
+
self.log.info("Test the getzmqnotifications RPC")
assert_equal(self.nodes[0].getzmqnotifications(), [
@@ -131,30 +168,366 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(self.nodes[1].getzmqnotifications(), [])
def test_reorg(self):
- import zmq
+ if not self.is_wallet_compiled():
+ self.log.info("Skipping reorg test because wallet is disabled")
+ return
+
address = 'tcp://127.0.0.1:28333'
- socket = self.ctx.socket(zmq.SUB)
- socket.set(zmq.RCVTIMEO, 60000)
- hashblock = ZMQSubscriber(socket, b'hashblock')
+
+ services = [b"hashblock", b"hashtx"]
+ sockets = []
+ subs = []
+ for service in services:
+ sockets.append(self.ctx.socket(zmq.SUB))
+ # 2 second timeout to check end of notifications
+ sockets[-1].set(zmq.RCVTIMEO, 2000)
+ subs.append(ZMQSubscriber(sockets[-1], service))
+
+ # Subscribe to all available topics.
+ hashblock = subs[0]
+ hashtx = subs[1]
# Should only notify the tip if a reorg occurs
- self.restart_node(0, ['-zmqpub%s=%s' % (hashblock.topic.decode(), address)])
- socket.connect(address)
+ self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx]])
+ for socket in sockets:
+ socket.connect(address)
# Relax so that the subscriber is ready before publishing zmq messages
sleep(0.2)
- # Generate 1 block in nodes[0] and receive all notifications
- self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ # Generate 1 block in nodes[0] with 1 mempool tx and receive all notifications
+ payment_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ disconnect_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ disconnect_cb = self.nodes[0].getblock(disconnect_block)["tx"][0]
assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex())
+ assert_equal(hashtx.receive().hex(), payment_txid)
+ assert_equal(hashtx.receive().hex(), disconnect_cb)
- # Generate 2 blocks in nodes[1]
- self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+ # Generate 2 blocks in nodes[1] to a different address to ensure split
+ connect_blocks = self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
# nodes[0] will reorg chain after connecting back nodes[1]
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
+ self.sync_blocks() # tx in mempool valid but not advertised
# Should receive nodes[1] tip
assert_equal(self.nodes[1].getbestblockhash(), hashblock.receive().hex())
+ # During reorg:
+ # Get old payment transaction notification from disconnect and disconnected cb
+ assert_equal(hashtx.receive().hex(), payment_txid)
+ assert_equal(hashtx.receive().hex(), disconnect_cb)
+ # And the payment transaction again due to mempool entry
+ assert_equal(hashtx.receive().hex(), payment_txid)
+ assert_equal(hashtx.receive().hex(), payment_txid)
+ # And the new connected coinbases
+ for i in [0, 1]:
+ assert_equal(hashtx.receive().hex(), self.nodes[1].getblock(connect_blocks[i])["tx"][0])
+
+ # If we do a simple invalidate we announce the disconnected coinbase
+ self.nodes[0].invalidateblock(connect_blocks[1])
+ assert_equal(hashtx.receive().hex(), self.nodes[1].getblock(connect_blocks[1])["tx"][0])
+ # And the current tip
+ assert_equal(hashtx.receive().hex(), self.nodes[1].getblock(connect_blocks[0])["tx"][0])
+
+ def test_sequence(self):
+ """
+ Sequence zmq notifications give every blockhash and txhash in order
+ of processing, regardless of IBD, re-orgs, etc.
+ Format of messages:
+ <32-byte hash>C : Blockhash connected
+ <32-byte hash>D : Blockhash disconnected
+ <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason
+ <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
+ """
+ self.log.info("Testing 'sequence' publisher")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Mempool sequence number starts at 1
+ seq_num = 1
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ dc_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+
+ # Note: We are not notified of any block transactions, coinbase or mined
+ assert_equal((self.nodes[0].getbestblockhash(), "C", None), seq.receive_sequence())
+
+ # Generate 2 blocks in nodes[1] to a different address to ensure a chain split
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ self.connect_nodes(0, 1)
+
+ # Then we receive all block (dis)connect notifications for the 2 block reorg
+ assert_equal((dc_block, "D", None), seq.receive_sequence())
+ block_count = self.nodes[1].getblockcount()
+ assert_equal((self.nodes[1].getblockhash(block_count-1), "C", None), seq.receive_sequence())
+ assert_equal((self.nodes[1].getblockhash(block_count), "C", None), seq.receive_sequence())
+
+ # Rest of test requires wallet functionality
+ if self.is_wallet_compiled():
+ self.log.info("Wait for tx from second node")
+ payment_txid = self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=5.0, replaceable=True)
+ self.sync_all()
+ self.log.info("Testing sequence notifications with mempool sequence values")
+
+ # Should receive the broadcasted txid.
+ assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ self.log.info("Testing RBF notification")
+ # Replace it to test eviction/addition notification
+ rbf_info = self.nodes[1].bumpfee(payment_txid)
+ self.sync_all()
+ assert_equal((payment_txid, "R", seq_num), seq.receive_sequence())
+ seq_num += 1
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Doesn't get published when mined, make a block and tx to "flush" the possibility
+ # though the mempool sequence number does go up by the number of transactions
+ # removed from the mempool by the block mining it.
+ mempool_size = len(self.nodes[0].getrawmempool())
+ c_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ self.sync_all()
+ # Make sure the number of mined transactions matches the number of txs out of mempool
+ mempool_size_delta = mempool_size - len(self.nodes[0].getrawmempool())
+ assert_equal(len(self.nodes[0].getblock(c_block)["tx"])-1, mempool_size_delta)
+ seq_num += mempool_size_delta
+ payment_txid_2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ self.sync_all()
+ assert_equal((c_block, "C", None), seq.receive_sequence())
+ assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Spot check getrawmempool results that they only show up when asked for
+ assert type(self.nodes[0].getrawmempool()) is list
+ assert type(self.nodes[0].getrawmempool(mempool_sequence=False)) is list
+ assert "mempool_sequence" not in self.nodes[0].getrawmempool(verbose=True)
+ assert_raises_rpc_error(-8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True)
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], seq_num)
+
+ self.log.info("Testing reorg notifications")
+ # Manually invalidate the last block to test mempool re-entry
+ # N.B. This part could be made more lenient in exact ordering
+ # since it greatly depends on inner-workings of blocks/mempool
+ # during "deep" re-orgs. Probably should "re-construct"
+ # blockchain/mempool state from notifications instead.
+ block_count = self.nodes[0].getblockcount()
+ best_hash = self.nodes[0].getbestblockhash()
+ self.nodes[0].invalidateblock(best_hash)
+ sleep(2) # Bit of room to make sure transaction things happened
+
+ # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
+ # of the time they were gathered.
+ assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
+
+ assert_equal((best_hash, "D", None), seq.receive_sequence())
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Other things may happen but aren't wallet-deterministic so we don't test for them currently
+ self.nodes[0].reconsiderblock(best_hash)
+ self.nodes[1].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all()
+
+ self.log.info("Evict mempool transaction by block conflict")
+ orig_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # More to be simply mined
+ more_tx = []
+ for _ in range(5):
+ more_tx.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.1))
+
+ raw_tx = self.nodes[0].getrawtransaction(orig_txid)
+ bump_info = self.nodes[0].bumpfee(orig_txid)
+ # Mine the pre-bump tx
+ block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1))
+ tx = FromHex(CTransaction(), raw_tx)
+ block.vtx.append(tx)
+ for txid in more_tx:
+ tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ block.vtx.append(tx)
+ add_witness_commitment(block)
+ block.solve()
+ assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None)
+ tip = self.nodes[0].getbestblockhash()
+ assert_equal(int(tip, 16), block.sha256)
+ orig_txid_2 = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # Flush old notifications until evicted tx original entry
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ while hash_str != orig_txid:
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ mempool_seq += 1
+
+ # Added original tx
+ assert_equal(label, "A")
+ # More transactions to be simply mined
+ for i in range(len(more_tx)):
+ assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Bumped by rbf
+ assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((bump_info["txid"], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Conflict announced first, then block
+ assert_equal((bump_info["txid"], "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((tip, "C", None), seq.receive_sequence())
+ mempool_seq += len(more_tx)
+ # Last tx
+ assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
+
+ def test_mempool_sync(self):
+ """
+ Use sequence notification plus getrawmempool sequence results to "sync mempool"
+ """
+ if not self.is_wallet_compiled():
+ self.log.info("Skipping mempool sync test")
+ return
+
+ self.log.info("Testing 'mempool sync' usage of sequence notifier")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ self.connect_nodes(0, 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # In-memory counter, should always start at 1
+ next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"]
+ assert_equal(next_mempool_seq, 1)
+
+ # Some transactions have been happening but we aren't consuming zmq notifications yet
+ # or we lost a ZMQ message somehow and want to start over
+ txids = []
+ num_txs = 5
+ for _ in range(num_txs):
+ txids.append(self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True))
+ self.sync_all()
+
+ # 1) Consume backlog until we get a mempool sequence number
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+ while zmq_mem_seq is None:
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+
+ assert label == "A" or label == "R"
+ assert hash_str is not None
+
+ # 2) We need to "seed" our view of the mempool
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+ assert_equal(get_raw_seq, 6)
+ # Snapshot may be too old compared to zmq message we read off latest
+ while zmq_mem_seq >= get_raw_seq:
+ sleep(2)
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+
+ # Things continue to happen in the "interim" while waiting for snapshot results
+ # We have node 0 do all these to avoid p2p races with RBF announcements
+ for _ in range(num_txs):
+ txids.append(self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True))
+ self.nodes[0].bumpfee(txids[-1])
+ self.sync_all()
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ final_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True)
+
+ # 3) Consume ZMQ backlog until we get to "now" for the mempool snapshot
+ while True:
+ if zmq_mem_seq == get_raw_seq - 1:
+ break
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ zmq_mem_seq = mempool_sequence
+ if zmq_mem_seq > get_raw_seq:
+ raise Exception("We somehow jumped mempool sequence numbers! zmq_mem_seq: {} > get_raw_seq: {}".format(zmq_mem_seq, get_raw_seq))
+
+ # 4) Moving forward, we apply the delta to our local view
+ # remaining txs(5) + 1 rbf(A+R) + 1 block connect + 1 final tx
+ expected_sequence = get_raw_seq
+ r_gap = 0
+ for _ in range(num_txs + 2 + 1 + 1):
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ if mempool_sequence != expected_sequence:
+ # Detected "R" gap, means this a conflict eviction, and mempool tx are being evicted before its
+ # position in the incoming block message "C"
+ if label == "R":
+ assert mempool_sequence > expected_sequence
+ r_gap += mempool_sequence - expected_sequence
+ else:
+ raise Exception("WARNING: txhash has unexpected mempool sequence value: {} vs expected {}".format(mempool_sequence, expected_sequence))
+ if label == "A":
+ assert hash_str not in mempool_view
+ mempool_view.add(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "R":
+ assert hash_str in mempool_view
+ mempool_view.remove(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "C":
+ # (Attempt to) remove all txids from known block connects
+ block_txids = self.nodes[0].getblock(hash_str)["tx"][1:]
+ for txid in block_txids:
+ if txid in mempool_view:
+ expected_sequence += 1
+ mempool_view.remove(txid)
+ expected_sequence -= r_gap
+ r_gap = 0
+ elif label == "D":
+ # Not useful for mempool tracking per se
+ continue
+ else:
+ raise Exception("Unexpected ZMQ sequence label!")
+
+ assert_equal(self.nodes[0].getrawmempool(), [final_txid])
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], expected_sequence)
+
+ # 5) If you miss a zmq/mempool sequence number, go back to step (2)
+
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+
+ def test_multiple_interfaces(self):
+ # Set up two subscribers with different addresses
+ subscribers = []
+ for i in range(2):
+ address = 'tcp://127.0.0.1:%d' % (28334 + i)
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ hashblock = ZMQSubscriber(socket, b"hashblock")
+ socket.connect(address)
+ subscribers.append({'address': address, 'hashblock': hashblock})
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (subscriber['hashblock'].topic.decode(), subscriber['address']) for subscriber in subscribers])
+
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+
+ # Should receive the same block hash on both subscribers
+ assert_equal(self.nodes[0].getbestblockhash(), subscribers[0]['hashblock'].receive().hex())
+ assert_equal(self.nodes[0].getbestblockhash(), subscribers[1]['hashblock'].receive().hex())
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 6df8f1c3ec..fc4c775668 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test mempool acceptance of raw transactions."""
+from decimal import Decimal
from io import BytesIO
import math
@@ -82,29 +83,31 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction not in the mempool')
- fee = 0.00000700
+ fee = Decimal('0.000007')
raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later
- outputs=[{node.getnewaddress(): 0.3 - fee}],
+ outputs=[{node.getnewaddress(): Decimal('0.3') - fee}],
))['hex']
tx = CTransaction()
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
txid_0 = tx.rehash()
self.check_mempool_result(
- result_expected=[{'txid': txid_0, 'allowed': True}],
+ result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee}}],
rawtxs=[raw_tx_0],
)
self.log.info('A final transaction not in the mempool')
coin = coins.pop() # Pick a random coin(base) to spend
+ output_amount = Decimal('0.025')
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
- outputs=[{node.getnewaddress(): 0.025}],
+ outputs=[{node.getnewaddress(): output_amount}],
locktime=node.getblockcount() + 2000, # Can be anything
))['hex']
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
+ fee_expected = coin['amount'] - output_amount
self.check_mempool_result(
- result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee_expected}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
@@ -127,7 +130,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
txid_0 = tx.rehash()
self.check_mempool_result(
- result_expected=[{'txid': txid_0, 'allowed': True}],
+ result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
rawtxs=[raw_tx_0],
)
@@ -187,7 +190,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
# Reference tx should be valid on itself
self.check_mempool_result(
- result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index 31fb751904..7168cb4ab2 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -8,7 +8,7 @@ NOTE: The test is designed to prevent cases when compatibility is broken acciden
In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
Download node binaries:
-contrib/devtools/previous_release.py -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
Only v0.15.2 is required by this test. The rest is used in other backwards compatibility tests.
"""
@@ -21,6 +21,7 @@ from test_framework.test_framework import BitcoinTestFramework
class MempoolCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ self.wallet_names = [None, self.default_wallet_name]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 98dac30ace..d7cb7db9f8 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -7,13 +7,12 @@
from decimal import Decimal
from test_framework.messages import COIN
-from test_framework.mininode import P2PTxInvStore
+from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
satoshi_round,
- wait_until,
)
# default limits
@@ -59,7 +58,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
- self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
+ peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
self.nodes[0].generate(101)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
@@ -81,7 +80,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
- self.nodes[0].p2p.wait_for_broadcast(witness_chain)
+ peer_inv_store.wait_for_broadcast(witness_chain)
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
# count and fees should look correct
@@ -269,8 +268,8 @@ class MempoolPackagesTest(BitcoinTestFramework):
# - txs from previous ancestor test (-> custom ancestor limit)
# - parent tx for descendant test
# - txs chained off parent tx (-> custom descendant limit)
- wait_until(lambda: len(self.nodes[1].getrawmempool(False)) ==
- MAX_ANCESTORS_CUSTOM + 1 + MAX_DESCENDANTS_CUSTOM, timeout=10)
+ self.wait_until(lambda: len(self.nodes[1].getrawmempool(False)) ==
+ MAX_ANCESTORS_CUSTOM + 1 + MAX_DESCENDANTS_CUSTOM, timeout=10)
mempool0 = self.nodes[0].getrawmempool(False)
mempool1 = self.nodes[1].getrawmempool(False)
assert set(mempool1).issubset(set(mempool0))
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 85c4d6d570..70cd4ebb3b 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -39,15 +39,12 @@ from decimal import Decimal
import os
import time
+from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.mininode import P2PTxInvStore
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_raises_rpc_error,
- connect_nodes,
- disconnect_nodes,
- wait_until,
)
@@ -84,11 +81,11 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
# disconnect nodes & make a txn that remains in the unbroadcast set.
- disconnect_nodes(self.nodes[0], 1)
+ self.disconnect_nodes(0, 1)
assert(len(self.nodes[0].getpeerinfo()) == 0)
assert(len(self.nodes[0].p2ps) == 0)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
@@ -172,7 +169,7 @@ class MempoolPersistTest(BitcoinTestFramework):
# check that txn gets broadcast due to unbroadcast logic
conn = node0.add_p2p_connection(P2PTxInvStore())
node0.mockscheduler(16*60) # 15 min + 1 for buffer
- wait_until(lambda: len(conn.get_invs()) == 1)
+ self.wait_until(lambda: len(conn.get_invs()) == 1)
if __name__ == '__main__':
MempoolPersistTest().main()
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index 365d011157..b475b65e68 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -7,13 +7,11 @@ to peers until a GETDATA is received."""
import time
-from test_framework.mininode import P2PTxInvStore
+from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
create_confirmed_utxos,
- disconnect_nodes,
)
MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
@@ -36,7 +34,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
min_relay_fee = node.getnetworkinfo()["relayfee"]
utxos = create_confirmed_utxos(min_relay_fee, node, 10)
- disconnect_nodes(node, 1)
+ self.disconnect_nodes(0, 1)
self.log.info("Generate transactions that only node 0 knows about")
@@ -70,7 +68,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.restart_node(0)
self.log.info("Reconnect nodes & check if they are sent to node 1")
- connect_nodes(node, 1)
+ self.connect_nodes(0, 1)
# fast forward into the future & ensure that the second node has the txns
node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
@@ -91,7 +89,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
time.sleep(2) # allow sufficient time for possibility of broadcast
assert_equal(len(conn.get_invs()), 0)
- disconnect_nodes(node, 1)
+ self.disconnect_nodes(0, 1)
node.disconnect_p2ps()
def test_txn_removal(self):
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index 63d1ccfb36..80635e33c5 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -20,12 +20,11 @@ from test_framework.messages import (
CBlockHeader,
BLOCK_HEADER_SIZE,
)
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
@@ -56,7 +55,7 @@ class MiningTest(BitcoinTestFramework):
assert_equal(mining_info['currentblocktx'], 0)
assert_equal(mining_info['currentblockweight'], 4000)
self.restart_node(0)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
def run_test(self):
self.mine_chain()
@@ -234,9 +233,9 @@ class MiningTest(BitcoinTestFramework):
assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
# Should ask for the block from a p2p node, if they announce the header as well:
- node.add_p2p_connection(P2PDataStore())
- node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders
- node.p2p.send_blocks_and_test(blocks=[block], node=node)
+ peer = node.add_p2p_connection(P2PDataStore())
+ peer.wait_for_getheaders(timeout=5) # Drop the first getheaders
+ peer.send_blocks_and_test(blocks=[block], node=node)
# Must be active now:
assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index 6d0b241e57..2adafb1fdb 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -5,11 +5,13 @@
"""Test longpolling with getblocktemplate."""
from decimal import Decimal
+import random
+import threading
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import get_rpc_proxy, random_transaction
+from test_framework.util import get_rpc_proxy
+from test_framework.wallet import MiniWallet
-import threading
class LongpollThread(threading.Thread):
def __init__(self, node):
@@ -29,45 +31,48 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
self.num_nodes = 2
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.")
+ self.log.info("Test that longpollid doesn't change between successive getblocktemplate() invocations if nothing else happens")
self.nodes[0].generate(10)
template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
longpollid = template['longpollid']
- # longpollid should not change between successive invocations if nothing else happens
template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']})
assert template2['longpollid'] == longpollid
- # Test 1: test that the longpolling wait if we do nothing
+ self.log.info("Test that longpoll waits if we do nothing")
thr = LongpollThread(self.nodes[0])
thr.start()
# check that thread still lives
thr.join(5) # wait 5 seconds or until thread exits
assert thr.is_alive()
- # Test 2: test that longpoll will terminate if another node generates a block
- self.nodes[1].generate(1) # generate a block on another node
+ miniwallets = [ MiniWallet(node) for node in self.nodes ]
+ self.log.info("Test that longpoll will terminate if another node generates a block")
+ miniwallets[1].generate(1) # generate a block on another node
# check that thread will exit now that new transaction entered mempool
thr.join(5) # wait 5 seconds or until thread exits
assert not thr.is_alive()
- # Test 3: test that longpoll will terminate if we generate a block ourselves
+ self.log.info("Test that longpoll will terminate if we generate a block ourselves")
thr = LongpollThread(self.nodes[0])
thr.start()
- self.nodes[0].generate(1) # generate a block on another node
+ miniwallets[0].generate(1) # generate a block on own node
thr.join(5) # wait 5 seconds or until thread exits
assert not thr.is_alive()
- # Test 4: test that introducing a new transaction into the mempool will terminate the longpoll
+ # Add enough mature utxos to the wallets, so that all txs spend confirmed coins
+ self.nodes[0].generate(100)
+ self.sync_blocks()
+
+ self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll")
thr = LongpollThread(self.nodes[0])
thr.start()
# generate a random transaction and submit it
min_relay_fee = self.nodes[0].getnetworkinfo()["relayfee"]
- # min_relay_fee is fee per 1000 bytes, which should be more than enough.
- (txid, txhex, fee) = random_transaction(self.nodes, Decimal("1.1"), min_relay_fee, Decimal("0.001"), 20)
+ fee_rate = min_relay_fee + Decimal('0.00000010') * random.randint(0,20)
+ miniwallets[0].send_self_transfer(from_node=random.choice(self.nodes),
+ fee_rate=fee_rate)
# after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned
thr.join(60 + 20)
assert not thr.is_alive()
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 5c7e27a3a8..80f262d0d3 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -12,9 +12,7 @@ from test_framework.messages import (
NODE_WITNESS,
msg_addr,
)
-from test_framework.mininode import (
- P2PInterface,
-)
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
new file mode 100755
index 0000000000..23ce3e5d04
--- /dev/null
+++ b/test/functional/p2p_addrv2_relay.py
@@ -0,0 +1,79 @@
+#!/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 addrv2 relay
+"""
+
+import time
+
+from test_framework.messages import (
+ CAddress,
+ msg_addrv2,
+ NODE_NETWORK,
+ NODE_WITNESS,
+)
+from test_framework.p2p import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+ADDRS = []
+for i in range(10):
+ 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):
+ addrv2_received_and_checked = False
+
+ def __init__(self):
+ super().__init__(support_addrv2 = True)
+
+ def on_addrv2(self, message):
+ for addr in message.addrs:
+ assert_equal(addr.nServices, 9)
+ assert addr.ip.startswith('123.123.123.')
+ assert (8333 <= addr.port < 8343)
+ self.addrv2_received_and_checked = True
+
+ def wait_for_addrv2(self):
+ self.wait_until(lambda: "addrv2" in self.last_message)
+
+
+class AddrTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.log.info('Create connection that sends addrv2 messages')
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+ msg = msg_addrv2()
+
+ self.log.info('Send too-large addrv2 message')
+ msg.addrs = ADDRS * 101
+ with self.nodes[0].assert_debug_log(['addrv2 message size = 1010']):
+ addr_source.send_and_ping(msg)
+
+ self.log.info('Check that addrv2 message content is relayed and added to addrman')
+ addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
+ msg.addrs = ADDRS
+ with self.nodes[0].assert_debug_log([
+ 'Added 10 addresses from 127.0.0.1: 0 tried',
+ 'received: addrv2 (131 bytes) peer=0',
+ 'sending addrv2 (131 bytes) peer=1',
+ ]):
+ addr_source.send_and_ping(msg)
+ self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
+ addr_receiver.wait_for_addrv2()
+
+ assert addr_receiver.addrv2_received_and_checked
+
+
+if __name__ == '__main__':
+ AddrTest().main()
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index 6d947ac660..3250cbecf9 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -4,12 +4,13 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests NODE_COMPACT_FILTERS (BIP 157/158).
-Tests that a node configured with -blockfilterindex and -peerblockfilters can serve
-cfheaders and cfcheckpts.
+Tests that a node configured with -blockfilterindex and -peerblockfilters signals
+NODE_COMPACT_FILTERS and can serve cfilters, cfheaders and cfcheckpts.
"""
from test_framework.messages import (
FILTER_TYPE_BASIC,
+ NODE_COMPACT_FILTERS,
hash256,
msg_getcfcheckpt,
msg_getcfheaders,
@@ -17,13 +18,10 @@ from test_framework.messages import (
ser_uint256,
uint256_from_str,
)
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- disconnect_nodes,
- wait_until,
)
class CFiltersClient(P2PInterface):
@@ -61,14 +59,22 @@ class CompactFiltersTest(BitcoinTestFramework):
self.sync_blocks(timeout=600)
# Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
- disconnect_nodes(self.nodes[0], 1)
+ self.disconnect_nodes(0, 1)
self.nodes[0].generate(1)
- wait_until(lambda: self.nodes[0].getblockcount() == 1000)
+ self.wait_until(lambda: self.nodes[0].getblockcount() == 1000)
stale_block_hash = self.nodes[0].getblockhash(1000)
self.nodes[1].generate(1001)
- wait_until(lambda: self.nodes[1].getblockcount() == 2000)
+ self.wait_until(lambda: self.nodes[1].getblockcount() == 2000)
+
+ # Check that nodes have signalled NODE_COMPACT_FILTERS correctly.
+ assert node0.nServices & NODE_COMPACT_FILTERS != 0
+ assert node1.nServices & NODE_COMPACT_FILTERS == 0
+
+ # Check that the localservices is as expected.
+ assert int(self.nodes[0].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS != 0
+ assert int(self.nodes[1].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS == 0
self.log.info("get cfcheckpt on chain to be re-orged out.")
request = msg_getcfcheckpt(
@@ -82,7 +88,7 @@ class CompactFiltersTest(BitcoinTestFramework):
assert_equal(len(response.headers), 1)
self.log.info("Reorg node 0 to a new chain.")
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks(timeout=600)
main_block_hash = self.nodes[0].getblockhash(1000)
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 27e6b669f6..e80422d1cf 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -5,7 +5,7 @@
"""Test p2p blocksonly"""
from test_framework.messages import msg_tx, CTransaction, FromHex
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -17,7 +17,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.extra_args = [["-blocksonly"]]
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ block_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
@@ -41,45 +41,49 @@ class P2PBlocksOnly(BitcoinTestFramework):
)['hex']
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
- self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.wait_for_disconnect()
+ block_relay_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ block_relay_peer.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
# Remove the disconnected peer and add a new one.
del self.nodes[0].p2ps[0]
- self.nodes[0].add_p2p_connection(P2PInterface())
+ tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
- self.nodes[0].p2p.wait_for_tx(txid)
+ tx_relay_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
- self.log.info('Check that txs from forcerelay peers are not rejected and relayed to others')
- self.log.info("Restarting node 0 with forcerelay permission and blocksonly")
- self.restart_node(0, ["-persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
+ self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others')
+ self.log.info("Restarting node 0 with relay permission and blocksonly")
+ self.restart_node(0, ["-persistmempool=0", "-whitelist=relay@127.0.0.1", "-blocksonly", '-deprecatedrpc=whitelisted'])
assert_equal(self.nodes[0].getrawmempool(), [])
first_peer = self.nodes[0].add_p2p_connection(P2PInterface())
second_peer = self.nodes[0].add_p2p_connection(P2PInterface())
peer_1_info = self.nodes[0].getpeerinfo()[0]
- assert_equal(peer_1_info['whitelisted'], True)
- assert_equal(peer_1_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool', 'download'])
+ assert_equal(peer_1_info['permissions'], ['relay'])
peer_2_info = self.nodes[0].getpeerinfo()[1]
- assert_equal(peer_2_info['whitelisted'], True)
- assert_equal(peer_2_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool', 'download'])
+ assert_equal(peer_2_info['permissions'], ['relay'])
assert_equal(self.nodes[0].testmempoolaccept([sigtx])[0]['allowed'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
- self.log.info('Check that the tx from forcerelay first_peer is relayed to others (ie.second_peer)')
+ self.log.info('Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer)')
with self.nodes[0].assert_debug_log(["received getdata"]):
+ # Note that normally, first_peer would never send us transactions since we're a blocksonly node.
+ # By activating blocksonly, we explicitly tell our peers that they should not send us transactions,
+ # and Bitcoin Core respects that choice and will not send transactions.
+ # But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to
+ # second_peer since we gave relay permission to first_peer.
+ # See https://github.com/bitcoin/bitcoin/issues/19943 for details.
first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.log.info('Check that the forcerelay peer is still connected after sending the transaction')
+ self.log.info('Check that the peer with relay-permission is still connected after sending the transaction')
assert_equal(first_peer.is_connected, True)
second_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
- self.log.info("Forcerelay peer's transaction is accepted and relayed")
+ self.log.info("Relay-permission peer's transaction is accepted and relayed")
if __name__ == '__main__':
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 225d393e1b..611bffb25f 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -9,12 +9,12 @@ Version 2 compact blocks are post-segwit (wtxids)
"""
import random
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
+from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS, add_witness_commitment
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.mininode import mininode_lock, P2PInterface
+from test_framework.p2p import p2p_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, wait_until, softfork_active
+from test_framework.util import assert_equal, softfork_active
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
@@ -48,12 +48,12 @@ class TestP2PConn(P2PInterface):
self.block_announced = True
self.announced_blockhashes.add(x.hash)
- # Requires caller to hold mininode_lock
+ # Requires caller to hold p2p_lock
def received_block_announcement(self):
return self.block_announced
def clear_block_announcement(self):
- with mininode_lock:
+ with p2p_lock:
self.block_announced = False
self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
@@ -73,7 +73,7 @@ class TestP2PConn(P2PInterface):
def request_headers_and_sync(self, locator, hashstop=0):
self.clear_block_announcement()
self.get_headers(locator, hashstop)
- wait_until(self.received_block_announcement, timeout=30, lock=mininode_lock)
+ self.wait_until(self.received_block_announcement, timeout=30)
self.clear_block_announcement()
# Block until a block announcement for a particular block hash is
@@ -81,7 +81,7 @@ class TestP2PConn(P2PInterface):
def wait_for_block_announcement(self, block_hash, timeout=30):
def received_hash():
return (block_hash in self.announced_blockhashes)
- wait_until(received_hash, timeout=timeout, lock=mininode_lock)
+ self.wait_until(received_hash, timeout=timeout)
def send_await_disconnect(self, message, timeout=30):
"""Sends a message to the node and wait for disconnect.
@@ -89,7 +89,7 @@ class TestP2PConn(P2PInterface):
This is used when we want to send a message into the node that we expect
will get us disconnected, eg an invalid block."""
self.send_message(message)
- wait_until(lambda: not self.is_connected, timeout=timeout, lock=mininode_lock)
+ self.wait_for_disconnect(timeout)
class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
@@ -104,11 +104,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def build_block_on_tip(self, node, segwit=False):
- height = node.getblockcount()
- tip = node.getbestblockhash()
- mtp = node.getblockheader(tip)['mediantime']
- block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1)
- block.nVersion = 4
+ block = create_block(tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
if segwit:
add_witness_commitment(block)
block.solve()
@@ -154,8 +150,8 @@ class CompactBlocksTest(BitcoinTestFramework):
# Make sure we get a SENDCMPCT message from our peer
def received_sendcmpct():
return (len(test_node.last_sendcmpct) > 0)
- wait_until(received_sendcmpct, timeout=30, lock=mininode_lock)
- with mininode_lock:
+ test_node.wait_until(received_sendcmpct, timeout=30)
+ with p2p_lock:
# Check that the first version received is the preferred one
assert_equal(test_node.last_sendcmpct[0].version, preferred_version)
# And that we receive versions down to 1.
@@ -170,7 +166,7 @@ class CompactBlocksTest(BitcoinTestFramework):
peer.wait_for_block_announcement(block_hash, timeout=30)
assert peer.block_announced
- with mininode_lock:
+ with p2p_lock:
assert predicate(peer), (
"block_hash={!r}, cmpctblock={!r}, inv={!r}".format(
block_hash, peer.last_message.get("cmpctblock", None), peer.last_message.get("inv", None)))
@@ -188,28 +184,21 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with too-high version
- sendcmpct = msg_sendcmpct()
- sendcmpct.version = preferred_version + 1
- sendcmpct.announce = True
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version+1))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with valid version, but announce=False
- sendcmpct.version = preferred_version
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Finally, try a SENDCMPCT message with announce=True
- sendcmpct.version = preferred_version
- sendcmpct.announce = True
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time (no headers sync should be needed!)
@@ -220,23 +209,17 @@ class CompactBlocksTest(BitcoinTestFramework):
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after sending a version-1, announce=false message.
- sendcmpct.version = preferred_version - 1
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version-1))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Now turn off announcements
- sendcmpct.version = preferred_version
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
if old_node is not None:
# Verify that a peer using an older protocol version can receive
# announcements from this node.
- sendcmpct.version = preferred_version - 1
- sendcmpct.announce = True
- old_node.send_and_ping(sendcmpct)
+ old_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version-1))
# Header sync
old_node.request_headers_and_sync(locator=[tip])
check_announcement_of_new_block(node, old_node, lambda p: "cmpctblock" in p.last_message)
@@ -294,11 +277,11 @@ class CompactBlocksTest(BitcoinTestFramework):
block.rehash()
# Wait until the block was announced (via compact blocks)
- wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30, lock=mininode_lock)
+ test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
# Now fetch and check the compact block
header_and_shortids = None
- with mininode_lock:
+ with p2p_lock:
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
@@ -308,11 +291,11 @@ class CompactBlocksTest(BitcoinTestFramework):
inv = CInv(MSG_CMPCT_BLOCK, block_hash)
test_node.send_message(msg_getdata([inv]))
- wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30, lock=mininode_lock)
+ test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
# Now fetch and check the compact block
header_and_shortids = None
- with mininode_lock:
+ with p2p_lock:
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
@@ -378,7 +361,7 @@ class CompactBlocksTest(BitcoinTestFramework):
if announce == "inv":
test_node.send_message(msg_inv([CInv(MSG_BLOCK, block.sha256)]))
- wait_until(lambda: "getheaders" in test_node.last_message, timeout=30, lock=mininode_lock)
+ test_node.wait_until(lambda: "getheaders" in test_node.last_message, timeout=30)
test_node.send_header_for_blocks([block])
else:
test_node.send_header_for_blocks([block])
@@ -397,7 +380,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
# Expect a getblocktxn message.
- with mininode_lock:
+ with p2p_lock:
assert "getblocktxn" in test_node.last_message
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [0]) # should be a coinbase request
@@ -439,7 +422,7 @@ class CompactBlocksTest(BitcoinTestFramework):
def test_getblocktxn_response(compact_block, peer, expected_result):
msg = msg_cmpctblock(compact_block.to_p2p())
peer.send_and_ping(msg)
- with mininode_lock:
+ with p2p_lock:
assert "getblocktxn" in peer.last_message
absolute_indexes = peer.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, expected_result)
@@ -504,13 +487,13 @@ class CompactBlocksTest(BitcoinTestFramework):
assert tx.hash in mempool
# Clear out last request.
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("getblocktxn", None)
# Send compact block
comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness)
test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
- with mininode_lock:
+ with p2p_lock:
# Shouldn't have gotten a request for any transaction
assert "getblocktxn" not in test_node.last_message
@@ -537,7 +520,7 @@ class CompactBlocksTest(BitcoinTestFramework):
comp_block.initialize_from_block(block, prefill_list=[0], use_witness=(version == 2))
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = []
- with mininode_lock:
+ with p2p_lock:
assert "getblocktxn" in test_node.last_message
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
@@ -588,10 +571,10 @@ class CompactBlocksTest(BitcoinTestFramework):
num_to_request = random.randint(1, len(block.vtx))
msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
test_node.send_message(msg)
- wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10, lock=mininode_lock)
+ test_node.wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10)
[tx.calc_sha256() for tx in block.vtx]
- with mininode_lock:
+ with p2p_lock:
assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int(block_hash, 16))
all_indices = msg.block_txn_request.to_absolute()
for index in all_indices:
@@ -611,11 +594,11 @@ class CompactBlocksTest(BitcoinTestFramework):
# allowed depth for a blocktxn response.
block_hash = node.getblockhash(current_height)
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("block", None)
test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg)
- with mininode_lock:
+ with p2p_lock:
test_node.last_message["block"].block.calc_sha256()
assert_equal(test_node.last_message["block"].block.sha256, int(block_hash, 16))
assert "blocktxn" not in test_node.last_message
@@ -628,21 +611,21 @@ class CompactBlocksTest(BitcoinTestFramework):
for _ in range(MAX_CMPCTBLOCK_DEPTH + 1):
test_node.clear_block_announcement()
new_blocks.append(node.generate(1)[0])
- wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock)
+ test_node.wait_until(test_node.received_block_announcement, timeout=30)
test_node.clear_block_announcement()
test_node.send_message(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))
- wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30, lock=mininode_lock)
+ test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
test_node.clear_block_announcement()
node.generate(1)
- wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock)
+ test_node.wait_until(test_node.received_block_announcement, timeout=30)
test_node.clear_block_announcement()
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("block", None)
test_node.send_message(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))
- wait_until(lambda: "block" in test_node.last_message, timeout=30, lock=mininode_lock)
- with mininode_lock:
+ test_node.wait_until(lambda: "block" in test_node.last_message, timeout=30)
+ with p2p_lock:
test_node.last_message["block"].block.calc_sha256()
assert_equal(test_node.last_message["block"].block.sha256, int(new_blocks[0], 16))
@@ -670,10 +653,10 @@ class CompactBlocksTest(BitcoinTestFramework):
# (to avoid fingerprinting attacks).
msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("blocktxn", None)
test_node.send_and_ping(msg)
- with mininode_lock:
+ with p2p_lock:
assert "blocktxn" not in test_node.last_message
def test_end_to_end_block_relay(self, listeners):
@@ -689,8 +672,8 @@ class CompactBlocksTest(BitcoinTestFramework):
node.submitblock(ToHex(block))
for l in listeners:
- wait_until(lambda: "cmpctblock" in l.last_message, timeout=30, lock=mininode_lock)
- with mininode_lock:
+ l.wait_until(lambda: "cmpctblock" in l.last_message, timeout=30)
+ with p2p_lock:
for l in listeners:
l.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
assert_equal(l.last_message["cmpctblock"].header_and_shortids.header.sha256, block.sha256)
@@ -729,11 +712,7 @@ class CompactBlocksTest(BitcoinTestFramework):
node = self.nodes[0]
tip = node.getbestblockhash()
peer.get_headers(locator=[int(tip, 16)], hashstop=0)
-
- msg = msg_sendcmpct()
- msg.version = peer.cmpct_version
- msg.announce = True
- peer.send_and_ping(msg)
+ peer.send_and_ping(msg_sendcmpct(announce=True, version=peer.cmpct_version))
def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer):
node = self.nodes[0]
@@ -747,7 +726,7 @@ class CompactBlocksTest(BitcoinTestFramework):
cmpct_block.initialize_from_block(block)
msg = msg_cmpctblock(cmpct_block.to_p2p())
peer.send_and_ping(msg)
- with mininode_lock:
+ with p2p_lock:
assert "getblocktxn" in peer.last_message
return block, cmpct_block
@@ -786,6 +765,9 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def run_test(self):
+ # Get the nodes out of IBD
+ self.nodes[0].generate(1)
+
# Setup the p2p connections
self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1), services=NODE_NETWORK)
diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py
index 09b9ebeb2d..3088a8aa46 100755
--- a/test/functional/p2p_disconnect_ban.py
+++ b/test/functional/p2p_disconnect_ban.py
@@ -9,8 +9,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
- wait_until,
)
class DisconnectBanTest(BitcoinTestFramework):
@@ -20,15 +18,15 @@ class DisconnectBanTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Connect nodes both way")
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 0)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 0)
self.log.info("Test setban and listbanned RPCs")
self.log.info("setban: successfully ban single IP address")
assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point
self.nodes[1].setban(subnet="127.0.0.1", command="add")
- wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10)
+ self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10)
assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point
assert_equal(len(self.nodes[1].listbanned()), 1)
@@ -79,8 +77,8 @@ class DisconnectBanTest(BitcoinTestFramework):
# Clear ban lists
self.nodes[1].clearbanned()
self.log.info("Connect nodes both way")
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 0)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 0)
self.log.info("Test disconnectnode RPCs")
@@ -95,18 +93,18 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: successfully disconnect node by address")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
self.nodes[0].disconnectnode(address=address1)
- wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
self.log.info("disconnectnode: successfully reconnect node")
- connect_nodes(self.nodes[0], 1) # reconnect the node
+ self.connect_nodes(0, 1) # reconnect the node
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
self.log.info("disconnectnode: successfully disconnect node by node id")
id1 = self.nodes[0].getpeerinfo()[0]['id']
self.nodes[0].disconnectnode(nodeid=id1)
- wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
+ self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10)
assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1]
if __name__ == '__main__':
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index f8552cf53d..2349afa1ee 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -8,7 +8,7 @@ from test_framework.messages import (
CBlockHeader,
FromHex,
)
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PInterface,
msg_headers,
)
@@ -46,8 +46,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers))
+ peer_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_checkpoint.send_and_ping(msg_headers(self.headers))
assert {
'height': 546,
'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70',
@@ -57,14 +57,14 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.log.info("Feed all fork headers (fails due to checkpoint)")
with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint']):
- self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
- self.nodes[0].p2p.wait_for_disconnect()
+ peer_checkpoint.send_message(msg_headers(self.headers_fork))
+ peer_checkpoint.wait_for_disconnect()
self.log.info("Feed all fork headers (succeeds without checkpoint)")
# On node 0 it succeeds because checkpoints are disabled
self.restart_node(0, extra_args=['-nocheckpoints'])
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_no_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_no_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
@@ -73,8 +73,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
} in self.nodes[0].getchaintips()
# On node 1 it succeeds because no checkpoint has been reached yet by a chain tip
- self.nodes[1].add_p2p_connection(P2PInterface())
- self.nodes[1].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_before_checkpoint = self.nodes[1].add_p2p_connection(P2PInterface())
+ peer_before_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py
index b2b3a89aab..72a255991c 100755
--- a/test/functional/p2p_eviction.py
+++ b/test/functional/p2p_eviction.py
@@ -15,11 +15,11 @@ Therefore, this test is limited to the remaining protection criteria.
import time
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.mininode import P2PInterface, P2PDataStore
-from test_framework.util import assert_equal, wait_until
from test_framework.blocktools import 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
+from test_framework.util import assert_equal
class SlowP2PDataStore(P2PDataStore):
@@ -92,7 +92,7 @@ class P2PEvict(BitcoinTestFramework):
for _ in range(8):
fastpeer = node.add_p2p_connection(P2PInterface())
current_peer += 1
- wait_until(lambda: "ping" in fastpeer.last_message, timeout=10)
+ self.wait_until(lambda: "ping" in fastpeer.last_message, timeout=10)
# Make sure by asking the node what the actual min pings are
peerinfo = node.getpeerinfo()
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 0b51d8f4bb..51b7623fe3 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -5,26 +5,12 @@
"""Test processing of feefilter messages."""
from decimal import Decimal
-import time
from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
-from test_framework.mininode import mininode_lock, P2PInterface
+from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
-
-
-def hashToHex(hash):
- return format(hash, '064x')
-
-
-# Wait up to 60 secs to see if the testnode has received all the expected invs
-def allInvsMatch(invsExpected, testnode):
- for _ in range(60):
- with mininode_lock:
- if (sorted(invsExpected) == sorted(testnode.txinvs)):
- return True
- time.sleep(1)
- return False
+from test_framework.wallet import MiniWallet
class FeefilterConn(P2PInterface):
@@ -34,7 +20,7 @@ class FeefilterConn(P2PInterface):
self.feefilter_received = True
def assert_feefilter_received(self, recv: bool):
- with mininode_lock:
+ with p2p_lock:
assert_equal(self.feefilter_received, recv)
@@ -46,10 +32,14 @@ class TestP2PConn(P2PInterface):
def on_inv(self, message):
for i in message.inv:
if (i.type == MSG_TX) or (i.type == MSG_WTX):
- self.txinvs.append(hashToHex(i.hash))
+ self.txinvs.append('{:064x}'.format(i.hash))
+
+ def wait_for_invs_to_match(self, invs_expected):
+ invs_expected.sort()
+ self.wait_until(lambda: invs_expected == sorted(self.txinvs))
def clear_invs(self):
- with mininode_lock:
+ with p2p_lock:
self.txinvs = []
@@ -61,10 +51,12 @@ class FeeFilterTest(BitcoinTestFramework):
# mempool and wallet feerate calculation based on GetFee
# rounding down 3 places, leading to stranded transactions.
# See issue #16499
- self.extra_args = [["-minrelaytxfee=0.00000100", "-mintxfee=0.00000100"]] * self.num_nodes
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ # grant noban permission to all peers to speed up tx relay / mempool sync
+ self.extra_args = [[
+ "-minrelaytxfee=0.00000100",
+ "-mintxfee=0.00000100",
+ "-whitelist=noban@127.0.0.1",
+ ]] * self.num_nodes
def run_test(self):
self.test_feefilter_forcerelay()
@@ -85,29 +77,28 @@ class FeeFilterTest(BitcoinTestFramework):
def test_feefilter(self):
node1 = self.nodes[1]
node0 = self.nodes[0]
+ miniwallet = MiniWallet(node1)
+ # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
+ miniwallet.generate(5)
+ node1.generate(100)
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
- # Test that invs are received by test connection for all txs at
- # feerate of .2 sat/byte
- node1.settxfee(Decimal("0.00000200"))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
- assert allInvsMatch(txids, conn)
+ self.log.info("Test txs paying 0.2 sat/byte are received by test connection")
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000200'), from_node=node1)['wtxid'] for _ in range(3)]
+ conn.wait_for_invs_to_match(txids)
conn.clear_invs()
- # Set a filter of .15 sat/byte on test connection
+ # Set a fee filter of 0.15 sat/byte on test connection
conn.send_and_ping(msg_feefilter(150))
- # Test that txs are still being received by test connection (paying .15 sat/byte)
- node1.settxfee(Decimal("0.00000150"))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
- assert allInvsMatch(txids, conn)
+ self.log.info("Test txs paying 0.15 sat/byte are received by test connection")
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000150'), from_node=node1)['wtxid'] for _ in range(3)]
+ conn.wait_for_invs_to_match(txids)
conn.clear_invs()
- # Change tx fee rate to .1 sat/byte and test they are no longer received
- # by the test connection
- node1.settxfee(Decimal("0.00000100"))
- [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
+ self.log.info("Test txs paying 0.1 sat/byte are no longer received by test connection")
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000100'), from_node=node1)['wtxid'] for _ in range(3)]
self.sync_mempools() # must be sure node 0 has received all txs
# Send one transaction from node0 that should be received, so that we
@@ -117,15 +108,15 @@ class FeeFilterTest(BitcoinTestFramework):
# to 35 entries in an inv, which means that when this next transaction
# is eligible for relay, the prior transactions from node1 are eligible
# as well.
- node0.settxfee(Decimal("0.00020000"))
- txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
- assert allInvsMatch(txids, conn)
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node0)['wtxid'] for _ in range(1)]
+ conn.wait_for_invs_to_match(txids)
conn.clear_invs()
+ self.sync_mempools() # must be sure node 1 has received all txs
- # Remove fee filter and check that txs are received again
+ self.log.info("Remove fee filter and check txs are received again")
conn.send_and_ping(msg_feefilter(0))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
- assert allInvsMatch(txids, conn)
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node1)['wtxid'] for _ in range(3)]
+ conn.wait_for_invs_to_match(txids)
conn.clear_invs()
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index ce3856fc95..642a217047 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -19,7 +19,7 @@ from test_framework.messages import (
msg_mempool,
msg_version,
)
-from test_framework.mininode import P2PInterface, mininode_lock
+from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
from test_framework.test_framework import BitcoinTestFramework
@@ -60,22 +60,22 @@ class P2PBloomFilter(P2PInterface):
@property
def tx_received(self):
- with mininode_lock:
+ with p2p_lock:
return self._tx_received
@tx_received.setter
def tx_received(self, value):
- with mininode_lock:
+ with p2p_lock:
self._tx_received = value
@property
def merkleblock_received(self):
- with mininode_lock:
+ with p2p_lock:
return self._merkleblock_received
@merkleblock_received.setter
def merkleblock_received(self, value):
- with mininode_lock:
+ with p2p_lock:
self._merkleblock_received = value
@@ -131,7 +131,7 @@ class FilterTest(BitcoinTestFramework):
self.log.debug("Send a mempool msg after connecting and check that the tx is received")
self.nodes[0].add_p2p_connection(filter_peer)
filter_peer.send_and_ping(filter_peer.watch_filter_init)
- self.nodes[0].p2p.send_message(msg_mempool())
+ filter_peer.send_message(msg_mempool())
filter_peer.wait_for_tx(txid)
def test_frelay_false(self, filter_peer):
diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py
index d743abe681..aaf862e6c8 100755
--- a/test/functional/p2p_fingerprint.py
+++ b/test/functional/p2p_fingerprint.py
@@ -12,7 +12,7 @@ import time
from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.messages import CInv, MSG_BLOCK
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PInterface,
msg_headers,
msg_block,
@@ -22,9 +22,9 @@ from test_framework.mininode import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
)
+
class P2PFingerprintTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -102,12 +102,12 @@ class P2PFingerprintTest(BitcoinTestFramework):
# Check that getdata request for stale block succeeds
self.send_block_request(stale_hash, node0)
test_function = lambda: self.last_block_equals(stale_hash, node0)
- wait_until(test_function, timeout=3)
+ self.wait_until(test_function, timeout=3)
# Check that getheader request for stale block header succeeds
self.send_header_request(stale_hash, node0)
test_function = lambda: self.last_header_equals(stale_hash, node0)
- wait_until(test_function, timeout=3)
+ self.wait_until(test_function, timeout=3)
# Longest chain is extended so stale is much older than chain tip
self.nodes[0].setmocktime(0)
@@ -138,11 +138,11 @@ class P2PFingerprintTest(BitcoinTestFramework):
self.send_block_request(block_hash, node0)
test_function = lambda: self.last_block_equals(block_hash, node0)
- wait_until(test_function, timeout=3)
+ self.wait_until(test_function, timeout=3)
self.send_header_request(block_hash, node0)
test_function = lambda: self.last_header_equals(block_hash, node0)
- wait_until(test_function, timeout=3)
+ self.wait_until(test_function, timeout=3)
if __name__ == '__main__':
P2PFingerprintTest().main()
diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py
index c9278eab92..2b75ad5175 100755
--- a/test/functional/p2p_getaddr_caching.py
+++ b/test/functional/p2p_getaddr_caching.py
@@ -5,37 +5,20 @@
"""Test addr response caching"""
import time
-from test_framework.messages import (
- CAddress,
- NODE_NETWORK,
- NODE_WITNESS,
- msg_addr,
- msg_getaddr,
-)
-from test_framework.mininode import (
+
+from test_framework.messages import msg_getaddr
+from test_framework.p2p import (
P2PInterface,
- mininode_lock
+ p2p_lock
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
+# As defined in net_processing.
MAX_ADDR_TO_SEND = 1000
-
-def gen_addrs(n):
- addrs = []
- for i in range(n):
- addr = CAddress()
- addr.time = int(time.time())
- addr.nServices = NODE_NETWORK | NODE_WITNESS
- # Use first octets to occupy different AddrMan buckets
- first_octet = i >> 8
- second_octet = i % 256
- addr.ip = "{}.{}.1.1".format(first_octet, second_octet)
- addr.port = 8333
- addrs.append(addr)
- return addrs
+MAX_PCT_ADDR_TO_SEND = 23
class AddrReceiver(P2PInterface):
@@ -44,7 +27,7 @@ class AddrReceiver(P2PInterface):
self.received_addrs = None
def get_received_addrs(self):
- with mininode_lock:
+ with p2p_lock:
return self.received_addrs
def on_addr(self, message):
@@ -62,18 +45,16 @@ class AddrTest(BitcoinTestFramework):
self.num_nodes = 1
def run_test(self):
- self.log.info('Create connection that sends and requests addr messages')
- addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
-
- msg_send_addrs = msg_addr()
self.log.info('Fill peer AddrMan with a lot of records')
- # Since these addrs are sent from the same source, not all of them will be stored,
- # because we allocate a limited number of AddrMan buckets per addr source.
- total_addrs = 10000
- addrs = gen_addrs(total_addrs)
- for i in range(int(total_addrs/MAX_ADDR_TO_SEND)):
- msg_send_addrs.addrs = addrs[i * MAX_ADDR_TO_SEND:(i + 1) * MAX_ADDR_TO_SEND]
- addr_source.send_and_ping(msg_send_addrs)
+ for i in range(10000):
+ first_octet = i >> 8
+ second_octet = i % 256
+ a = "{}.{}.1.1".format(first_octet, second_octet)
+ self.nodes[0].addpeeraddress(a, 8333)
+
+ # Need to make sure we hit MAX_ADDR_TO_SEND records in the addr response later because
+ # only a fraction of all known addresses can be cached and returned.
+ assert(len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100)))
responses = []
self.log.info('Send many addr requests within short time to receive same response')
@@ -89,7 +70,7 @@ class AddrTest(BitcoinTestFramework):
responses.append(addr_receiver.get_received_addrs())
for response in responses[1:]:
assert_equal(response, responses[0])
- assert(len(response) < MAX_ADDR_TO_SEND)
+ assert(len(response) == MAX_ADDR_TO_SEND)
cur_mock_time += 3 * 24 * 60 * 60
self.nodes[0].setmocktime(cur_mock_time)
diff --git a/test/functional/p2p_getdata.py b/test/functional/p2p_getdata.py
index d1b11c2c61..89d68d5ba0 100755
--- a/test/functional/p2p_getdata.py
+++ b/test/functional/p2p_getdata.py
@@ -9,7 +9,7 @@ from test_framework.messages import (
CInv,
msg_getdata,
)
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
@@ -42,7 +42,7 @@ class GetdataTest(BitcoinTestFramework):
good_getdata = msg_getdata()
good_getdata.inv.append(CInv(t=2, h=best_block))
p2p_block_store.send_and_ping(good_getdata)
- p2p_block_store.wait_until(lambda: self.nodes[0].p2ps[0].blocks[best_block] == 1)
+ p2p_block_store.wait_until(lambda: p2p_block_store.blocks[best_block] == 1)
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index e280a62997..483f25f48c 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -14,7 +14,7 @@ import copy
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script
from test_framework.messages import COIN
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -27,7 +27,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
def run_test(self):
# Add p2p connection to node0
node = self.nodes[0] # convenience reference to the node
- node.add_p2p_connection(P2PDataStore())
+ peer = node.add_p2p_connection(P2PDataStore())
best_block = node.getblock(node.getbestblockhash())
tip = int(node.getbestblockhash(), 16)
@@ -42,7 +42,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block1], node, success=True)
+ peer.send_blocks_and_test([block1], node, success=True)
self.log.info("Mature the block.")
node.generatetoaddress(100, node.get_deterministic_priv_key().address)
@@ -80,7 +80,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
assert_equal(orig_hash, block2.rehash())
assert block2_orig.vtx != block2.vtx
- node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
+ peer.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
# Check transactions for duplicate inputs (CVE-2018-17144)
self.log.info("Test duplicate input block.")
@@ -91,7 +91,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
block2_dup.rehash()
block2_dup.solve()
- node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
self.log.info("Test very broken block.")
@@ -104,14 +104,14 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block3.rehash()
block3.solve()
- node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
+ peer.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
# Complete testing of CVE-2012-2459 by sending the original block.
# It should be accepted even though it has the same hash as the mutated one.
self.log.info("Test accepting original block after rejecting its mutated version.")
- node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
+ peer.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
# Update tip info
height += 1
@@ -131,7 +131,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block4.rehash()
block4.solve()
self.log.info("Test inflation by duplicating input")
- node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py
index 0155eb21f0..e4fc9fd178 100755
--- a/test/functional/p2p_invalid_locator.py
+++ b/test/functional/p2p_invalid_locator.py
@@ -6,7 +6,7 @@
"""
from test_framework.messages import msg_getheaders, msg_getblocks, MAX_LOCATOR_SZ
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
@@ -23,20 +23,20 @@ class InvalidLocatorTest(BitcoinTestFramework):
block_count = node.getblockcount()
for msg in [msg_getheaders(), msg_getblocks()]:
self.log.info('Wait for disconnect when sending {} hashes in locator'.format(MAX_LOCATOR_SZ + 1))
- node.add_p2p_connection(P2PInterface())
+ exceed_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ + 1), -1)]
- node.p2p.send_message(msg)
- node.p2p.wait_for_disconnect()
+ exceed_max_peer.send_message(msg)
+ exceed_max_peer.wait_for_disconnect()
node.disconnect_p2ps()
self.log.info('Wait for response when sending {} hashes in locator'.format(MAX_LOCATOR_SZ))
- node.add_p2p_connection(P2PInterface())
+ within_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ), -1)]
- node.p2p.send_message(msg)
+ within_max_peer.send_message(msg)
if type(msg) == msg_getheaders:
- node.p2p.wait_for_header(node.getbestblockhash())
+ within_max_peer.wait_for_header(node.getbestblockhash())
else:
- node.p2p.wait_for_block(int(node.getbestblockhash(), 16))
+ within_max_peer.wait_for_block(int(node.getbestblockhash(), 16))
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index d9a9ae5188..db72a361d9 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -4,6 +4,9 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test node responses to invalid network messages."""
+import struct
+import time
+
from test_framework.messages import (
CBlockHeader,
CInv,
@@ -17,18 +20,19 @@ from test_framework.messages import (
MSG_TX,
ser_string,
)
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PDataStore,
P2PInterface,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
+ hex_str_to_bytes,
)
VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix
+
class msg_unrecognized:
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
@@ -44,6 +48,11 @@ class msg_unrecognized:
return "{}(data={})".format(self.msgtype, self.str_data)
+class SenderOfAddrV2(P2PInterface):
+ def wait_for_sendaddrv2(self):
+ self.wait_until(lambda: 'sendaddrv2' in self.last_message)
+
+
class InvalidMessagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -55,6 +64,10 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.test_checksum()
self.test_size()
self.test_msgtype()
+ self.test_addrv2_empty()
+ self.test_addrv2_no_addresses()
+ self.test_addrv2_too_long_address()
+ self.test_addrv2_unrecognized_network()
self.test_oversized_inv_msg()
self.test_oversized_getdata_msg()
self.test_oversized_headers_msg()
@@ -65,13 +78,13 @@ class InvalidMessagesTest(BitcoinTestFramework):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
# Create valid message
msg = conn.build_message(msg_ping(nonce=12345))
- cut_pos = 12 # Chosen at an arbitrary position within the header
+ cut_pos = 12 # Chosen at an arbitrary position within the header
# Send message in two pieces
- before = int(self.nodes[0].getnettotals()['totalbytesrecv'])
+ before = self.nodes[0].getnettotals()['totalbytesrecv']
conn.send_raw_message(msg[:cut_pos])
# Wait until node has processed the first half of the message
- wait_until(lambda: int(self.nodes[0].getnettotals()['totalbytesrecv']) != before)
- middle = int(self.nodes[0].getnettotals()['totalbytesrecv'])
+ self.wait_until(lambda: self.nodes[0].getnettotals()['totalbytesrecv'] != before)
+ middle = self.nodes[0].getnettotals()['totalbytesrecv']
# If this assert fails, we've hit an unlikely race
# where the test framework sent a message in between the two halves
assert_equal(middle, before + cut_pos)
@@ -82,7 +95,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(['PROCESSMESSAGE: INVALID MESSAGESTART badmsg']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - MESSAGESTART (badmsg, 2 bytes), received ffffffff']):
msg = conn.build_message(msg_unrecognized(str_data="d"))
# modify magic bytes
msg = b'\xff' * 4 + msg[4:]
@@ -101,12 +114,14 @@ class InvalidMessagesTest(BitcoinTestFramework):
msg = msg[:cut_len] + b'\xff' * 4 + msg[cut_len + 4:]
conn.send_raw_message(msg)
conn.sync_with_ping(timeout=1)
+ # Check that traffic is accounted for (24 bytes header + 2 bytes payload)
+ assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
self.nodes[0].disconnect_p2ps()
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(['']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE (badmsg, 4000001 bytes)']):
msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
msg = conn.build_message(msg)
conn.send_raw_message(msg)
@@ -116,16 +131,95 @@ 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(['PROCESSMESSAGE: ERRORS IN HEADER']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - COMMAND']):
msg = msg_unrecognized(str_data="d")
- msg.msgtype = b'\xff' * 12
msg = conn.build_message(msg)
# Modify msgtype
msg = msg[:7] + b'\x00' + msg[7 + 1:]
conn.send_raw_message(msg)
conn.sync_with_ping(timeout=1)
+ # Check that traffic is accounted for (24 bytes header + 2 bytes payload)
+ assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
self.nodes[0].disconnect_p2ps()
+ def test_addrv2(self, label, required_log_messages, raw_addrv2):
+ node = self.nodes[0]
+ conn = node.add_p2p_connection(SenderOfAddrV2())
+
+ # Make sure bitcoind signals support for ADDRv2, otherwise this test
+ # will bombard an old node with messages it does not recognize which
+ # will produce unexpected results.
+ conn.wait_for_sendaddrv2()
+
+ self.log.info('Test addrv2: ' + label)
+
+ msg = msg_unrecognized(str_data=b'')
+ msg.msgtype = b'addrv2'
+ with node.assert_debug_log(required_log_messages):
+ # override serialize() which would include the length of the data
+ msg.serialize = lambda: raw_addrv2
+ conn.send_raw_message(conn.build_message(msg))
+ conn.sync_with_ping()
+
+ node.disconnect_p2ps()
+
+ def test_addrv2_empty(self):
+ self.test_addrv2('empty',
+ [
+ 'received: addrv2 (0 bytes)',
+ 'ProcessMessages(addrv2, 0 bytes): Exception',
+ 'end of data',
+ ],
+ b'')
+
+ def test_addrv2_no_addresses(self):
+ self.test_addrv2('no addresses',
+ [
+ 'received: addrv2 (1 bytes)',
+ ],
+ hex_str_to_bytes('00'))
+
+ def test_addrv2_too_long_address(self):
+ self.test_addrv2('too long address',
+ [
+ 'received: addrv2 (525 bytes)',
+ 'ProcessMessages(addrv2, 525 bytes): Exception',
+ 'Address too long: 513 > 512',
+ ],
+ hex_str_to_bytes(
+ '01' + # number of entries
+ '61bc6649' + # time, Fri Jan 9 02:54:25 UTC 2009
+ '00' + # service flags, COMPACTSIZE(NODE_NONE)
+ '01' + # network type (IPv4)
+ 'fd0102' + # address length (COMPACTSIZE(513))
+ 'ab' * 513 + # address
+ '208d')) # port
+
+ def test_addrv2_unrecognized_network(self):
+ now_hex = struct.pack('<I', int(time.time())).hex()
+ self.test_addrv2('unrecognized network',
+ [
+ 'received: addrv2 (25 bytes)',
+ 'IP 9.9.9.9 mapped',
+ 'Added 1 addresses',
+ ],
+ hex_str_to_bytes(
+ '02' + # number of entries
+ # this should be ignored without impeding acceptance of subsequent ones
+ now_hex + # time
+ '01' + # service flags, COMPACTSIZE(NODE_NETWORK)
+ '99' + # network type (unrecognized)
+ '02' + # address length (COMPACTSIZE(2))
+ 'ab' * 2 + # address
+ '208d' + # port
+ # this should be added:
+ now_hex + # time
+ '01' + # service flags, COMPACTSIZE(NODE_NETWORK)
+ '01' + # network type (IPv4)
+ '04' + # address length (COMPACTSIZE(4))
+ '09' * 4 + # address
+ '208d')) # port
+
def test_oversized_msg(self, msg, size):
msg_type = msg.msgtype.decode('ascii')
self.log.info("Test {} message of size {} is logged as misbehaving".format(msg_type, size))
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index c70a892463..489d903c21 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -13,11 +13,10 @@ from test_framework.messages import (
CTxIn,
CTxOut,
)
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
)
from data import invalid_txs
@@ -62,7 +61,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block], node, success=True)
+ node.p2ps[0].send_blocks_and_test([block], node, success=True)
self.log.info("Mature the block.")
self.nodes[0].generatetoaddress(100, self.nodes[0].get_deterministic_priv_key().address)
@@ -73,7 +72,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)
template = BadTxTemplate(spend_block=block1)
tx = template.get_tx()
- node.p2p.send_txs_and_test(
+ node.p2ps[0].send_txs_and_test(
[tx], node, success=False,
expect_disconnect=template.expect_disconnect,
reject_reason=template.reject_reason,
@@ -122,7 +121,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the orphans ... ')
# Send valid orphan txs from p2ps[0]
- node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
+ node.p2ps[0].send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
# Send invalid tx from p2ps[1]
node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)
@@ -131,7 +130,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the withhold tx ... ')
with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
- node.p2p.send_txs_and_test([tx_withhold], node, success=True)
+ node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True)
# Transactions that should end up in the mempool
expected_mempool = {
@@ -146,7 +145,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
# tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)
- wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
+ self.wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
assert_equal(expected_mempool, set(node.getrawmempool()))
self.log.info('Test orphan pool overflow')
@@ -156,14 +155,14 @@ class InvalidTxRequestTest(BitcoinTestFramework):
orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
- node.p2p.send_txs_and_test(orphan_tx_pool, node, success=False)
+ node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
rejected_parent = CTransaction()
rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
rejected_parent.rehash()
with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.hash)]):
- node.p2p.send_txs_and_test([rejected_parent], node, success=False)
+ node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
if __name__ == '__main__':
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 2fc5245241..4b32d60db0 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -15,21 +15,19 @@ import time
from test_framework.messages import (
msg_getaddr,
msg_ping,
- msg_verack,
msg_version,
)
-from test_framework.mininode import mininode_lock, P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
- wait_until,
)
DISCOURAGEMENT_THRESHOLD = 100
-class CLazyNode(P2PInterface):
+class LazyPeer(P2PInterface):
def __init__(self):
super().__init__()
self.unexpected_msg = False
@@ -42,6 +40,7 @@ class CLazyNode(P2PInterface):
def on_open(self):
self.ever_connected = True
+ # Does not respond to "version" with "verack"
def on_version(self, message): self.bad_message(message)
def on_verack(self, message): self.bad_message(message)
def on_inv(self, message): self.bad_message(message)
@@ -64,21 +63,8 @@ class CLazyNode(P2PInterface):
def on_blocktxn(self, message): self.bad_message(message)
-# Node that never sends a version. We'll use this to send a bunch of messages
-# anyway, and eventually get disconnected.
-class CNodeNoVersionMisbehavior(CLazyNode):
- pass
-
-
-# Node that never sends a version. This one just sits idle and hopes to receive
-# any message (it shouldn't!)
-class CNodeNoVersionIdle(CLazyNode):
- def __init__(self):
- super().__init__()
-
-
-# Node that sends a version but not a verack.
-class CNodeNoVerackIdle(CLazyNode):
+# Peer that sends a version but not a verack.
+class NoVerackIdlePeer(LazyPeer):
def __init__(self):
self.version_received = False
super().__init__()
@@ -97,6 +83,7 @@ class P2PVersionStore(P2PInterface):
version_received = None
def on_version(self, msg):
+ # Responds with an appropriate verack
super().on_version(msg)
self.version_received = msg
@@ -106,39 +93,45 @@ class P2PLeakTest(BitcoinTestFramework):
self.num_nodes = 1
def run_test(self):
- no_version_disconnect_node = self.nodes[0].add_p2p_connection(
- CNodeNoVersionMisbehavior(), send_version=False, wait_for_verack=False)
- no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False, wait_for_verack=False)
- no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle(), wait_for_verack=False)
+ # Peer that never sends a version. We will send a bunch of messages
+ # from this peer anyway and verify eventual disconnection.
+ no_version_disconnect_peer = self.nodes[0].add_p2p_connection(
+ LazyPeer(), send_version=False, wait_for_verack=False)
+
+ # Another peer that never sends a version, nor any other messages. It shouldn't receive anything from the node.
+ no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False)
+
+ # Peer that sends a version but not a verack.
+ no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False)
- # Send enough veracks without a message to reach the peer discouragement
- # threshold. This should get us disconnected.
+ # Send enough ping messages (any non-version message will do) prior to sending
+ # version to reach the peer discouragement threshold. This should get us disconnected.
for _ in range(DISCOURAGEMENT_THRESHOLD):
- no_version_disconnect_node.send_message(msg_verack())
+ no_version_disconnect_peer.send_message(msg_ping())
- # Wait until we got the verack in response to the version. Though, don't wait for the other node to receive the
+ # Wait until we got the verack in response to the version. Though, don't wait for the node to receive the
# verack, since we never sent one
- no_verack_idlenode.wait_for_verack()
+ no_verack_idle_peer.wait_for_verack()
- wait_until(lambda: no_version_disconnect_node.ever_connected, timeout=10, lock=mininode_lock)
- wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock)
- wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock)
+ no_version_disconnect_peer.wait_until(lambda: no_version_disconnect_peer.ever_connected, check_connected=False)
+ no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected)
+ no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received)
- # Mine a block and make sure that it's not sent to the connected nodes
- self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)
+ # Mine a block and make sure that it's not sent to the connected peers
+ self.nodes[0].generate(nblocks=1)
#Give the node enough time to possibly leak out a message
time.sleep(5)
- # Expect this node to be disconnected for misbehavior
- assert not no_version_disconnect_node.is_connected
+ # Expect this peer to be disconnected for misbehavior
+ assert not no_version_disconnect_peer.is_connected
self.nodes[0].disconnect_p2ps()
# Make sure no unexpected messages came in
- assert no_version_disconnect_node.unexpected_msg == False
- assert no_version_idlenode.unexpected_msg == False
- assert no_verack_idlenode.unexpected_msg == False
+ assert no_version_disconnect_peer.unexpected_msg == False
+ assert no_version_idle_peer.unexpected_msg == False
+ assert no_verack_idle_peer.unexpected_msg == False
self.log.info('Check that the version message does not leak the local address of the node')
p2p_version_store = self.nodes[0].add_p2p_connection(P2PVersionStore())
@@ -151,13 +144,13 @@ class P2PLeakTest(BitcoinTestFramework):
assert_equal(ver.nStartingHeight, 201)
assert_equal(ver.nRelay, 1)
- self.log.info('Check that old nodes are disconnected')
- p2p_old_node = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
+ self.log.info('Check that old peers are disconnected')
+ p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
old_version_msg = msg_version()
old_version_msg.nVersion = 31799
with self.nodes[0].assert_debug_log(['peer=4 using obsolete version 31799; disconnecting']):
- p2p_old_node.send_message(old_version_msg)
- p2p_old_node.wait_for_disconnect()
+ p2p_old_peer.send_message(old_version_msg)
+ p2p_old_peer.wait_for_disconnect()
if __name__ == '__main__':
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
index da30ad5977..a45f792e81 100755
--- a/test/functional/p2p_leak_tx.py
+++ b/test/functional/p2p_leak_tx.py
@@ -5,11 +5,12 @@
"""Test that we don't leak txs to inbound peers that we haven't yet announced to"""
from test_framework.messages import msg_getdata, CInv, MSG_TX
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import p2p_lock, P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
+from test_framework.wallet import MiniWallet
class P2PNode(P2PDataStore):
@@ -21,12 +22,12 @@ class P2PLeakTxTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
gen_node = self.nodes[0] # The block and tx generating node
- gen_node.generate(1)
+ 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)
inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
@@ -34,18 +35,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
self.log.info("Running test up to {} times.".format(MAX_REPEATS))
for i in range(MAX_REPEATS):
self.log.info('Run repeat {}'.format(i + 1))
- txid = gen_node.sendtoaddress(gen_node.getnewaddress(), 0.01)
+ txid = miniwallet.send_self_transfer(from_node=gen_node)['wtxid']
want_tx = msg_getdata()
want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16)))
- inbound_peer.last_message.pop('notfound', None)
+ with p2p_lock:
+ inbound_peer.last_message.pop('notfound', None)
inbound_peer.send_and_ping(want_tx)
if inbound_peer.last_message.get('notfound'):
self.log.debug('tx {} was not yet announced to us.'.format(txid))
self.log.debug("node has responded with a notfound message. End test.")
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
- inbound_peer.last_message.pop('notfound')
+ with p2p_lock:
+ inbound_peer.last_message.pop('notfound')
break
else:
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
diff --git a/test/functional/p2p_nobloomfilter_messages.py b/test/functional/p2p_nobloomfilter_messages.py
index accc5dc23c..c2311cb197 100755
--- a/test/functional/p2p_nobloomfilter_messages.py
+++ b/test/functional/p2p_nobloomfilter_messages.py
@@ -12,7 +12,7 @@ Test that, when bloom filters are not enabled, peers are disconnected if:
"""
from test_framework.messages import msg_mempool, msg_filteradd, msg_filterload, msg_filterclear
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index a2f6ea538c..b1a7ef6877 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -9,13 +9,10 @@ and that it responds to getdata requests for blocks correctly:
- send a block within 288 + 2 of the tip
- disconnect peers who request blocks older than that."""
from test_framework.messages import CInv, MSG_BLOCK, msg_getdata, msg_verack, NODE_NETWORK_LIMITED, NODE_WITNESS
-from test_framework.mininode import P2PInterface, mininode_lock
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- disconnect_nodes,
- connect_nodes,
- wait_until,
)
@@ -28,7 +25,7 @@ class P2PIgnoreInv(P2PInterface):
self.firstAddrnServices = message.addrs[0].nServices
def wait_for_addr(self, timeout=5):
test_function = lambda: self.last_message.get("addr")
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def send_getdata_for_block(self, blockhash):
getdata_request = msg_getdata()
getdata_request.inv.append(CInv(MSG_BLOCK, int(blockhash, 16)))
@@ -41,9 +38,9 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.extra_args = [['-prune=550', '-addrmantest'], [], []]
def disconnect_all(self):
- disconnect_nodes(self.nodes[0], 1)
- disconnect_nodes(self.nodes[0], 2)
- disconnect_nodes(self.nodes[1], 2)
+ self.disconnect_nodes(0, 1)
+ self.disconnect_nodes(0, 2)
+ self.disconnect_nodes(1, 2)
def setup_network(self):
self.add_nodes(self.num_nodes, self.extra_args)
@@ -61,7 +58,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services)
self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
blocks = self.nodes[1].generatetoaddress(292, self.nodes[1].get_deterministic_priv_key().address)
self.sync_blocks([self.nodes[0], self.nodes[1]])
@@ -86,7 +83,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
# connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer
# because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
try:
self.sync_blocks([self.nodes[0], self.nodes[2]], timeout=5)
except:
@@ -95,7 +92,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0)
# now connect also to node 1 (non pruned)
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
# sync must be possible
self.sync_blocks()
@@ -107,7 +104,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address)
# connect node1 (non pruned) with node0 (pruned) and check if the can sync
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
# sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED)
self.sync_blocks([self.nodes[0], self.nodes[1]])
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 254352c816..653e3894af 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -13,7 +13,7 @@ from test_framework.messages import (
CTxInWitness,
FromHex,
)
-from test_framework.mininode import P2PDataStore
+from test_framework.p2p import P2PDataStore
from test_framework.script import (
CScript,
OP_TRUE,
@@ -22,9 +22,7 @@ from test_framework.test_node import ErrorMatch
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
p2p_port,
- wait_until,
)
@@ -44,6 +42,13 @@ class P2PPermissionsTests(BitcoinTestFramework):
True)
self.checkpermission(
+ # check without deprecatedrpc=whitelisted
+ ["-whitelist=127.0.0.1"],
+ # Make sure the default values in the command line documentation match the ones here
+ ["relay", "noban", "mempool", "download"],
+ None)
+
+ self.checkpermission(
# no permission (even with forcerelay)
["-whitelist=@127.0.0.1", "-whitelistforcerelay=1"],
[],
@@ -82,6 +87,12 @@ class P2PPermissionsTests(BitcoinTestFramework):
False)
self.checkpermission(
+ # check without deprecatedrpc=whitelisted
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
+ ["noban", "mempool", "download"],
+ None)
+
+ self.checkpermission(
# legacy whitelistforcerelay should be ignored
["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
["noban", "mempool", "download"],
@@ -109,7 +120,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
self.sync_all()
self.log.debug("Create a connection from a forcerelay peer that rebroadcasts raw txs")
- # A python mininode is needed to send the raw transaction directly. If a full node was used, it could only
+ # A test framework p2p connection is needed to send the raw transaction directly. If a full node was used, it could only
# rebroadcast via the inv-getdata mechanism. However, even for forcerelay connections, a full node would
# currently not request a txid that is already in the mempool.
self.restart_node(1, extra_args=["-whitelist=forcerelay@127.0.0.1"])
@@ -134,10 +145,10 @@ class P2PPermissionsTests(BitcoinTestFramework):
p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
self.log.debug("Check that node[1] will send the tx to node[0] even though it is already in the mempool")
- connect_nodes(self.nodes[1], 0)
+ self.connect_nodes(1, 0)
with self.nodes[1].assert_debug_log(["Force relaying tx {} from peer=0".format(txid)]):
p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
- wait_until(lambda: txid in self.nodes[0].getrawmempool())
+ self.wait_until(lambda: txid in self.nodes[0].getrawmempool())
self.log.debug("Check that node[1] will not send an invalid tx to node[0]")
tx.vout[0].nValue += 1
@@ -150,10 +161,15 @@ class P2PPermissionsTests(BitcoinTestFramework):
)
def checkpermission(self, args, expectedPermissions, whitelisted):
+ if whitelisted is not None:
+ args = [*args, '-deprecatedrpc=whitelisted']
self.restart_node(1, args)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
- assert_equal(peerinfo['whitelisted'], whitelisted)
+ if whitelisted is None:
+ assert 'whitelisted' not in peerinfo
+ else:
+ assert_equal(peerinfo['whitelisted'], whitelisted)
assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
for p in expectedPermissions:
if not p in peerinfo['permissions']:
diff --git a/test/functional/p2p_ping.py b/test/functional/p2p_ping.py
index 5f5fd3e104..888e986fba 100755
--- a/test/functional/p2p_ping.py
+++ b/test/functional/p2p_ping.py
@@ -8,7 +8,7 @@
import time
from test_framework.messages import msg_pong
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 564e49f3d8..5c15538418 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -3,6 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test segwit transactions and blocks on P2P network."""
+from decimal import Decimal
import math
import random
import struct
@@ -25,6 +26,7 @@ from test_framework.messages import (
MSG_BLOCK,
MSG_TX,
MSG_WITNESS_FLAG,
+ MSG_WITNESS_TX,
MSG_WTX,
NODE_NETWORK,
NODE_WITNESS,
@@ -42,9 +44,9 @@ from test_framework.messages import (
uint256_from_str,
FromHex,
)
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PInterface,
- mininode_lock,
+ p2p_lock,
)
from test_framework.script import (
CScript,
@@ -53,6 +55,7 @@ from test_framework.script import (
MAX_SCRIPT_ELEMENT_SIZE,
OP_0,
OP_1,
+ OP_2,
OP_16,
OP_2DROP,
OP_CHECKMULTISIG,
@@ -78,17 +81,13 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- disconnect_nodes,
softfork_active,
hex_str_to_bytes,
assert_raises_rpc_error,
- wait_until,
)
# The versionbit bit used to signal activation of SegWit
VB_WITNESS_BIT = 1
-VB_PERIOD = 144
VB_TOP_BITS = 0x20000000
MAX_SIGOP_COST = 80000
@@ -153,8 +152,8 @@ class TestP2PConn(P2PInterface):
self.lastgetdata = []
self.wtxidrelay = wtxidrelay
- # Avoid sending out msg_getdata in the mininode thread as a reply to invs.
- # They are not needed and would only lead to races because we send msg_getdata out in the test thread
+ # Don't send getdata message replies to invs automatically.
+ # We'll send the getdata messages explicitly in the test logic.
def on_inv(self, message):
pass
@@ -177,7 +176,7 @@ class TestP2PConn(P2PInterface):
if success:
# sanity check
assert (self.wtxidrelay and use_wtxid) or (not self.wtxidrelay and not use_wtxid)
- with mininode_lock:
+ with p2p_lock:
self.last_message.pop("getdata", None)
if use_wtxid:
wtxid = tx.calc_sha256(True)
@@ -195,7 +194,7 @@ class TestP2PConn(P2PInterface):
assert not self.last_message.get("getdata")
def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60):
- with mininode_lock:
+ with p2p_lock:
self.last_message.pop("getdata", None)
self.last_message.pop("getheaders", None)
msg = msg_headers()
@@ -209,7 +208,7 @@ class TestP2PConn(P2PInterface):
self.wait_for_getdata([block.sha256])
def request_block(self, blockhash, inv_type, timeout=60):
- with mininode_lock:
+ with p2p_lock:
self.last_message.pop("block", None)
self.send_message(msg_getdata(inv=[CInv(inv_type, blockhash)]))
self.wait_for_block(blockhash, timeout)
@@ -232,8 +231,8 @@ class SegWitTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
self.sync_all()
# Helper functions
@@ -497,7 +496,7 @@ class SegWitTest(BitcoinTestFramework):
# node2 doesn't need to be connected for this test.
# (If it's connected, node0 may propagate an invalid block to it over
# compact blocks and the nodes would have inconsistent tips.)
- disconnect_nodes(self.nodes[0], 2)
+ self.disconnect_nodes(0, 2)
# Create two outputs, a p2wsh and p2sh-p2wsh
witness_program = CScript([OP_TRUE])
@@ -559,7 +558,7 @@ class SegWitTest(BitcoinTestFramework):
# TODO: support multiple acceptable reject reasons.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
@@ -695,13 +694,13 @@ class SegWitTest(BitcoinTestFramework):
if not self.segwit_active:
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
# in blocks and the tx is impossible to mine right now.
- assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}])
# Create the same output as tx3, but by replacing tx
tx3_out = tx3.vout[0]
tx3 = tx
tx3.vout = [tx3_out]
tx3.rehash()
- assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00011000')}}])
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True)
self.nodes[0].generate(1)
@@ -1400,7 +1399,11 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(len(self.nodes[1].getrawmempool()), 0)
for version in list(range(OP_1, OP_16 + 1)) + [OP_0]:
# First try to spend to a future version segwit script_pubkey.
- script_pubkey = CScript([CScriptOp(version), witness_hash])
+ if version == OP_1:
+ # Don't use 32-byte v1 witness (used by Taproot; see BIP 341)
+ script_pubkey = CScript([CScriptOp(version), witness_hash + b'\x00'])
+ else:
+ script_pubkey = CScript([CScriptOp(version), witness_hash])
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
tx.vout = [CTxOut(self.utxo[0].nValue - 1000, script_pubkey)]
tx.rehash()
@@ -1413,9 +1416,9 @@ class SegWitTest(BitcoinTestFramework):
self.sync_blocks()
assert len(self.nodes[0].getrawmempool()) == 0
- # Finally, verify that version 0 -> version 1 transactions
+ # Finally, verify that version 0 -> version 2 transactions
# are standard
- script_pubkey = CScript([CScriptOp(OP_1), witness_hash])
+ script_pubkey = CScript([CScriptOp(OP_2), witness_hash])
tx2 = CTransaction()
tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
tx2.vout = [CTxOut(tx.vout[0].nValue - 1000, script_pubkey)]
@@ -1940,7 +1943,7 @@ class SegWitTest(BitcoinTestFramework):
"""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)])
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
# We reconnect more than 100 blocks, give it plenty of time
self.sync_blocks(timeout=240)
@@ -2096,14 +2099,14 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
raw = self.nodes[0].signrawtransactionwithwallet(raw)
assert raw['complete']
raw = raw['hex']
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
@subtest # type: ignore
def test_wtxid_relay(self):
@@ -2114,7 +2117,7 @@ class SegWitTest(BitcoinTestFramework):
# Check wtxidrelay feature negotiation message through connecting a new peer
def received_wtxidrelay():
return (len(self.wtx_node.last_wtxidrelay) > 0)
- wait_until(received_wtxidrelay, timeout=60, lock=mininode_lock)
+ self.wtx_node.wait_until(received_wtxidrelay)
# Create a Segwit output from the latest UTXO
# and announce it to the network
@@ -2138,27 +2141,27 @@ class SegWitTest(BitcoinTestFramework):
# Announce Segwit transaction with wtxid
# and wait for getdata
self.wtx_node.announce_tx_and_wait_for_getdata(tx2, use_wtxid=True)
- with mininode_lock:
+ with p2p_lock:
lgd = self.wtx_node.lastgetdata[:]
assert_equal(lgd, [CInv(MSG_WTX, tx2.calc_sha256(True))])
# Announce Segwit transaction from non wtxidrelay peer
# and wait for getdata
self.tx_node.announce_tx_and_wait_for_getdata(tx2, use_wtxid=False)
- with mininode_lock:
+ with p2p_lock:
lgd = self.tx_node.lastgetdata[:]
assert_equal(lgd, [CInv(MSG_TX|MSG_WITNESS_FLAG, tx2.sha256)])
# Send tx2 through; it's an orphan so won't be accepted
- with mininode_lock:
+ with p2p_lock:
self.wtx_node.last_message.pop("getdata", None)
test_transaction_acceptance(self.nodes[0], self.wtx_node, tx2, with_witness=True, accepted=False)
# Expect a request for parent (tx) by txid despite use of WTX peer
self.wtx_node.wait_for_getdata([tx.sha256], 60)
- with mininode_lock:
+ with p2p_lock:
lgd = self.wtx_node.lastgetdata[:]
- assert_equal(lgd, [CInv(MSG_TX|MSG_WITNESS_FLAG, tx.sha256)])
+ assert_equal(lgd, [CInv(MSG_WITNESS_TX, tx.sha256)])
# Send tx through
test_transaction_acceptance(self.nodes[0], self.wtx_node, tx, with_witness=False, accepted=True)
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index 126a46bd53..04e6ec4172 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -87,11 +87,11 @@ e. Announce one more that doesn't connect.
"""
from test_framework.blocktools import create_block, create_coinbase
from test_framework.messages import CInv
-from test_framework.mininode import (
+from test_framework.p2p import (
CBlockHeader,
NODE_WITNESS,
P2PInterface,
- mininode_lock,
+ p2p_lock,
MSG_BLOCK,
msg_block,
msg_getblocks,
@@ -104,7 +104,6 @@ from test_framework.mininode import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
)
DIRECT_FETCH_RESPONSE_TIME = 0.05
@@ -147,7 +146,7 @@ class BaseNode(P2PInterface):
def wait_for_block_announcement(self, block_hash, timeout=60):
test_function = lambda: self.last_blockhash_announced == block_hash
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
+ self.wait_until(test_function, timeout=timeout)
def on_inv(self, message):
self.block_announced = True
@@ -163,7 +162,7 @@ class BaseNode(P2PInterface):
self.last_blockhash_announced = message.headers[-1].sha256
def clear_block_announcements(self):
- with mininode_lock:
+ with p2p_lock:
self.block_announced = False
self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
@@ -174,8 +173,8 @@ class BaseNode(P2PInterface):
"""Test whether the last headers announcements received are right.
Headers may be announced across more than one message."""
test_function = lambda: (len(self.recent_headers_announced) >= len(headers))
- wait_until(test_function, timeout=60, lock=mininode_lock)
- with mininode_lock:
+ self.wait_until(test_function)
+ with p2p_lock:
assert_equal(self.recent_headers_announced, headers)
self.block_announced = False
self.last_message.pop("headers", None)
@@ -186,9 +185,9 @@ class BaseNode(P2PInterface):
inv should be a list of block hashes."""
test_function = lambda: self.block_announced
- wait_until(test_function, timeout=60, lock=mininode_lock)
+ self.wait_until(test_function)
- with mininode_lock:
+ with p2p_lock:
compare_inv = []
if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_message["inv"].inv]
@@ -298,7 +297,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks([new_block])
test_node.wait_for_getdata([new_block.sha256])
test_node.send_and_ping(msg_block(new_block)) # make sure this block is processed
- wait_until(lambda: inv_node.block_announced, timeout=60, lock=mininode_lock)
+ inv_node.wait_until(lambda: inv_node.block_announced)
inv_node.clear_block_announcements()
test_node.clear_block_announcements()
@@ -456,7 +455,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
# should not have received any getdata messages
- with mininode_lock:
+ with p2p_lock:
assert "getdata" not in test_node.last_message
# This time, direct fetch should work
@@ -494,7 +493,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[0:1])
test_node.sync_with_ping()
- with mininode_lock:
+ with p2p_lock:
assert "getdata" not in test_node.last_message
# Announcing one more block on fork should trigger direct fetch for
@@ -513,7 +512,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks[18:19])
test_node.sync_with_ping()
- with mininode_lock:
+ with p2p_lock:
assert "getdata" not in test_node.last_message
self.log.info("Part 4: success!")
@@ -536,7 +535,7 @@ class SendHeadersTest(BitcoinTestFramework):
block_time += 1
height += 1
# Send the header of the second block -> this won't connect.
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[1]])
test_node.wait_for_getheaders()
@@ -559,7 +558,7 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(1, MAX_UNCONNECTING_HEADERS):
# Send a header that doesn't connect, check that we get a getheaders.
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i]])
test_node.wait_for_getheaders()
@@ -574,7 +573,7 @@ class SendHeadersTest(BitcoinTestFramework):
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS
for i in range(5 * MAX_UNCONNECTING_HEADERS - 1):
# Send a header that doesn't connect, check that we get a getheaders.
- with mininode_lock:
+ with p2p_lock:
test_node.last_message.pop("getheaders", None)
test_node.send_header_for_blocks([blocks[i % len(blocks)]])
test_node.wait_for_getheaders()
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index 5a4fa42988..ce12ce26ce 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -24,7 +24,7 @@
from time import sleep
from test_framework.messages import msg_ping
-from test_framework.mininode import P2PInterface
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 3ea1c6e5e7..16d9302db8 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -16,14 +16,13 @@ from test_framework.messages import (
msg_inv,
msg_notfound,
)
-from test_framework.mininode import (
+from test_framework.p2p import (
P2PInterface,
- mininode_lock,
+ p2p_lock,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- wait_until,
)
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
@@ -43,15 +42,15 @@ class TestP2PConn(P2PInterface):
# Constants from net_processing
GETDATA_TX_INTERVAL = 60 # seconds
-MAX_GETDATA_RANDOM_DELAY = 2 # seconds
INBOUND_PEER_TX_DELAY = 2 # seconds
TXID_RELAY_DELAY = 2 # seconds
+OVERLOADED_PEER_DELAY = 2 # seconds
MAX_GETDATA_IN_FLIGHT = 100
-TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
+MAX_PEER_TX_ANNOUNCEMENTS = 5000
# Python test constants
NUM_INBOUND = 10
-MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY + TXID_RELAY_DELAY
+MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + INBOUND_PEER_TX_DELAY + TXID_RELAY_DELAY
class TxDownloadTest(BitcoinTestFramework):
@@ -73,14 +72,14 @@ class TxDownloadTest(BitcoinTestFramework):
def getdata_found(peer_index):
p = self.nodes[0].p2ps[peer_index]
- with mininode_lock:
+ with p2p_lock:
return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid
node_0_mocktime = int(time.time())
while outstanding_peer_index:
node_0_mocktime += MAX_GETDATA_INBOUND_WAIT
self.nodes[0].setmocktime(node_0_mocktime)
- wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
+ self.wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
for i in outstanding_peer_index:
if getdata_found(i):
outstanding_peer_index.remove(i)
@@ -122,60 +121,143 @@ class TxDownloadTest(BitcoinTestFramework):
# * the first time it is re-requested from the outbound peer, plus
# * 2 seconds to avoid races
assert self.nodes[1].getpeerinfo()[0]['inbound'] == False
- timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + (
- GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY)
+ timeout = 2 + INBOUND_PEER_TX_DELAY + GETDATA_TX_INTERVAL
self.log.info("Tx should be received at node 1 after {} seconds".format(timeout))
self.sync_mempools(timeout=timeout)
def test_in_flight_max(self):
- self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format(
- MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60))
+ self.log.info("Test that we don't load peers with more than {} transaction requests immediately".format(MAX_GETDATA_IN_FLIGHT))
txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)]
p = self.nodes[0].p2ps[0]
- with mininode_lock:
+ with p2p_lock:
p.tx_getdata_count = 0
- p.send_message(msg_inv([CInv(t=MSG_WTX, h=i) for i in txids]))
- wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT, lock=mininode_lock)
- with mininode_lock:
+ mock_time = int(time.time() + 1)
+ self.nodes[0].setmocktime(mock_time)
+ for i in range(MAX_GETDATA_IN_FLIGHT):
+ p.send_message(msg_inv([CInv(t=MSG_WTX, h=txids[i])]))
+ p.sync_with_ping()
+ mock_time += INBOUND_PEER_TX_DELAY
+ self.nodes[0].setmocktime(mock_time)
+ p.wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT)
+ for i in range(MAX_GETDATA_IN_FLIGHT, len(txids)):
+ p.send_message(msg_inv([CInv(t=MSG_WTX, h=txids[i])]))
+ p.sync_with_ping()
+ self.log.info("No more than {} requests should be seen within {} seconds after announcement".format(MAX_GETDATA_IN_FLIGHT, INBOUND_PEER_TX_DELAY + OVERLOADED_PEER_DELAY - 1))
+ self.nodes[0].setmocktime(mock_time + INBOUND_PEER_TX_DELAY + OVERLOADED_PEER_DELAY - 1)
+ p.sync_with_ping()
+ with p2p_lock:
assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT)
-
- self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request")
- p.send_message(msg_notfound(vec=[CInv(t=MSG_WTX, h=txids[0])]))
- wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT + 1, timeout=10, lock=mininode_lock)
- with mininode_lock:
- assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1)
-
- WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL
- self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60))
- self.nodes[0].setmocktime(int(time.time() + WAIT_TIME))
- wait_until(lambda: p.tx_getdata_count == MAX_GETDATA_IN_FLIGHT + 2)
- self.nodes[0].setmocktime(0)
+ self.log.info("If we wait {} seconds after announcement, we should eventually get more requests".format(INBOUND_PEER_TX_DELAY + OVERLOADED_PEER_DELAY))
+ self.nodes[0].setmocktime(mock_time + INBOUND_PEER_TX_DELAY + OVERLOADED_PEER_DELAY)
+ p.wait_until(lambda: p.tx_getdata_count == len(txids))
+
+ def test_expiry_fallback(self):
+ self.log.info('Check that expiry will select another peer for download')
+ WTXID = 0xffaa
+ peer1 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer2 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ for p in [peer1, peer2]:
+ p.send_message(msg_inv([CInv(t=MSG_WTX, h=WTXID)]))
+ # One of the peers is asked for the tx
+ peer2.wait_until(lambda: sum(p.tx_getdata_count for p in [peer1, peer2]) == 1)
+ with p2p_lock:
+ peer_expiry, peer_fallback = (peer1, peer2) if peer1.tx_getdata_count == 1 else (peer2, peer1)
+ assert_equal(peer_fallback.tx_getdata_count, 0)
+ self.nodes[0].setmocktime(int(time.time()) + GETDATA_TX_INTERVAL + 1) # Wait for request to peer_expiry to expire
+ peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1)
+ with p2p_lock:
+ assert_equal(peer_fallback.tx_getdata_count, 1)
+ self.restart_node(0) # reset mocktime
+
+ def test_disconnect_fallback(self):
+ self.log.info('Check that disconnect will select another peer for download')
+ WTXID = 0xffbb
+ peer1 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer2 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ for p in [peer1, peer2]:
+ p.send_message(msg_inv([CInv(t=MSG_WTX, h=WTXID)]))
+ # One of the peers is asked for the tx
+ peer2.wait_until(lambda: sum(p.tx_getdata_count for p in [peer1, peer2]) == 1)
+ with p2p_lock:
+ peer_disconnect, peer_fallback = (peer1, peer2) if peer1.tx_getdata_count == 1 else (peer2, peer1)
+ assert_equal(peer_fallback.tx_getdata_count, 0)
+ peer_disconnect.peer_disconnect()
+ peer_disconnect.wait_for_disconnect()
+ peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1)
+ with p2p_lock:
+ assert_equal(peer_fallback.tx_getdata_count, 1)
+
+ def test_notfound_fallback(self):
+ self.log.info('Check that notfounds will select another peer for download immediately')
+ WTXID = 0xffdd
+ peer1 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer2 = self.nodes[0].add_p2p_connection(TestP2PConn())
+ for p in [peer1, peer2]:
+ p.send_message(msg_inv([CInv(t=MSG_WTX, h=WTXID)]))
+ # One of the peers is asked for the tx
+ peer2.wait_until(lambda: sum(p.tx_getdata_count for p in [peer1, peer2]) == 1)
+ with p2p_lock:
+ peer_notfound, peer_fallback = (peer1, peer2) if peer1.tx_getdata_count == 1 else (peer2, peer1)
+ assert_equal(peer_fallback.tx_getdata_count, 0)
+ peer_notfound.send_and_ping(msg_notfound(vec=[CInv(MSG_WTX, WTXID)])) # Send notfound, so that fallback peer is selected
+ peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1)
+ with p2p_lock:
+ assert_equal(peer_fallback.tx_getdata_count, 1)
+
+ def test_preferred_inv(self):
+ self.log.info('Check that invs from preferred peers are downloaded immediately')
+ self.restart_node(0, extra_args=['-whitelist=noban@127.0.0.1'])
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer.send_message(msg_inv([CInv(t=MSG_WTX, h=0xff00ff00)]))
+ peer.wait_until(lambda: peer.tx_getdata_count >= 1, timeout=1)
+ with p2p_lock:
+ assert_equal(peer.tx_getdata_count, 1)
+
+ def test_large_inv_batch(self):
+ self.log.info('Test how large inv batches are handled with relay permission')
+ self.restart_node(0, extra_args=['-whitelist=relay@127.0.0.1'])
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer.send_message(msg_inv([CInv(t=MSG_WTX, h=wtxid) for wtxid in range(MAX_PEER_TX_ANNOUNCEMENTS + 1)]))
+ peer.wait_until(lambda: peer.tx_getdata_count == MAX_PEER_TX_ANNOUNCEMENTS + 1)
+
+ self.log.info('Test how large inv batches are handled without relay permission')
+ self.restart_node(0)
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer.send_message(msg_inv([CInv(t=MSG_WTX, h=wtxid) for wtxid in range(MAX_PEER_TX_ANNOUNCEMENTS + 1)]))
+ peer.wait_until(lambda: peer.tx_getdata_count == MAX_PEER_TX_ANNOUNCEMENTS)
+ peer.sync_with_ping()
+ with p2p_lock:
+ assert_equal(peer.tx_getdata_count, MAX_PEER_TX_ANNOUNCEMENTS)
def test_spurious_notfound(self):
self.log.info('Check that spurious notfound is ignored')
self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(MSG_TX, 1)]))
def run_test(self):
- # Setup the p2p connections
- self.peers = []
- for node in self.nodes:
- for _ in range(NUM_INBOUND):
- self.peers.append(node.add_p2p_connection(TestP2PConn()))
-
- self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
-
+ # Run tests without mocktime that only need one peer-connection first, to avoid restarting the nodes
+ self.test_expiry_fallback()
+ self.test_disconnect_fallback()
+ self.test_notfound_fallback()
+ self.test_preferred_inv()
+ self.test_large_inv_batch()
self.test_spurious_notfound()
- # Test the in-flight max first, because we want no transactions in
- # flight ahead of this test.
- self.test_in_flight_max()
-
- self.test_inv_block()
-
- self.test_tx_requests()
+ # Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when
+ # the next trickle relay event happens.
+ for test in [self.test_in_flight_max, self.test_inv_block, self.test_tx_requests]:
+ self.stop_nodes()
+ self.start_nodes()
+ self.connect_nodes(1, 0)
+ # Setup the p2p connections
+ self.peers = []
+ for node in self.nodes:
+ for _ in range(NUM_INBOUND):
+ self.peers.append(node.add_p2p_connection(TestP2PConn()))
+ self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
+ test()
if __name__ == '__main__':
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 71b0b0f63a..e7a05d8547 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -55,12 +55,11 @@ import time
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script
from test_framework.messages import CBlockHeader, CInv, MSG_BLOCK, msg_block, msg_headers, msg_inv
-from test_framework.mininode import mininode_lock, P2PInterface
+from test_framework.p2p import p2p_lock, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
@@ -199,13 +198,13 @@ class AcceptBlockTest(BitcoinTestFramework):
# 6. Try to get node to request the missing block.
# Poke the node with an inv for block at height 3 and see if that
# triggers a getdata on block 2 (it should if block 2 is missing).
- with mininode_lock:
+ with p2p_lock:
# Clear state so we can check the getdata request
test_node.last_message.pop("getdata", None)
test_node.send_message(msg_inv([CInv(MSG_BLOCK, block_h3.sha256)]))
test_node.sync_with_ping()
- with mininode_lock:
+ with p2p_lock:
getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block
@@ -284,7 +283,7 @@ class AcceptBlockTest(BitcoinTestFramework):
test_node.wait_for_disconnect()
# 9. Connect node1 to node0 and ensure it is able to sync
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks([self.nodes[0], self.nodes[1]])
self.log.info("Successfully synced nodes 1 and 0")
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 7c70f30ca3..f965677408 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -22,6 +22,17 @@ from decimal import Decimal
import http.client
import subprocess
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+ TIME_GENESIS_BLOCK,
+)
+from test_framework.messages import (
+ CBlockHeader,
+ FromHex,
+ msg_block,
+)
+from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -32,17 +43,6 @@ from test_framework.util import (
assert_is_hex_string,
assert_is_hash_string,
)
-from test_framework.blocktools import (
- create_block,
- create_coinbase,
- TIME_GENESIS_BLOCK,
-)
-from test_framework.messages import (
- msg_block,
-)
-from test_framework.mininode import (
- P2PInterface,
-)
class BlockchainTest(BitcoinTestFramework):
@@ -146,7 +146,19 @@ class BlockchainTest(BitcoinTestFramework):
'possible': True,
},
},
- 'active': False}
+ 'active': False
+ },
+ 'taproot': {
+ 'type': 'bip9',
+ 'bip9': {
+ 'status': 'active',
+ 'start_time': -1,
+ 'timeout': 9223372036854775807,
+ 'since': 0
+ },
+ 'height': 0,
+ 'active': True
+ }
})
def _test_getchaintxstats(self):
@@ -280,6 +292,14 @@ class BlockchainTest(BitcoinTestFramework):
assert isinstance(int(header['versionHex'], 16), int)
assert isinstance(header['difficulty'], Decimal)
+ # Test with verbose=False, which should return the header as hex.
+ header_hex = node.getblockheader(blockhash=besthash, verbose=False)
+ assert_is_hex_string(header_hex)
+
+ header = FromHex(CBlockHeader(), header_hex)
+ header.calc_sha256()
+ assert_equal(header.hash, besthash)
+
def _test_getdifficulty(self):
difficulty = self.nodes[0].getdifficulty()
# 1 hash in 2 should be valid, so difficulty should be 1/2**31
@@ -309,7 +329,7 @@ class BlockchainTest(BitcoinTestFramework):
def _test_waitforblockheight(self):
self.log.info("Test waitforblockheight")
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
current_height = node.getblock(node.getbestblockhash())['height']
@@ -326,7 +346,7 @@ class BlockchainTest(BitcoinTestFramework):
def solve_and_send_block(prevhash, height, time):
b = create_block(prevhash, create_coinbase(height), time)
b.solve()
- node.p2p.send_and_ping(msg_block(b))
+ peer.send_and_ping(msg_block(b))
return b
b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 3c81a4a4e2..f19c60dc36 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -129,7 +129,8 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
try:
node1.loadwallet('wmulti')
except JSONRPCException as e:
- if e.error['code'] == -18 and 'Wallet wmulti not found' in e.error['message']:
+ path = os.path.join(self.options.tmpdir, "node1", "regtest", "wallets", "wmulti")
+ if e.error['code'] == -18 and "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path) in e.error['message']:
node1.createwallet(wallet_name='wmulti', disable_private_keys=True)
else:
raise
diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py
index 9a21998d11..adcd8a7d4c 100755
--- a/test/functional/rpc_deprecated.py
+++ b/test/functional/rpc_deprecated.py
@@ -4,13 +4,13 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of RPC calls."""
from test_framework.test_framework import BitcoinTestFramework
-# from test_framework.util import assert_raises_rpc_error
+from test_framework.util import assert_raises_rpc_error, find_vout_for_address
class DeprecatedRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
- self.extra_args = [[], []]
+ self.extra_args = [[], ['-deprecatedrpc=bumpfee']]
def run_test(self):
# This test should be used to verify correct behaviour of deprecated
@@ -23,7 +23,38 @@ class DeprecatedRpcTest(BitcoinTestFramework):
# self.log.info("Test generate RPC")
# assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1)
# self.nodes[1].generate(1)
- self.log.info("No tested deprecated RPC methods")
+
+ if self.is_wallet_compiled():
+ self.log.info("Test bumpfee RPC")
+ self.nodes[0].generate(101)
+ self.nodes[0].createwallet(wallet_name='nopriv', disable_private_keys=True)
+ noprivs0 = self.nodes[0].get_wallet_rpc('nopriv')
+ w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ self.nodes[1].createwallet(wallet_name='nopriv', disable_private_keys=True)
+ noprivs1 = self.nodes[1].get_wallet_rpc('nopriv')
+
+ address = w0.getnewaddress()
+ desc = w0.getaddressinfo(address)['desc']
+ change_addr = w0.getrawchangeaddress()
+ change_desc = w0.getaddressinfo(change_addr)['desc']
+ txid = w0.sendtoaddress(address=address, amount=10)
+ vout = find_vout_for_address(w0, txid, address)
+ self.nodes[0].generate(1)
+ rawtx = w0.createrawtransaction([{'txid': txid, 'vout': vout}], {w0.getnewaddress(): 5}, 0, True)
+ rawtx = w0.fundrawtransaction(rawtx, {'changeAddress': change_addr})
+ signed_tx = w0.signrawtransactionwithwallet(rawtx['hex'])['hex']
+
+ noprivs0.importmulti([{'desc': desc, 'timestamp': 0}, {'desc': change_desc, 'timestamp': 0, 'internal': True}])
+ noprivs1.importmulti([{'desc': desc, 'timestamp': 0}, {'desc': change_desc, 'timestamp': 0, 'internal': True}])
+
+ txid = w0.sendrawtransaction(signed_tx)
+ self.sync_all()
+
+ assert_raises_rpc_error(-32, 'Using bumpfee with wallets that have private keys disabled is deprecated. Use psbtbumpfee instead or restart bitcoind with -deprecatedrpc=bumpfee. This functionality will be removed in 0.22', noprivs0.bumpfee, txid)
+ bumped_psbt = noprivs1.bumpfee(txid)
+ assert 'psbt' in bumped_psbt
+ else:
+ self.log.info("No tested deprecated RPC methods")
if __name__ == '__main__':
DeprecatedRpcTest().main()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 2a0971b808..167c4671ef 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -12,7 +12,6 @@ from test_framework.util import (
assert_greater_than,
assert_greater_than_or_equal,
assert_raises_rpc_error,
- connect_nodes,
count_bytes,
find_vout_for_address,
)
@@ -38,10 +37,10 @@ class RawTransactionsTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[0], 3)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
+ self.connect_nodes(0, 3)
def run_test(self):
self.log.info("Connect nodes, set fees, generate blocks, and sync")
@@ -224,7 +223,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
- assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ assert_raises_rpc_error(-5, "Change address must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
def test_valid_change_address(self):
self.log.info("Test fundrawtxn with a provided change address")
@@ -667,7 +666,7 @@ class RawTransactionsTest(BitcoinTestFramework):
result = self.nodes[3].fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee)
result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
- assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
+ assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py
new file mode 100755
index 0000000000..e55f2e6d12
--- /dev/null
+++ b/test/functional/rpc_generate.py
@@ -0,0 +1,36 @@
+#!/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 generate RPC."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
+
+class RPCGenerateTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ message = (
+ "generate\n"
+ "has been replaced by the -generate "
+ "cli option. Refer to -help for more information."
+ )
+
+ self.log.info("Test rpc generate raises with message to use cli option")
+ assert_raises_rpc_error(-32601, message, self.nodes[0].rpc.generate)
+
+ self.log.info("Test rpc generate help prints message to use cli option")
+ assert_equal(message, self.nodes[0].help("generate"))
+
+ self.log.info("Test rpc generate is a hidden command not discoverable in general help")
+ assert message not in self.nodes[0].help()
+
+
+if __name__ == "__main__":
+ RPCGenerateTest().main()
diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py
index 8fa36445cd..c3c3622cf9 100755
--- a/test/functional/rpc_getblockfilter.py
+++ b/test/functional/rpc_getblockfilter.py
@@ -7,7 +7,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal, assert_is_hex_string, assert_raises_rpc_error,
- connect_nodes, disconnect_nodes
)
FILTER_TYPES = ["basic"]
@@ -20,7 +19,7 @@ class GetBlockFilterTest(BitcoinTestFramework):
def run_test(self):
# Create two chains by disconnecting nodes 0 & 1, mining, then reconnecting
- disconnect_nodes(self.nodes[0], 1)
+ self.disconnect_nodes(0, 1)
self.nodes[0].generate(3)
self.nodes[1].generate(4)
@@ -29,7 +28,7 @@ class GetBlockFilterTest(BitcoinTestFramework):
chain0_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
# Reorg node 0 to a new chain
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks()
assert_equal(self.nodes[0].getblockcount(), 4)
diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py
index 977dc805ef..ea064f9763 100755
--- a/test/functional/rpc_getdescriptorinfo.py
+++ b/test/functional/rpc_getdescriptorinfo.py
@@ -17,6 +17,7 @@ class DescriptorTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-disablewallet"]]
+ self.wallet_names = []
def test_desc(self, desc, isrange, issolvable, hasprivatekeys):
info = self.nodes[0].getdescriptorinfo(desc)
diff --git a/test/functional/rpc_getpeerinfo_banscore_deprecation.py b/test/functional/rpc_getpeerinfo_deprecation.py
index b830248e1e..340a66e12f 100755
--- a/test/functional/rpc_getpeerinfo_banscore_deprecation.py
+++ b/test/functional/rpc_getpeerinfo_deprecation.py
@@ -2,23 +2,37 @@
# 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 deprecation of getpeerinfo RPC banscore field."""
+"""Test deprecation of getpeerinfo RPC fields."""
from test_framework.test_framework import BitcoinTestFramework
-class GetpeerinfoBanscoreDeprecationTest(BitcoinTestFramework):
+class GetpeerinfoDeprecationTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [[], ["-deprecatedrpc=banscore"]]
def run_test(self):
+ self.test_banscore_deprecation()
+ self.test_addnode_deprecation()
+
+ def test_banscore_deprecation(self):
self.log.info("Test getpeerinfo by default no longer returns a banscore field")
assert "banscore" not in self.nodes[0].getpeerinfo()[0].keys()
self.log.info("Test getpeerinfo returns banscore with -deprecatedrpc=banscore")
assert "banscore" in self.nodes[1].getpeerinfo()[0].keys()
+ def test_addnode_deprecation(self):
+ self.restart_node(1, ["-deprecatedrpc=getpeerinfo_addnode"])
+ self.connect_nodes(0, 1)
+
+ self.log.info("Test getpeerinfo by default no longer returns an addnode field")
+ assert "addnode" not in self.nodes[0].getpeerinfo()[0].keys()
+
+ self.log.info("Test getpeerinfo returns addnode with -deprecatedrpc=addnode")
+ assert "addnode" in self.nodes[1].getpeerinfo()[0].keys()
+
if __name__ == "__main__":
- GetpeerinfoBanscoreDeprecationTest().main()
+ GetpeerinfoDeprecationTest().main()
diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py
index 1fdc134f97..f884b8d293 100755
--- a/test/functional/rpc_invalidateblock.py
+++ b/test/functional/rpc_invalidateblock.py
@@ -8,8 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR
from test_framework.util import (
assert_equal,
- connect_nodes,
- wait_until,
)
@@ -33,7 +31,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getblockcount(), 6)
self.log.info("Connect nodes to force a reorg")
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks(self.nodes[0:2])
assert_equal(self.nodes[0].getblockcount(), 6)
badhash = self.nodes[1].getblockhash(2)
@@ -44,7 +42,7 @@ class InvalidateTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbestblockhash(), besthash_n0)
self.log.info("Make sure we won't reorg to a lower work chain:")
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
self.log.info("Sync node 2 to node 1 so both have 6 blocks")
self.sync_blocks(self.nodes[1:3])
assert_equal(self.nodes[2].getblockcount(), 6)
@@ -57,9 +55,9 @@ class InvalidateTest(BitcoinTestFramework):
self.log.info("..and then mine a block")
self.nodes[2].generatetoaddress(1, self.nodes[2].get_deterministic_priv_key().address)
self.log.info("Verify all nodes are at the right height")
- wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5)
- wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5)
- wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5)
+ self.wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5)
+ self.wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5)
+ self.wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5)
self.log.info("Verify that we reconsider all ancestors as well")
blocks = self.nodes[1].generatetodescriptor(10, ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR)
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index c8517d719e..0493ceeb64 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -27,8 +27,8 @@ class RpcMiscTest(BitcoinTestFramework):
self.log.info("test CHECK_NONFATAL")
assert_raises_rpc_error(
-1,
- "Internal bug detected: 'request.params.size() != 100'",
- lambda: node.echo(*[0] * 100),
+ 'Internal bug detected: \'request.params[9].get_str() != "trigger_internal_bug"\'',
+ lambda: node.echo(arg9='trigger_internal_bug'),
)
self.log.info("test getmemoryinfo")
@@ -61,6 +61,34 @@ class RpcMiscTest(BitcoinTestFramework):
node.logging(include=['qt'])
assert_equal(node.logging()['qt'], True)
+ 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.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values()))
+
+ # Returns a list of all running indices by default
+ assert_equal(
+ node.getindexinfo(),
+ {
+ "txindex": {"synced": True, "best_block_height": 200},
+ "basic block filter index": {"synced": True, "best_block_height": 200}
+ }
+ )
+
+ # Specifying an index by name returns only the status of that index
+ assert_equal(
+ node.getindexinfo("txindex"),
+ {
+ "txindex": {"synced": True, "best_block_height": 200},
+ }
+ )
+
+ # Specifying an unknown index name returns an empty result
+ assert_equal(node.getindexinfo("foo"), {})
+
if __name__ == '__main__':
RpcMiscTest().main()
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 3336246c8b..71e8e1b22a 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -8,24 +8,23 @@ Tests correspond to code in rpc/net.cpp.
"""
from decimal import Decimal
+from itertools import product
+import time
+from test_framework.p2p import P2PInterface
+import test_framework.messages
+from test_framework.messages import (
+ NODE_NETWORK,
+ NODE_WITNESS,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
+ assert_approx,
assert_equal,
assert_greater_than_or_equal,
assert_greater_than,
assert_raises_rpc_error,
- connect_nodes,
p2p_port,
- wait_until,
-)
-from test_framework.mininode import P2PInterface
-import test_framework.messages
-from test_framework.messages import (
- CAddress,
- msg_addr,
- NODE_NETWORK,
- NODE_WITNESS,
)
@@ -50,25 +49,28 @@ class NetTest(BitcoinTestFramework):
self.supports_cli = False
def run_test(self):
- self.log.info('Get out of IBD for the minfeefilter test')
- self.nodes[0].generate(1)
- self.log.info('Connect nodes both way')
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 0)
-
- self._test_connection_count()
- self._test_getnettotals()
- self._test_getnetworkinfo()
- self._test_getaddednodeinfo()
- self._test_getpeerinfo()
+ # Get out of IBD for the minfeefilter and getpeerinfo tests.
+ self.nodes[0].generate(101)
+ # Connect nodes both ways.
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 0)
+ self.sync_all()
+
+ self.test_connection_count()
+ self.test_getpeerinfo()
+ self.test_getnettotals()
+ self.test_getnetworkinfo()
+ self.test_getaddednodeinfo()
self.test_service_flags()
- self._test_getnodeaddresses()
+ self.test_getnodeaddresses()
- def _test_connection_count(self):
- # connect_nodes connects each node to the other
+ def test_connection_count(self):
+ self.log.info("Test getconnectioncount")
+ # After using `connect_nodes` to connect nodes 0 and 1 to each other.
assert_equal(self.nodes[0].getconnectioncount(), 2)
- def _test_getnettotals(self):
+ def test_getnettotals(self):
+ self.log.info("Test getnettotals")
# getnettotals totalbytesrecv and totalbytessent should be
# consistent with getpeerinfo. Since the RPC calls are not atomic,
# and messages might have been recvd or sent between RPC calls, call
@@ -90,39 +92,47 @@ class NetTest(BitcoinTestFramework):
# the bytes sent/received should change
# note ping and pong are 32 bytes each
self.nodes[0].ping()
- wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
- wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
peer_info_after_ping = self.nodes[0].getpeerinfo()
for before, after in zip(peer_info, peer_info_after_ping):
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
- def _test_getnetworkinfo(self):
- assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
- assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ def test_getnetworkinfo(self):
+ self.log.info("Test getnetworkinfo")
+ info = self.nodes[0].getnetworkinfo()
+ assert_equal(info['networkactive'], True)
+ assert_equal(info['connections'], 2)
+ assert_equal(info['connections_in'], 1)
+ assert_equal(info['connections_out'], 1)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.nodes[0].setnetworkactive(state=False)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
# Wait a bit for all sockets to close
- wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
+ self.wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
self.nodes[0].setnetworkactive(state=True)
- self.log.info('Connect nodes both way')
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 0)
+ # Connect nodes both ways.
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 0)
- assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
- assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ info = self.nodes[0].getnetworkinfo()
+ assert_equal(info['networkactive'], True)
+ assert_equal(info['connections'], 2)
+ assert_equal(info['connections_in'], 1)
+ assert_equal(info['connections_out'], 1)
# check the `servicesnames` field
network_info = [node.getnetworkinfo() for node in self.nodes]
for info in network_info:
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
- def _test_getaddednodeinfo(self):
+ def test_getaddednodeinfo(self):
+ self.log.info("Test getaddednodeinfo")
assert_equal(self.nodes[0].getaddednodeinfo(), [])
# add a node (node2) to node0
ip_port = "127.0.0.1:{}".format(p2p_port(2))
@@ -131,11 +141,30 @@ class NetTest(BitcoinTestFramework):
added_nodes = self.nodes[0].getaddednodeinfo(ip_port)
assert_equal(len(added_nodes), 1)
assert_equal(added_nodes[0]['addednode'], ip_port)
+ # check that node cannot be added again
+ assert_raises_rpc_error(-23, "Node already added", self.nodes[0].addnode, node=ip_port, command='add')
+ # check that node can be removed
+ self.nodes[0].addnode(node=ip_port, command='remove')
+ assert_equal(self.nodes[0].getaddednodeinfo(), [])
+ # check that trying to remove the node again returns an error
+ assert_raises_rpc_error(-24, "Node could not be removed", self.nodes[0].addnode, node=ip_port, command='remove')
# check that a non-existent node returns an error
assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1')
- def _test_getpeerinfo(self):
+ def test_getpeerinfo(self):
+ self.log.info("Test getpeerinfo")
+ # Create a few getpeerinfo last_block/last_transaction values.
+ if self.is_wallet_compiled():
+ self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
+ self.nodes[1].generate(1)
+ self.sync_all()
+ time_now = int(time.time())
peer_info = [x.getpeerinfo() for x in self.nodes]
+ # Verify last_block and last_transaction keys/values.
+ for node, peer, field in product(range(self.num_nodes), range(2), ['last_block', 'last_transaction']):
+ assert field in peer_info[node][peer].keys()
+ if peer_info[node][peer][field] != 0:
+ assert_approx(peer_info[node][peer][field], time_now, vspan=60)
# check both sides of bidirectional connection between nodes
# the address bound to on one side will be the source address for the other node
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr'])
@@ -146,38 +175,50 @@ class NetTest(BitcoinTestFramework):
for info in peer_info:
assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"])
+ assert_equal(peer_info[0][0]['connection_type'], 'inbound')
+ assert_equal(peer_info[0][1]['connection_type'], 'manual')
+
+ assert_equal(peer_info[1][0]['connection_type'], 'manual')
+ assert_equal(peer_info[1][1]['connection_type'], 'inbound')
+
def test_service_flags(self):
+ self.log.info("Test service flags")
self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63))
assert_equal(['UNKNOWN[2^4]', 'UNKNOWN[2^63]'], self.nodes[0].getpeerinfo()[-1]['servicesnames'])
self.nodes[0].disconnect_p2ps()
- def _test_getnodeaddresses(self):
+ def test_getnodeaddresses(self):
+ self.log.info("Test getnodeaddresses")
self.nodes[0].add_p2p_connection(P2PInterface())
- # send some addresses to the node via the p2p message addr
- msg = msg_addr()
+ # 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.
imported_addrs = []
- for i in range(256):
- a = "123.123.123.{}".format(i)
+ for i in range(10000):
+ first_octet = i >> 8
+ second_octet = i % 256
+ a = "{}.{}.1.1".format(first_octet, second_octet)
imported_addrs.append(a)
- addr = CAddress()
- addr.time = 100000000
- addr.nServices = NODE_NETWORK | NODE_WITNESS
- addr.ip = a
- addr.port = 8333
- msg.addrs.append(addr)
- self.nodes[0].p2p.send_and_ping(msg)
-
- # obtain addresses via rpc call and check they were ones sent in before
- REQUEST_COUNT = 10
- node_addresses = self.nodes[0].getnodeaddresses(REQUEST_COUNT)
- assert_equal(len(node_addresses), REQUEST_COUNT)
+ 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)
+ 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_greater_than(a["time"], 1527811200) # 1st June 2018
assert_equal(a["services"], NODE_NETWORK | NODE_WITNESS)
assert a["address"] in imported_addrs
assert_equal(a["port"], 8333)
+ node_addresses = self.nodes[0].getnodeaddresses(1)
+ assert_equal(len(node_addresses), 1)
+
assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
# addrman's size cannot be known reliably after insertion, as hash collisions may occur
diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py
index 8386e47411..04d55b103f 100755
--- a/test/functional/rpc_preciousblock.py
+++ b/test/functional/rpc_preciousblock.py
@@ -7,7 +7,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
)
def unidirectional_node_sync_via_rpc(node_src, node_dest):
@@ -61,7 +60,7 @@ class PreciousTest(BitcoinTestFramework):
self.log.info("Connect nodes and check no reorg occurs")
# Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
node_sync_via_rpc(self.nodes[0:2])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
assert_equal(self.nodes[0].getbestblockhash(), hashC)
assert_equal(self.nodes[1].getbestblockhash(), hashG)
self.log.info("Make Node0 prefer block G")
@@ -98,8 +97,8 @@ class PreciousTest(BitcoinTestFramework):
hashL = self.nodes[2].getbestblockhash()
self.log.info("Connect nodes and check no reorg occurs")
node_sync_via_rpc(self.nodes[1:3])
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
assert_equal(self.nodes[0].getbestblockhash(), hashH)
assert_equal(self.nodes[1].getbestblockhash(), hashH)
assert_equal(self.nodes[2].getbestblockhash(), hashL)
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index f7f23bc8f4..32dc2f8644 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -12,8 +12,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- connect_nodes,
- disconnect_nodes,
find_output,
)
@@ -30,7 +28,7 @@ class PSBTTest(BitcoinTestFramework):
self.num_nodes = 3
self.extra_args = [
["-walletrbf=1"],
- ["-walletrbf=0"],
+ ["-walletrbf=0", "-changetype=legacy"],
[]
]
self.supports_cli = False
@@ -46,7 +44,7 @@ class PSBTTest(BitcoinTestFramework):
# Disconnect offline node from others
# Topology of test network is linear, so this one call is enough
- disconnect_nodes(offline_node, 1)
+ self.disconnect_nodes(0, 1)
# Create watchonly on online_node
online_node.createwallet(wallet_name='wonline', disable_private_keys=True)
@@ -80,8 +78,16 @@ class PSBTTest(BitcoinTestFramework):
wonline.unloadwallet()
# Reconnect
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
+
+ def assert_change_type(self, psbtx, expected_type):
+ """Assert that the given PSBT has a change output with the given type."""
+
+ # The decodepsbt RPC is stateless and independent of any settings, we can always just call it on the first node
+ decoded_psbt = self.nodes[0].decodepsbt(psbtx["psbt"])
+ changepos = psbtx["changepos"]
+ assert_equal(decoded_psbt["tx"]["vout"][changepos]["scriptPubKey"]["type"], expected_type)
def run_test(self):
# Create and fund a raw tx for sending 10 BTC
@@ -94,6 +100,9 @@ class PSBTTest(BitcoinTestFramework):
psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt']
assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2)
+ # Inputs argument can be null
+ self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress():10})
+
# Node 1 should not be able to add anything to it but still return the psbtx same as before
psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt']
assert_equal(psbtx1, psbtx)
@@ -103,7 +112,16 @@ class PSBTTest(BitcoinTestFramework):
final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex']
self.nodes[0].sendrawtransaction(final_tx)
- # Get pubkeys
+ # Manually selected inputs can be locked:
+ assert_equal(len(self.nodes[0].listlockunspent()), 0)
+ utxo1 = self.nodes[0].listunspent()[0]
+ psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():1}, 0,{"lockUnspents": True})["psbt"]
+ assert_equal(len(self.nodes[0].listlockunspent()), 1)
+
+ # Locks are ignored for manually selected inputs
+ self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():1}, 0)
+
+ # Create p2sh, p2wpkh, and p2wsh addresses
pubkey0 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey']
pubkey1 = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey']
pubkey2 = self.nodes[2].getaddressinfo(self.nodes[2].getnewaddress())['pubkey']
@@ -172,8 +190,8 @@ class PSBTTest(BitcoinTestFramework):
# feeRate of 10 BTC / KB produces a total fee well above -maxtxfee
# previously this was silently capped at -maxtxfee
- assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 10, "add_inputs": True})
- assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():1}, 0, {"feeRate": 10, "add_inputs": False})
+ assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 10, "add_inputs": True})
+ assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():1}, 0, {"feeRate": 10, "add_inputs": False})
# partially sign multisig things with node 1
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs={self.nodes[1].getnewaddress():29.99}, options={'changeAddress': self.nodes[1].getrawchangeaddress()})['psbt']
@@ -289,6 +307,21 @@ class PSBTTest(BitcoinTestFramework):
# when attempting BnB coin selection
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
+ # Make sure the wallet's change type is respected by default
+ small_output = {self.nodes[0].getnewaddress():0.1}
+ psbtx_native = self.nodes[0].walletcreatefundedpsbt([], [small_output])
+ self.assert_change_type(psbtx_native, "witness_v0_keyhash")
+ psbtx_legacy = self.nodes[1].walletcreatefundedpsbt([], [small_output])
+ self.assert_change_type(psbtx_legacy, "pubkeyhash")
+
+ # Make sure the change type of the wallet can also be overwritten
+ psbtx_np2wkh = self.nodes[1].walletcreatefundedpsbt([], [small_output], 0, {"change_type":"p2sh-segwit"})
+ self.assert_change_type(psbtx_np2wkh, "scripthash")
+
+ # Make sure the change type cannot be specified if a change address is given
+ invalid_options = {"change_type":"legacy","changeAddress":self.nodes[0].getnewaddress()}
+ assert_raises_rpc_error(-8, "both change address and address type options", self.nodes[0].walletcreatefundedpsbt, [], [small_output], 0, invalid_options)
+
# Regression test for 14473 (mishandling of already-signed witness transaction):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], 0, {"add_inputs": True})
complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"])
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 23b5e647d6..326495843f 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -20,7 +20,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
hex_str_to_bytes,
)
@@ -60,7 +59,7 @@ class RawTransactionsTest(BitcoinTestFramework):
def setup_network(self):
super().setup_network()
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
def run_test(self):
self.log.info('prepare some coins for multiple *rawtransaction commands')
@@ -96,7 +95,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844'}], {})
assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {})
assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {})
- assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {})
assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {})
# Test `createrawtransaction` invalid `outputs`
@@ -456,9 +455,9 @@ class RawTransactionsTest(BitcoinTestFramework):
# Thus, testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0]
assert_equal(testres['allowed'], False)
- assert_equal(testres['reject-reason'], 'absurdly-high-fee')
+ assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
- assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
+ assert_raises_rpc_error(-25, 'Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)', self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
# and the following calls should both succeed
testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']])[0]
assert_equal(testres['allowed'], True)
@@ -480,9 +479,9 @@ class RawTransactionsTest(BitcoinTestFramework):
# Thus, testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']])[0]
assert_equal(testres['allowed'], False)
- assert_equal(testres['reject-reason'], 'absurdly-high-fee')
+ assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
- assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'])
+ assert_raises_rpc_error(-25, 'Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)', self.nodes[2].sendrawtransaction, rawTxSigned['hex'])
# and the following calls should both succeed
testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.20000000')[0]
assert_equal(testres['allowed'], True)
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
index 1cc1fb164b..bc48449084 100755
--- a/test/functional/rpc_setban.py
+++ b/test/functional/rpc_setban.py
@@ -6,7 +6,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
- connect_nodes,
p2p_port
)
@@ -18,7 +17,7 @@ class SetBanTests(BitcoinTestFramework):
def run_test(self):
# Node 0 connects to Node 1, check that the noban permission is not granted
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
assert(not 'noban' in peerinfo['permissions'])
@@ -32,14 +31,14 @@ class SetBanTests(BitcoinTestFramework):
# However, node 0 should be able to reconnect if it has noban permission
self.restart_node(1, ['-whitelist=127.0.0.1'])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
assert('noban' in peerinfo['permissions'])
# If we remove the ban, Node 0 should be able to reconnect even without noban permission
self.nodes[1].setban("127.0.0.1", "remove")
self.restart_node(1, [])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
assert(not 'noban' in peerinfo['permissions'])
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 3d08202724..704b65c060 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -198,10 +198,30 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert_equal(spending_tx_signed['complete'], True)
self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+ def OP_1NEGATE_test(self):
+ self.log.info("Test OP_1NEGATE (0x4f) satisfies BIP62 minimal push standardness rule")
+ hex_str = (
+ "0200000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "FFFFFFFF00000000044F024F9CFDFFFFFF01F0B9F5050000000023210277777777"
+ "77777777777777777777777777777777777777777777777777777777AC66030000"
+ )
+ prev_txs = [
+ {
+ "txid": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ "vout": 0,
+ "scriptPubKey": "A914AE44AB6E9AA0B71F1CD2B453B69340E9BFBAEF6087",
+ "redeemScript": "4F9C",
+ "amount": 1,
+ }
+ ]
+ txn = self.nodes[0].signrawtransactionwithwallet(hex_str, prev_txs)
+ assert txn["complete"]
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
self.witness_script_test()
+ self.OP_1NEGATE_test()
self.test_with_lock_outputs()
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index ca8be42d3b..93fb62c5d6 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -6,41 +6,31 @@
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, connect_nodes
+from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.wallet import MiniWallet
+
class MerkleBlockTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 2
self.setup_clean_chain = True
- # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing
- self.extra_args = [[], [], [], ["-txindex"]]
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
- def setup_network(self):
- self.setup_nodes()
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[0], 3)
-
- self.sync_all()
+ self.extra_args = [
+ [],
+ ["-txindex"],
+ ]
def run_test(self):
- self.log.info("Mining blocks...")
- self.nodes[0].generate(105)
+ 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.sync_all()
chain_height = self.nodes[1].getblockcount()
assert_equal(chain_height, 105)
- assert_equal(self.nodes[1].getbalance(), 0)
- assert_equal(self.nodes[2].getbalance(), 0)
-
- node0utxos = self.nodes[0].listunspent(1)
- tx1 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
- txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx1)["hex"])
- tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
- txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx2)["hex"])
+
+ txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
+ txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
# This will raise an exception because the transaction is not yet in a block
assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])
@@ -53,50 +43,54 @@ class MerkleBlockTest(BitcoinTestFramework):
txlist.append(blocktxn[1])
txlist.append(blocktxn[2])
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1])), [txid1])
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist)
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2], blockhash)), txlist)
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1])), [txid1])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2])), txlist)
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2], blockhash)), txlist)
- txin_spent = self.nodes[1].listunspent(1).pop()
- tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 49.98})
- txid3 = self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransactionwithwallet(tx3)["hex"])
+ txin_spent = miniwallet.get_utxo() # Get the change from txid2
+ tx3 = miniwallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=txin_spent)
+ txid3 = tx3['txid']
self.nodes[0].generate(1)
self.sync_all()
txid_spent = txin_spent["txid"]
- txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2
+ txid_unspent = txid1 # Input was change from txid2, so txid1 should be unspent
# Invalid txids
- assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["00000000000000000000000000000000"], blockhash)
- assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash)
+ assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["00000000000000000000000000000000"], blockhash)
+ assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash)
# Invalid blockhashes
- assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
- assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
+ assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000")
# We can't find the block from a fully-spent tx
- assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid_spent])
# We can get the proof if we specify the block
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid_spent], blockhash)), [txid_spent])
# We can't get the proof if we specify a non-existent block
- assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000")
# We can get the proof if the transaction is unspent
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid_unspent])), [txid_unspent])
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
- assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2]))), sorted(txlist))
- assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1]))), sorted(txlist))
+ assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2]))), sorted(txlist))
+ assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid2, txid1]))), sorted(txlist))
# We can always get a proof if we have a -txindex
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[1].gettxoutproof([txid_spent])), [txid_spent])
# We can't get a proof if we specify transactions from different blocks
- assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
+ assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3])
+ # Test empty list
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [])
+ # Test duplicate txid
+ assert_raises_rpc_error(-8, 'Invalid parameter, duplicated txid', self.nodes[0].gettxoutproof, [txid1, txid1])
# Now we'll try tweaking a proof.
- proof = self.nodes[3].gettxoutproof([txid1, txid2])
+ proof = self.nodes[1].gettxoutproof([txid1, txid2])
assert txid1 in self.nodes[0].verifytxoutproof(proof)
assert txid2 in self.nodes[1].verifytxoutproof(proof)
tweaked_proof = FromHex(CMerkleBlock(), proof)
# Make sure that our serialization/deserialization is working
- assert txid1 in self.nodes[2].verifytxoutproof(ToHex(tweaked_proof))
+ assert txid1 in self.nodes[0].verifytxoutproof(ToHex(tweaked_proof))
# Check to see if we can go up the merkle tree and pass this off as a
# single-transaction block
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 9506b63f82..360962b8da 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -2,17 +2,17 @@
# Copyright (c) 2016-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.
-"""Encode and decode BASE58, P2PKH and P2SH addresses."""
+"""Encode and decode Bitcoin addresses.
+
+- base58 P2PKH and P2SH addresses.
+- bech32 segwit v0 P2WPKH and P2WSH addresses."""
import enum
import unittest
from .script import hash256, hash160, sha256, CScript, OP_0
-from .util import hex_str_to_bytes
-
-from . import segwit_addr
-
-from test_framework.util import assert_equal
+from .segwit_addr import encode_segwit_address
+from .util import assert_equal, hex_str_to_bytes
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
@@ -35,7 +35,7 @@ def byte_to_base58(b, version):
str = chr(version).encode('latin-1').hex() + str
checksum = hash256(hex_str_to_bytes(str)).hex()
str += checksum[:8]
- value = int('0x'+str,0)
+ value = int('0x' + str, 0)
while value > 0:
result = chars[value % 58] + result
value //= 58
@@ -45,7 +45,10 @@ def byte_to_base58(b, version):
return result
-def base58_to_byte(s, verify_checksum=True):
+def base58_to_byte(s):
+ """Converts a base58-encoded string to its data and version.
+
+ Throws if the base58 checksum is invalid."""
if not s:
return b''
n = 0
@@ -65,66 +68,67 @@ def base58_to_byte(s, verify_checksum=True):
else:
break
res = b'\x00' * pad + res
- if verify_checksum:
- assert_equal(hash256(res[:-4])[:4], res[-4:])
+
+ # Assert if the checksum is invalid
+ assert_equal(hash256(res[:-4])[:4], res[-4:])
return res[1:-4], int(res[0])
-def keyhash_to_p2pkh(hash, main = False):
+def keyhash_to_p2pkh(hash, main=False):
assert len(hash) == 20
version = 0 if main else 111
return byte_to_base58(hash, version)
-def scripthash_to_p2sh(hash, main = False):
+def scripthash_to_p2sh(hash, main=False):
assert len(hash) == 20
version = 5 if main else 196
return byte_to_base58(hash, version)
-def key_to_p2pkh(key, main = False):
+def key_to_p2pkh(key, main=False):
key = check_key(key)
return keyhash_to_p2pkh(hash160(key), main)
-def script_to_p2sh(script, main = False):
+def script_to_p2sh(script, main=False):
script = check_script(script)
return scripthash_to_p2sh(hash160(script), main)
-def key_to_p2sh_p2wpkh(key, main = False):
+def key_to_p2sh_p2wpkh(key, main=False):
key = check_key(key)
p2shscript = CScript([OP_0, hash160(key)])
return script_to_p2sh(p2shscript, main)
-def program_to_witness(version, program, main = False):
+def program_to_witness(version, program, main=False):
if (type(program) is str):
program = hex_str_to_bytes(program)
assert 0 <= version <= 16
assert 2 <= len(program) <= 40
assert version > 0 or len(program) in [20, 32]
- return segwit_addr.encode("bc" if main else "bcrt", version, program)
+ return encode_segwit_address("bc" if main else "bcrt", version, program)
-def script_to_p2wsh(script, main = False):
+def script_to_p2wsh(script, main=False):
script = check_script(script)
return program_to_witness(0, sha256(script), main)
-def key_to_p2wpkh(key, main = False):
+def key_to_p2wpkh(key, main=False):
key = check_key(key)
return program_to_witness(0, hash160(key), main)
-def script_to_p2sh_p2wsh(script, main = False):
+def script_to_p2sh_p2wsh(script, main=False):
script = check_script(script)
p2shscript = CScript([OP_0, sha256(script)])
return script_to_p2sh(p2shscript, main)
def check_key(key):
if (type(key) is str):
- key = hex_str_to_bytes(key) # Assuming this is hex string
+ key = hex_str_to_bytes(key) # Assuming this is hex string
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
return key
assert False
def check_script(script):
if (type(script) is str):
- script = hex_str_to_bytes(script) # Assuming this is hex string
+ script = hex_str_to_bytes(script) # Assuming this is hex string
if (type(script) is bytes or type(script) is CScript):
return script
assert False
@@ -135,15 +139,15 @@ class TestFrameworkScript(unittest.TestCase):
def check_base58(data, version):
self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 111)
+ check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 111)
+ check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 0)
+ check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 0)
+ check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
diff --git a/test/functional/test_framework/bip340_test_vectors.csv b/test/functional/test_framework/bip340_test_vectors.csv
new file mode 100644
index 0000000000..e068322deb
--- /dev/null
+++ b/test/functional/test_framework/bip340_test_vectors.csv
@@ -0,0 +1,16 @@
+index,secret key,public key,aux_rand,message,signature,verification result,comment
+0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE,
+1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE,
+2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE,
+3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n
+4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE,
+5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve
+6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false
+7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message
+8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value
+9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0
+10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1
+11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve
+12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size
+13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
+14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index afc1995009..0859380d06 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -4,6 +4,10 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
+from binascii import a2b_hex
+import io
+import struct
+import time
import unittest
from .address import (
@@ -43,7 +47,9 @@ from .script import (
from .util import assert_equal
from io import BytesIO
+WITNESS_SCALE_FACTOR = 4
MAX_BLOCK_SIGOPS = 20000
+MAX_BLOCK_SIGOPS_WEIGHT = MAX_BLOCK_SIGOPS * WITNESS_SCALE_FACTOR
# Genesis block time (regtest)
TIME_GENESIS_BLOCK = 1296688602
@@ -51,19 +57,31 @@ TIME_GENESIS_BLOCK = 1296688602
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
+NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
-def create_block(hashprev, coinbase, ntime=None, *, version=1):
+
+def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None):
"""Create a block (with regtest difficulty)."""
block = CBlock()
- block.nVersion = version
- if ntime is None:
- import time
- block.nTime = int(time.time() + 600)
+ if tmpl is None:
+ tmpl = {}
+ block.nVersion = version or tmpl.get('version') or 1
+ block.nTime = ntime or tmpl.get('curtime') or int(time.time() + 600)
+ block.hashPrevBlock = hashprev or int(tmpl['previousblockhash'], 0x10)
+ if tmpl and not tmpl.get('bits') is None:
+ block.nBits = struct.unpack('>I', a2b_hex(tmpl['bits']))[0]
else:
- block.nTime = ntime
- block.hashPrevBlock = hashprev
- block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
+ block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
+ if coinbase is None:
+ coinbase = create_coinbase(height=tmpl['height'])
block.vtx.append(coinbase)
+ if txlist:
+ for tx in txlist:
+ if not hasattr(tx, 'calc_sha256'):
+ txo = CTransaction()
+ txo.deserialize(io.BytesIO(tx))
+ tx = txo
+ block.vtx.append(tx)
block.hashMerkleRoot = block.calc_merkle_root()
block.calc_sha256()
return block
@@ -101,22 +119,31 @@ def script_BIP34_coinbase_height(height):
return CScript([CScriptNum(height)])
-def create_coinbase(height, pubkey=None):
- """Create a coinbase transaction, assuming no miner fees.
+def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
+ """Create a coinbase transaction.
If pubkey is passed in, the coinbase output will be a P2PK output;
- otherwise an anyone-can-spend output."""
+ otherwise an anyone-can-spend output.
+
+ If extra_output_script is given, make a 0-value output to that
+ script. This is useful to pad block weight/sigops as needed. """
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
- if (pubkey is not None):
+ coinbaseoutput.nValue += fees
+ if pubkey is not None:
coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG])
else:
coinbaseoutput.scriptPubKey = CScript([OP_TRUE])
coinbase.vout = [coinbaseoutput]
+ if extra_output_script is not None:
+ coinbaseoutput2 = CTxOut()
+ coinbaseoutput2.nValue = 0
+ coinbaseoutput2.scriptPubKey = extra_output_script
+ coinbase.vout.append(coinbaseoutput2)
coinbase.calc_sha256()
return coinbase
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 912c0ca978..a6bc187985 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Pieter Wuille
+# Copyright (c) 2019-2020 Pieter Wuille
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test-only secp256k1 elliptic curve implementation
@@ -6,24 +6,23 @@
WARNING: This code is slow, uses bad randomness, does not properly protect
keys, and is trivially vulnerable to side channel attacks. Do not use for
anything but tests."""
+import csv
+import hashlib
+import os
import random
+import sys
+import unittest
-def modinv(a, n):
- """Compute the modular inverse of a modulo n
+from .util import modinv
- See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
- """
- t1, t2 = 0, 1
- r1, r2 = n, a
- while r2 != 0:
- q = r1 // r2
- t1, t2 = t2, t1 - q * t2
- r1, r2 = r2, r1 - q * r2
- if r1 > 1:
- return None
- if t1 < 0:
- t1 += n
- return t1
+def TaggedHash(tag, data):
+ ss = hashlib.sha256(tag.encode('utf-8')).digest()
+ ss += ss
+ ss += data
+ return hashlib.sha256(ss).digest()
+
+def xor_bytes(b0, b1):
+ return bytes(x ^ y for (x, y) in zip(b0, b1))
def jacobi_symbol(n, k):
"""Compute the Jacobi symbol of n modulo k
@@ -83,6 +82,10 @@ class EllipticCurve:
inv_3 = (inv_2 * inv) % self.p
return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
+ def has_even_y(self, p1):
+ """Whether the point p1 has an even Y coordinate when expressed in affine coordinates."""
+ return not (p1[2] == 0 or self.affine(p1)[1] & 1)
+
def negate(self, p1):
"""Negate a Jacobian point tuple p1."""
x1, y1, z1 = p1
@@ -101,13 +104,13 @@ class EllipticCurve:
return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
def lift_x(self, x):
- """Given an X coordinate on the curve, return a corresponding affine point."""
+ """Given an X coordinate on the curve, return a corresponding affine point for which the Y coordinate is even."""
x_3 = pow(x, 3, self.p)
v = x_3 + self.a * x + self.b
y = modsqrt(v, self.p)
if y is None:
return None
- return (x, y, 1)
+ return (x, self.p - y if y & 1 else y, 1)
def double(self, p1):
"""Double a Jacobian tuple p1
@@ -212,7 +215,8 @@ class EllipticCurve:
r = self.add(r, p)
return r
-SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7)
+SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
+SECP256K1 = EllipticCurve(SECP256K1_FIELD_SIZE, 0, 7)
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
@@ -236,9 +240,9 @@ class ECPubKey():
x = int.from_bytes(data[1:33], 'big')
if SECP256K1.is_x_coord(x):
p = SECP256K1.lift_x(x)
- # if the oddness of the y co-ord isn't correct, find the other
- # valid y
- if (p[1] & 1) != (data[0] & 1):
+ # Make the Y coordinate odd if required (lift_x always produces
+ # a point with an even Y coordinate).
+ if data[0] & 1:
p = SECP256K1.negate(p)
self.p = p
self.valid = True
@@ -322,6 +326,10 @@ class ECPubKey():
return False
return True
+def generate_privkey():
+ """Generate a valid random 32-byte private key."""
+ return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
+
class ECKey():
"""A secp256k1 private key"""
@@ -339,7 +347,7 @@ class ECKey():
def generate(self, compressed=True):
"""Generate a random private key (compressed or uncompressed)."""
- self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
+ self.set(generate_privkey(), compressed)
def get_bytes(self):
"""Retrieve the 32-byte representation of this key."""
@@ -384,3 +392,161 @@ class ECKey():
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
+
+def compute_xonly_pubkey(key):
+ """Compute an x-only (32 byte) public key from a (32 byte) private key.
+
+ This also returns whether the resulting public key was negated.
+ """
+
+ assert len(key) == 32
+ x = int.from_bytes(key, 'big')
+ if x == 0 or x >= SECP256K1_ORDER:
+ return (None, None)
+ P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, x)]))
+ return (P[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(P))
+
+def tweak_add_privkey(key, tweak):
+ """Tweak a private key (after negating it if needed)."""
+
+ assert len(key) == 32
+ assert len(tweak) == 32
+
+ x = int.from_bytes(key, 'big')
+ if x == 0 or x >= SECP256K1_ORDER:
+ return None
+ if not SECP256K1.has_even_y(SECP256K1.mul([(SECP256K1_G, x)])):
+ x = SECP256K1_ORDER - x
+ t = int.from_bytes(tweak, 'big')
+ if t >= SECP256K1_ORDER:
+ return None
+ x = (x + t) % SECP256K1_ORDER
+ if x == 0:
+ return None
+ return x.to_bytes(32, 'big')
+
+def tweak_add_pubkey(key, tweak):
+ """Tweak a public key and return whether the result had to be negated."""
+
+ assert len(key) == 32
+ assert len(tweak) == 32
+
+ x_coord = int.from_bytes(key, 'big')
+ if x_coord >= SECP256K1_FIELD_SIZE:
+ return None
+ P = SECP256K1.lift_x(x_coord)
+ if P is None:
+ return None
+ t = int.from_bytes(tweak, 'big')
+ if t >= SECP256K1_ORDER:
+ return None
+ Q = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, t), (P, 1)]))
+ if Q is None:
+ return None
+ return (Q[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(Q))
+
+def verify_schnorr(key, sig, msg):
+ """Verify a Schnorr signature (see BIP 340).
+
+ - key is a 32-byte xonly pubkey (computed using compute_xonly_pubkey).
+ - sig is a 64-byte Schnorr signature
+ - msg is a 32-byte message
+ """
+ assert len(key) == 32
+ assert len(msg) == 32
+ assert len(sig) == 64
+
+ x_coord = int.from_bytes(key, 'big')
+ if x_coord == 0 or x_coord >= SECP256K1_FIELD_SIZE:
+ return False
+ P = SECP256K1.lift_x(x_coord)
+ if P is None:
+ return False
+ r = int.from_bytes(sig[0:32], 'big')
+ if r >= SECP256K1_FIELD_SIZE:
+ return False
+ s = int.from_bytes(sig[32:64], 'big')
+ if s >= SECP256K1_ORDER:
+ return False
+ e = int.from_bytes(TaggedHash("BIP0340/challenge", sig[0:32] + key + msg), 'big') % SECP256K1_ORDER
+ R = SECP256K1.mul([(SECP256K1_G, s), (P, SECP256K1_ORDER - e)])
+ if not SECP256K1.has_even_y(R):
+ return False
+ if ((r * R[2] * R[2]) % SECP256K1_FIELD_SIZE) != R[0]:
+ return False
+ return True
+
+def sign_schnorr(key, msg, aux=None, flip_p=False, flip_r=False):
+ """Create a Schnorr signature (see BIP 340)."""
+
+ if aux is None:
+ aux = bytes(32)
+
+ assert len(key) == 32
+ assert len(msg) == 32
+ assert len(aux) == 32
+
+ sec = int.from_bytes(key, 'big')
+ if sec == 0 or sec >= SECP256K1_ORDER:
+ return None
+ P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, sec)]))
+ if SECP256K1.has_even_y(P) == flip_p:
+ sec = SECP256K1_ORDER - sec
+ t = (sec ^ int.from_bytes(TaggedHash("BIP0340/aux", aux), 'big')).to_bytes(32, 'big')
+ kp = int.from_bytes(TaggedHash("BIP0340/nonce", t + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
+ assert kp != 0
+ R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, kp)]))
+ k = kp if SECP256K1.has_even_y(R) != flip_r else SECP256K1_ORDER - kp
+ e = int.from_bytes(TaggedHash("BIP0340/challenge", R[0].to_bytes(32, 'big') + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
+ return R[0].to_bytes(32, 'big') + ((k + e * sec) % SECP256K1_ORDER).to_bytes(32, 'big')
+
+class TestFrameworkKey(unittest.TestCase):
+ def test_schnorr(self):
+ """Test the Python Schnorr implementation."""
+ byte_arrays = [generate_privkey() for _ in range(3)] + [v.to_bytes(32, 'big') for v in [0, SECP256K1_ORDER - 1, SECP256K1_ORDER, 2**256 - 1]]
+ keys = {}
+ for privkey in byte_arrays: # build array of key/pubkey pairs
+ pubkey, _ = compute_xonly_pubkey(privkey)
+ if pubkey is not None:
+ keys[privkey] = pubkey
+ for msg in byte_arrays: # test every combination of message, signing key, verification key
+ for sign_privkey, sign_pubkey in keys.items():
+ sig = sign_schnorr(sign_privkey, msg)
+ for verify_privkey, verify_pubkey in keys.items():
+ if verify_privkey == sign_privkey:
+ self.assertTrue(verify_schnorr(verify_pubkey, sig, msg))
+ sig = list(sig)
+ sig[random.randrange(64)] ^= (1 << (random.randrange(8))) # damaging signature should break things
+ sig = bytes(sig)
+ self.assertFalse(verify_schnorr(verify_pubkey, sig, msg))
+
+ def test_schnorr_testvectors(self):
+ """Implement the BIP340 test vectors (read from bip340_test_vectors.csv)."""
+ num_tests = 0
+ with open(os.path.join(sys.path[0], 'test_framework', 'bip340_test_vectors.csv'), newline='', encoding='utf8') as csvfile:
+ reader = csv.reader(csvfile)
+ next(reader)
+ for row in reader:
+ (i_str, seckey_hex, pubkey_hex, aux_rand_hex, msg_hex, sig_hex, result_str, comment) = row
+ i = int(i_str)
+ pubkey = bytes.fromhex(pubkey_hex)
+ msg = bytes.fromhex(msg_hex)
+ sig = bytes.fromhex(sig_hex)
+ result = result_str == 'TRUE'
+ if seckey_hex != '':
+ seckey = bytes.fromhex(seckey_hex)
+ pubkey_actual = compute_xonly_pubkey(seckey)[0]
+ self.assertEqual(pubkey.hex(), pubkey_actual.hex(), "BIP340 test vector %i (%s): pubkey mismatch" % (i, comment))
+ aux_rand = bytes.fromhex(aux_rand_hex)
+ try:
+ sig_actual = sign_schnorr(seckey, msg, aux_rand)
+ self.assertEqual(sig.hex(), sig_actual.hex(), "BIP340 test vector %i (%s): sig mismatch" % (i, comment))
+ except RuntimeError as e:
+ self.fail("BIP340 test vector %i (%s): signing raised exception %s" % (i, comment, e))
+ result_actual = verify_schnorr(pubkey, sig, msg)
+ if result:
+ self.assertEqual(result, result_actual, "BIP340 test vector %i (%s): verification failed" % (i, comment))
+ else:
+ self.assertEqual(result, result_actual, "BIP340 test vector %i (%s): verification succeeded unexpectedly" % (i, comment))
+ num_tests += 1
+ self.assertTrue(num_tests >= 15) # expect at least 15 test vectors
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index da956a94de..ff7f73bdf4 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -22,6 +22,7 @@ from codecs import encode
import copy
import hashlib
from io import BytesIO
+import math
import random
import socket
import struct
@@ -32,7 +33,7 @@ from test_framework.util import hex_str_to_bytes, assert_equal
MIN_VERSION_SUPPORTED = 60001
MY_VERSION = 70016 # past wtxid relay
-MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
+MY_SUBVERSION = b"/python-p2p-tester:0.0.3/"
MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
MAX_LOCATOR_SZ = 101
@@ -53,6 +54,7 @@ NODE_NETWORK = (1 << 0)
NODE_GETUTXO = (1 << 1)
NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
+NODE_COMPACT_FILTERS = (1 << 6)
NODE_NETWORK_LIMITED = (1 << 10)
MSG_TX = 1
@@ -62,9 +64,12 @@ MSG_CMPCT_BLOCK = 4
MSG_WTX = 5
MSG_WITNESS_FLAG = 1 << 30
MSG_TYPE_MASK = 0xffffffff >> 2
+MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG
FILTER_TYPE_BASIC = 0
+WITNESS_SCALE_FACTOR = 4
+
# Serialization/deserialization tools
def sha256(s):
return hashlib.new('sha256', s).digest()
@@ -131,12 +136,17 @@ def uint256_from_compact(c):
return v
-def deser_vector(f, c):
+# deser_function_name: Allow for an alternate deserialization function on the
+# entries in the vector.
+def deser_vector(f, c, deser_function_name=None):
nit = deser_compact_size(f)
r = []
for _ in range(nit):
t = c()
- t.deserialize(f)
+ if deser_function_name:
+ getattr(t, deser_function_name)(f)
+ else:
+ t.deserialize(f)
r.append(t)
return r
@@ -199,38 +209,82 @@ def ToHex(obj):
class CAddress:
- __slots__ = ("ip", "nServices", "pchReserved", "port", "time")
+ __slots__ = ("net", "ip", "nServices", "port", "time")
+
+ # see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
+ NET_IPV4 = 1
+
+ ADDRV2_NET_NAME = {
+ NET_IPV4: "IPv4"
+ }
+
+ ADDRV2_ADDRESS_LENGTH = {
+ NET_IPV4: 4
+ }
def __init__(self):
self.time = 0
self.nServices = 1
- self.pchReserved = b"\x00" * 10 + b"\xff" * 2
+ self.net = self.NET_IPV4
self.ip = "0.0.0.0"
self.port = 0
def deserialize(self, f, *, with_time=True):
+ """Deserialize from addrv1 format (pre-BIP155)"""
if with_time:
# VERSION messages serialize CAddress objects without time
- self.time = struct.unpack("<i", f.read(4))[0]
+ self.time = struct.unpack("<I", f.read(4))[0]
self.nServices = struct.unpack("<Q", f.read(8))[0]
- self.pchReserved = f.read(12)
+ # We only support IPv4 which means skip 12 bytes and read the next 4 as IPv4 address.
+ f.read(12)
+ self.net = self.NET_IPV4
self.ip = socket.inet_ntoa(f.read(4))
self.port = struct.unpack(">H", f.read(2))[0]
def serialize(self, *, with_time=True):
+ """Serialize in addrv1 format (pre-BIP155)"""
+ assert self.net == self.NET_IPV4
r = b""
if with_time:
# VERSION messages serialize CAddress objects without time
- r += struct.pack("<i", self.time)
+ r += struct.pack("<I", self.time)
r += struct.pack("<Q", self.nServices)
- r += self.pchReserved
+ r += b"\x00" * 10 + b"\xff" * 2
+ r += socket.inet_aton(self.ip)
+ r += struct.pack(">H", self.port)
+ return r
+
+ def deserialize_v2(self, f):
+ """Deserialize from addrv2 format (BIP155)"""
+ self.time = struct.unpack("<I", f.read(4))[0]
+
+ self.nServices = deser_compact_size(f)
+
+ self.net = struct.unpack("B", f.read(1))[0]
+ assert self.net == self.NET_IPV4
+
+ address_length = deser_compact_size(f)
+ assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]
+
+ self.ip = socket.inet_ntoa(f.read(4))
+
+ self.port = struct.unpack(">H", f.read(2))[0]
+
+ def serialize_v2(self):
+ """Serialize in addrv2 format (BIP155)"""
+ assert self.net == self.NET_IPV4
+ r = b""
+ r += struct.pack("<I", self.time)
+ r += ser_compact_size(self.nServices)
+ r += struct.pack("B", self.net)
+ r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])
r += socket.inet_aton(self.ip)
r += struct.pack(">H", self.port)
return r
def __repr__(self):
- return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
- self.ip, self.port)
+ return ("CAddress(nServices=%i net=%s addr=%s port=%i)"
+ % (self.nServices, self.ADDRV2_NET_NAME[self.net], self.ip, self.port))
class CInv:
@@ -243,8 +297,8 @@ class CInv:
MSG_TX | MSG_WITNESS_FLAG: "WitnessTx",
MSG_BLOCK | MSG_WITNESS_FLAG: "WitnessBlock",
MSG_FILTERED_BLOCK: "filtered Block",
- 4: "CompactBlock",
- 5: "WTX",
+ MSG_CMPCT_BLOCK: "CompactBlock",
+ MSG_WTX: "WTX",
}
def __init__(self, t=0, h=0):
@@ -252,12 +306,12 @@ class CInv:
self.hash = h
def deserialize(self, f):
- self.type = struct.unpack("<i", f.read(4))[0]
+ self.type = struct.unpack("<I", f.read(4))[0]
self.hash = deser_uint256(f)
def serialize(self):
r = b""
- r += struct.pack("<i", self.type)
+ r += struct.pack("<I", self.type)
r += ser_uint256(self.hash)
return r
@@ -535,6 +589,13 @@ class CTransaction:
return False
return True
+ # Calculate the virtual transaction size using witness and non-witness
+ # serialization size (does NOT use sigops).
+ def get_vsize(self):
+ with_witness_size = len(self.serialize_with_witness())
+ without_witness_size = len(self.serialize_without_witness())
+ return math.ceil(((WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size) / WITNESS_SCALE_FACTOR)
+
def __repr__(self):
return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
% (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
@@ -1052,6 +1113,40 @@ class msg_addr:
return "msg_addr(addrs=%s)" % (repr(self.addrs))
+class msg_addrv2:
+ __slots__ = ("addrs",)
+ msgtype = b"addrv2"
+
+ def __init__(self):
+ self.addrs = []
+
+ def deserialize(self, f):
+ self.addrs = deser_vector(f, CAddress, "deserialize_v2")
+
+ def serialize(self):
+ return ser_vector(self.addrs, "serialize_v2")
+
+ def __repr__(self):
+ return "msg_addrv2(addrs=%s)" % (repr(self.addrs))
+
+
+class msg_sendaddrv2:
+ __slots__ = ()
+ msgtype = b"sendaddrv2"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_sendaddrv2()"
+
+
class msg_inv:
__slots__ = ("inv",)
msgtype = b"inv"
@@ -1460,9 +1555,9 @@ class msg_sendcmpct:
__slots__ = ("announce", "version")
msgtype = b"sendcmpct"
- def __init__(self):
- self.announce = False
- self.version = 1
+ def __init__(self, announce=False, version=1):
+ self.announce = announce
+ self.version = version
def deserialize(self, f):
self.announce = struct.unpack("<?", f.read(1))[0]
diff --git a/test/functional/test_framework/muhash.py b/test/functional/test_framework/muhash.py
new file mode 100644
index 0000000000..97d02359cb
--- /dev/null
+++ b/test/functional/test_framework/muhash.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2020 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Native Python MuHash3072 implementation."""
+
+import hashlib
+import unittest
+
+from .util import modinv
+
+def rot32(v, bits):
+ """Rotate the 32-bit value v left by bits bits."""
+ bits %= 32 # Make sure the term below does not throw an exception
+ return ((v << bits) & 0xffffffff) | (v >> (32 - bits))
+
+def chacha20_doubleround(s):
+ """Apply a ChaCha20 double round to 16-element state array s.
+
+ See https://cr.yp.to/chacha/chacha-20080128.pdf and https://tools.ietf.org/html/rfc8439
+ """
+ QUARTER_ROUNDS = [(0, 4, 8, 12),
+ (1, 5, 9, 13),
+ (2, 6, 10, 14),
+ (3, 7, 11, 15),
+ (0, 5, 10, 15),
+ (1, 6, 11, 12),
+ (2, 7, 8, 13),
+ (3, 4, 9, 14)]
+
+ for a, b, c, d in QUARTER_ROUNDS:
+ s[a] = (s[a] + s[b]) & 0xffffffff
+ s[d] = rot32(s[d] ^ s[a], 16)
+ s[c] = (s[c] + s[d]) & 0xffffffff
+ s[b] = rot32(s[b] ^ s[c], 12)
+ s[a] = (s[a] + s[b]) & 0xffffffff
+ s[d] = rot32(s[d] ^ s[a], 8)
+ s[c] = (s[c] + s[d]) & 0xffffffff
+ s[b] = rot32(s[b] ^ s[c], 7)
+
+def chacha20_32_to_384(key32):
+ """Specialized ChaCha20 implementation with 32-byte key, 0 IV, 384-byte output."""
+ # See RFC 8439 section 2.3 for chacha20 parameters
+ CONSTANTS = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
+
+ key_bytes = [0]*8
+ for i in range(8):
+ key_bytes[i] = int.from_bytes(key32[(4 * i):(4 * (i+1))], 'little')
+
+ INITIALIZATION_VECTOR = [0] * 4
+ init = CONSTANTS + key_bytes + INITIALIZATION_VECTOR
+ out = bytearray()
+ for counter in range(6):
+ init[12] = counter
+ s = init.copy()
+ for _ in range(10):
+ chacha20_doubleround(s)
+ for i in range(16):
+ out.extend(((s[i] + init[i]) & 0xffffffff).to_bytes(4, 'little'))
+ return bytes(out)
+
+def data_to_num3072(data):
+ """Hash a 32-byte array data to a 3072-bit number using 6 Chacha20 operations."""
+ bytes384 = chacha20_32_to_384(data)
+ return int.from_bytes(bytes384, 'little')
+
+class MuHash3072:
+ """Class representing the MuHash3072 computation of a set.
+
+ See https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html
+ """
+
+ MODULUS = 2**3072 - 1103717
+
+ def __init__(self):
+ """Initialize for an empty set."""
+ self.numerator = 1
+ self.denominator = 1
+
+ def insert(self, data):
+ """Insert a byte array data in the set."""
+ self.numerator = (self.numerator * data_to_num3072(data)) % self.MODULUS
+
+ def remove(self, data):
+ """Remove a byte array from the set."""
+ self.denominator = (self.denominator * data_to_num3072(data)) % self.MODULUS
+
+ def digest(self):
+ """Extract the final hash. Does not modify this object."""
+ val = (self.numerator * modinv(self.denominator, self.MODULUS)) % self.MODULUS
+ bytes384 = val.to_bytes(384, 'little')
+ return hashlib.sha256(bytes384).digest()
+
+class TestFrameworkMuhash(unittest.TestCase):
+ def test_muhash(self):
+ muhash = MuHash3072()
+ muhash.insert([0]*32)
+ muhash.insert([1] + [0]*31)
+ muhash.remove([2] + [0]*31)
+ finalized = muhash.digest()
+ # This mirrors the result in the C++ MuHash3072 unit test
+ self.assertEqual(finalized[::-1].hex(), "a44e16d5e34d259b349af21c06e65d653915d2e208e4e03f389af750dc0bfdc3")
+
+ def test_chacha20(self):
+ def chacha_check(key, result):
+ self.assertEqual(chacha20_32_to_384(key)[:64].hex(), result)
+
+ # Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
+ # Since the nonce is hardcoded to 0 in our function we only use those vectors.
+ chacha_check([0]*32, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586")
+ chacha_check([0]*31 + [1], "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963")
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/p2p.py
index eaf637fbb8..6846d31221 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/p2p.py
@@ -4,10 +4,14 @@
# Copyright (c) 2010-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.
-"""Bitcoin P2P network half-a-node.
+"""Test objects for interacting with a bitcoind node over the p2p protocol.
-This python code was modified from ArtForz' public domain half-a-node, as
-found in the mini-node branch of http://github.com/jgarzik/pynode.
+The P2PInterface objects interact with the bitcoind nodes under test using the
+node's p2p interface. They can be used to send messages to the node, and
+callbacks can be registered that execute when messages are received from the
+node. Messages are sent to/received from the node on an asyncio event loop.
+State held inside the objects must be guarded by the p2p_lock to avoid data
+races between the main testing thread and the event loop.
P2PConnection: A low-level connection object to a node's P2P interface
P2PInterface: A high-level interface object for communicating to a node over P2P
@@ -29,6 +33,7 @@ from test_framework.messages import (
MAX_HEADERS_RESULTS,
MIN_VERSION_SUPPORTED,
msg_addr,
+ msg_addrv2,
msg_block,
MSG_BLOCK,
msg_blocktxn,
@@ -52,6 +57,7 @@ from test_framework.messages import (
msg_notfound,
msg_ping,
msg_pong,
+ msg_sendaddrv2,
msg_sendcmpct,
msg_sendheaders,
msg_tx,
@@ -65,12 +71,13 @@ from test_framework.messages import (
NODE_WITNESS,
sha256,
)
-from test_framework.util import wait_until
+from test_framework.util import wait_until_helper
-logger = logging.getLogger("TestFramework.mininode")
+logger = logging.getLogger("TestFramework.p2p")
MESSAGEMAP = {
b"addr": msg_addr,
+ b"addrv2": msg_addrv2,
b"block": msg_block,
b"blocktxn": msg_blocktxn,
b"cfcheckpt": msg_cfcheckpt,
@@ -93,6 +100,7 @@ MESSAGEMAP = {
b"notfound": msg_notfound,
b"ping": msg_ping,
b"pong": msg_pong,
+ b"sendaddrv2": msg_sendaddrv2,
b"sendcmpct": msg_sendcmpct,
b"sendheaders": msg_sendheaders,
b"tx": msg_tx,
@@ -105,6 +113,7 @@ MAGIC_BYTES = {
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
"testnet3": b"\x0b\x11\x09\x07", # testnet3
"regtest": b"\xfa\xbf\xb5\xda", # regtest
+ "signet": b"\x0a\x03\xcf\x40", # signet
}
@@ -280,7 +289,7 @@ class P2PInterface(P2PConnection):
Individual testcases should subclass this and override the on_* methods
if they want to alter message handling behaviour."""
- def __init__(self):
+ def __init__(self, support_addrv2=False):
super().__init__()
# Track number of messages of each type received.
@@ -289,7 +298,7 @@ class P2PInterface(P2PConnection):
# Track the most recent message of each type.
# To wait for a message to be received, pop that message from
- # this and use wait_until.
+ # this and use self.wait_until.
self.last_message = {}
# A count of the number of ping messages we've sent to the node
@@ -298,6 +307,8 @@ class P2PInterface(P2PConnection):
# The network services received from the peer
self.nServices = 0
+ self.support_addrv2 = support_addrv2
+
def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs):
create_conn = super().peer_connect(*args, **kwargs)
@@ -320,7 +331,7 @@ class P2PInterface(P2PConnection):
We keep a count of how many of each message type has been received
and the most recent message of each type."""
- with mininode_lock:
+ with p2p_lock:
try:
msgtype = message.msgtype.decode('ascii')
self.message_count[msgtype] += 1
@@ -340,6 +351,7 @@ class P2PInterface(P2PConnection):
pass
def on_addr(self, message): pass
+ def on_addrv2(self, message): pass
def on_block(self, message): pass
def on_blocktxn(self, message): pass
def on_cfcheckpt(self, message): pass
@@ -360,6 +372,7 @@ class P2PInterface(P2PConnection):
def on_merkleblock(self, message): pass
def on_notfound(self, message): pass
def on_pong(self, message): pass
+ def on_sendaddrv2(self, message): pass
def on_sendcmpct(self, message): pass
def on_sendheaders(self, message): pass
def on_tx(self, message): pass
@@ -384,6 +397,8 @@ class P2PInterface(P2PConnection):
if message.nVersion >= 70016:
self.send_message(msg_wtxidrelay())
self.send_message(msg_verack())
+ if self.support_addrv2:
+ self.send_message(msg_sendaddrv2())
self.nServices = message.nServices
# Connection helper methods
@@ -394,7 +409,7 @@ class P2PInterface(P2PConnection):
assert self.is_connected
return test_function_in()
- wait_until(test_function, timeout=timeout, lock=mininode_lock, timeout_factor=self.timeout_factor)
+ wait_until_helper(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
def wait_for_disconnect(self, timeout=60):
test_function = lambda: not self.is_connected
@@ -498,7 +513,7 @@ class P2PInterface(P2PConnection):
# P2PConnection acquires this lock whenever delivering a message to a P2PInterface.
# This lock should be acquired in the thread running the test logic to synchronize
# access to any data shared with the P2PInterface or P2PConnection.
-mininode_lock = threading.Lock()
+p2p_lock = threading.Lock()
class NetworkThread(threading.Thread):
@@ -518,7 +533,7 @@ class NetworkThread(threading.Thread):
def close(self, timeout=10):
"""Close the connections and network event loop."""
self.network_event_loop.call_soon_threadsafe(self.network_event_loop.stop)
- wait_until(lambda: not self.network_event_loop.is_running(), timeout=timeout)
+ wait_until_helper(lambda: not self.network_event_loop.is_running(), timeout=timeout)
self.network_event_loop.close()
self.join(timeout)
# Safe to remove event loop.
@@ -592,7 +607,7 @@ class P2PDataStore(P2PInterface):
- if success is False: assert that the node's tip doesn't advance
- if reject_reason is set: assert that the correct reject message is logged"""
- with mininode_lock:
+ with p2p_lock:
for block in blocks:
self.block_store[block.sha256] = block
self.last_block_hash = block.sha256
@@ -629,7 +644,7 @@ class P2PDataStore(P2PInterface):
- if expect_disconnect is True: Skip the sync with ping
- if reject_reason is set: assert that the correct reject message is logged."""
- with mininode_lock:
+ with p2p_lock:
for tx in txs:
self.tx_store[tx.sha256] = tx
@@ -668,7 +683,7 @@ class P2PTxInvStore(P2PInterface):
self.tx_invs_received[i.hash] += 1
def get_invs(self):
- with mininode_lock:
+ with p2p_lock:
return list(self.tx_invs_received.keys())
def wait_for_broadcast(self, txns, timeout=60):
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 5e35ba0fce..8e5848d493 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -6,11 +6,15 @@
This file is modified from python-bitcoinlib.
"""
+
+from collections import namedtuple
import hashlib
import struct
import unittest
from typing import List, Dict
+from .key import TaggedHash, tweak_add_pubkey
+
from .messages import (
CTransaction,
CTxOut,
@@ -22,8 +26,13 @@ from .messages import (
)
MAX_SCRIPT_ELEMENT_SIZE = 520
+LOCKTIME_THRESHOLD = 500000000
+ANNEX_TAG = 0x50
+
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
+LEAF_VERSION_TAPSCRIPT = 0xc0
+
def hash160(s):
return hashlib.new('ripemd160', sha256(s)).digest()
@@ -239,11 +248,8 @@ OP_NOP8 = CScriptOp(0xb7)
OP_NOP9 = CScriptOp(0xb8)
OP_NOP10 = CScriptOp(0xb9)
-# template matching params
-OP_SMALLINTEGER = CScriptOp(0xfa)
-OP_PUBKEYS = CScriptOp(0xfb)
-OP_PUBKEYHASH = CScriptOp(0xfd)
-OP_PUBKEY = CScriptOp(0xfe)
+# BIP 342 opcodes (Tapscript)
+OP_CHECKSIGADD = CScriptOp(0xba)
OP_INVALIDOPCODE = CScriptOp(0xff)
@@ -359,10 +365,7 @@ OPCODE_NAMES.update({
OP_NOP8: 'OP_NOP8',
OP_NOP9: 'OP_NOP9',
OP_NOP10: 'OP_NOP10',
- OP_SMALLINTEGER: 'OP_SMALLINTEGER',
- OP_PUBKEYS: 'OP_PUBKEYS',
- OP_PUBKEYHASH: 'OP_PUBKEYHASH',
- OP_PUBKEY: 'OP_PUBKEY',
+ OP_CHECKSIGADD: 'OP_CHECKSIGADD',
OP_INVALIDOPCODE: 'OP_INVALIDOPCODE',
})
@@ -593,6 +596,7 @@ class CScript(bytes):
return n
+SIGHASH_DEFAULT = 0 # Taproot-only default, semantics same as SIGHASH_ALL
SIGHASH_ALL = 1
SIGHASH_NONE = 2
SIGHASH_SINGLE = 3
@@ -615,7 +619,6 @@ def FindAndDelete(script, sig):
r += script[last_sop_idx:]
return CScript(r)
-
def LegacySignatureHash(script, txTo, inIdx, hashtype):
"""Consensus-correct SignatureHash
@@ -738,3 +741,113 @@ class TestFrameworkScript(unittest.TestCase):
values = [0, 1, -1, -2, 127, 128, -255, 256, (1 << 15) - 1, -(1 << 16), (1 << 24) - 1, (1 << 31), 1 - (1 << 32), 1 << 40, 1500, -1500]
for value in values:
self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
+
+def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
+ assert (len(txTo.vin) == len(spent_utxos))
+ assert (input_index < len(txTo.vin))
+ out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
+ in_type = hash_type & SIGHASH_ANYONECANPAY
+ spk = spent_utxos[input_index].scriptPubKey
+ ss = bytes([0, hash_type]) # epoch, hash_type
+ ss += struct.pack("<i", txTo.nVersion)
+ ss += struct.pack("<I", txTo.nLockTime)
+ if in_type != SIGHASH_ANYONECANPAY:
+ ss += sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
+ ss += sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos))
+ ss += sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
+ ss += sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
+ if out_type == SIGHASH_ALL:
+ ss += sha256(b"".join(o.serialize() for o in txTo.vout))
+ spend_type = 0
+ if annex is not None:
+ spend_type |= 1
+ if (scriptpath):
+ spend_type |= 2
+ ss += bytes([spend_type])
+ if in_type == SIGHASH_ANYONECANPAY:
+ ss += txTo.vin[input_index].prevout.serialize()
+ ss += struct.pack("<q", spent_utxos[input_index].nValue)
+ ss += ser_string(spk)
+ ss += struct.pack("<I", txTo.vin[input_index].nSequence)
+ else:
+ ss += struct.pack("<I", input_index)
+ if (spend_type & 1):
+ ss += sha256(ser_string(annex))
+ if out_type == SIGHASH_SINGLE:
+ if input_index < len(txTo.vout):
+ ss += sha256(txTo.vout[input_index].serialize())
+ else:
+ ss += bytes(0 for _ in range(32))
+ if (scriptpath):
+ ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
+ ss += bytes([0])
+ ss += struct.pack("<i", codeseparator_pos)
+ assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
+ return TaggedHash("TapSighash", ss)
+
+def taproot_tree_helper(scripts):
+ if len(scripts) == 0:
+ return ([], bytes(0 for _ in range(32)))
+ if len(scripts) == 1:
+ # One entry: treat as a leaf
+ script = scripts[0]
+ assert(not callable(script))
+ if isinstance(script, list):
+ return taproot_tree_helper(script)
+ assert(isinstance(script, tuple))
+ version = LEAF_VERSION_TAPSCRIPT
+ name = script[0]
+ code = script[1]
+ if len(script) == 3:
+ version = script[2]
+ assert version & 1 == 0
+ assert isinstance(code, bytes)
+ h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
+ if name is None:
+ return ([], h)
+ return ([(name, version, code, bytes())], h)
+ elif len(scripts) == 2 and callable(scripts[1]):
+ # Two entries, and the right one is a function
+ left, left_h = taproot_tree_helper(scripts[0:1])
+ right_h = scripts[1](left_h)
+ left = [(name, version, script, control + right_h) for name, version, script, control in left]
+ right = []
+ else:
+ # Two or more entries: descend into each side
+ split_pos = len(scripts) // 2
+ left, left_h = taproot_tree_helper(scripts[0:split_pos])
+ right, right_h = taproot_tree_helper(scripts[split_pos:])
+ left = [(name, version, script, control + right_h) for name, version, script, control in left]
+ right = [(name, version, script, control + left_h) for name, version, script, control in right]
+ if right_h < left_h:
+ right_h, left_h = left_h, right_h
+ h = TaggedHash("TapBranch", left_h + right_h)
+ return (left + right, h)
+
+TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves")
+TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch")
+
+def taproot_construct(pubkey, scripts=None):
+ """Construct a tree of Taproot spending conditions
+
+ pubkey: an ECPubKey object for the internal pubkey
+ scripts: a list of items; each item is either:
+ - a (name, CScript) tuple
+ - a (name, CScript, leaf version) tuple
+ - another list of items (with the same structure)
+ - a function, which specifies how to compute the hashing partner
+ in function of the hash of whatever it is combined with
+
+ Returns: script (sPK or redeemScript), tweak, {name:(script, leaf version, negation flag, innerkey, merklepath), ...}
+ """
+ if scripts is None:
+ scripts = []
+
+ ret, h = taproot_tree_helper(scripts)
+ tweak = TaggedHash("TapTweak", pubkey + h)
+ tweaked, negated = tweak_add_pubkey(pubkey, tweak)
+ leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret)
+ return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves)
+
+def is_op_success(o):
+ return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)
diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py
index 02368e938f..00c0d8a919 100644
--- a/test/functional/test_framework/segwit_addr.py
+++ b/test/functional/test_framework/segwit_addr.py
@@ -3,7 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Reference implementation for Bech32 and segwit addresses."""
-
+import unittest
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
@@ -84,7 +84,7 @@ def convertbits(data, frombits, tobits, pad=True):
return ret
-def decode(hrp, addr):
+def decode_segwit_address(hrp, addr):
"""Decode a segwit address."""
hrpgot, data = bech32_decode(addr)
if hrpgot != hrp:
@@ -99,9 +99,23 @@ def decode(hrp, addr):
return (data[0], decoded)
-def encode(hrp, witver, witprog):
+def encode_segwit_address(hrp, witver, witprog):
"""Encode a segwit address."""
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
- if decode(hrp, ret) == (None, None):
+ if decode_segwit_address(hrp, ret) == (None, None):
return None
return ret
+
+class TestFrameworkScript(unittest.TestCase):
+ def test_segwit_encode_decode(self):
+ def test_python_bech32(addr):
+ hrp = addr[:4]
+ self.assertEqual(hrp, "bcrt")
+ (witver, witprog) = decode_segwit_address(hrp, addr)
+ self.assertEqual(encode_segwit_address(hrp, witver, witprog), addr)
+
+ # P2WPKH
+ test_python_bech32('bcrt1qthmht0k2qnh3wy7336z05lu2km7emzfpm3wg46')
+ # P2WSH
+ test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
+ test_python_bech32('bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85')
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 8d402d4888..20f608a9cf 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -20,18 +20,17 @@ import time
from .authproxy import JSONRPCException
from . import coverage
+from .p2p import NetworkThread
from .test_node import TestNode
-from .mininode import NetworkThread
from .util import (
MAX_NODES,
PortSeed,
assert_equal,
check_json_precision,
- connect_nodes,
- disconnect_nodes,
get_datadir_path,
initialize_datadir,
- wait_until,
+ p2p_port,
+ wait_until_helper,
)
@@ -102,8 +101,16 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
self.supports_cli = True
self.bind_to_localhost_only = True
- self.set_test_params()
self.parse_args()
+ self.default_wallet_name = "default_wallet" if self.options.descriptors else ""
+ self.wallet_data_filename = "wallet.dat"
+ # Optional list of wallet names that can be set in set_test_params to
+ # create and import keys to. If unset, default is len(nodes) *
+ # [default_wallet_name]. If wallet names are None, wallet creation is
+ # skipped. If list is truncated, wallet creation is skipped and keys
+ # are not imported.
+ self.wallet_names = None
+ self.set_test_params()
if self.options.timeout_factor == 0 :
self.options.timeout_factor = 99999
self.rpc_timeout = int(self.rpc_timeout * self.options.timeout_factor) # optionally, increase timeout by a factor
@@ -362,23 +369,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def setup_nodes(self):
"""Override this method to customize test node setup"""
extra_args = [[]] * self.num_nodes
- wallets = [[]] * self.num_nodes
if hasattr(self, "extra_args"):
extra_args = self.extra_args
- wallets = [[x for x in eargs if x.startswith('-wallet=')] for eargs in extra_args]
- extra_args = [x + ['-nowallet'] for x in extra_args]
self.add_nodes(self.num_nodes, extra_args)
self.start_nodes()
- for i, n in enumerate(self.nodes):
- n.extra_args.pop()
- if '-wallet=0' in n.extra_args or '-nowallet' in n.extra_args or '-disablewallet' in n.extra_args or not self.is_wallet_compiled():
- continue
- if '-wallet=' not in wallets[i] and not any([x.startswith('-wallet=') for x in wallets[i]]):
- wallets[i].append('-wallet=')
- for w in wallets[i]:
- wallet_name = w.split('=', 1)[1]
- n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors)
- self.import_deterministic_coinbase_privkeys()
+ if self.is_wallet_compiled():
+ self.import_deterministic_coinbase_privkeys()
if not self.setup_clean_chain:
for n in self.nodes:
assert_equal(n.getblockchaininfo()["blocks"], 199)
@@ -394,13 +390,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert_equal(chain_info["initialblockdownload"], False)
def import_deterministic_coinbase_privkeys(self):
- for n in self.nodes:
- try:
- n.getwalletinfo()
- except JSONRPCException as e:
- assert str(e).startswith('Method not found')
- continue
-
+ wallet_names = [self.default_wallet_name] * len(self.nodes) if self.wallet_names is None else self.wallet_names
+ assert len(wallet_names) <= len(self.nodes)
+ for wallet_name, n in zip(wallet_names, self.nodes):
+ if wallet_name is not None:
+ n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors, load_on_startup=True)
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
def run_test(self):
@@ -534,10 +528,49 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes[i].process.wait(timeout)
def connect_nodes(self, a, b):
- connect_nodes(self.nodes[a], b)
+ def connect_nodes_helper(from_connection, node_num):
+ ip_port = "127.0.0.1:" + str(p2p_port(node_num))
+ from_connection.addnode(ip_port, "onetry")
+ # poll until version handshake complete to avoid race conditions
+ # with transaction relaying
+ # See comments in net_processing:
+ # * Must have a version message before anything else
+ # * Must have a verack message before anything else
+ wait_until_helper(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
+ wait_until_helper(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()))
+
+ connect_nodes_helper(self.nodes[a], b)
def disconnect_nodes(self, a, b):
- disconnect_nodes(self.nodes[a], b)
+ def disconnect_nodes_helper(from_connection, node_num):
+ def get_peer_ids():
+ result = []
+ for peer in from_connection.getpeerinfo():
+ if "testnode{}".format(node_num) in peer['subver']:
+ result.append(peer['id'])
+ return result
+
+ peer_ids = get_peer_ids()
+ if not peer_ids:
+ self.log.warning("disconnect_nodes: {} and {} were not connected".format(
+ from_connection.index,
+ node_num,
+ ))
+ return
+ for peer_id in peer_ids:
+ try:
+ from_connection.disconnectnode(nodeid=peer_id)
+ except JSONRPCException as e:
+ # If this node is disconnected between calculating the peer id
+ # and issuing the disconnect, don't worry about it.
+ # This avoids a race condition if we're mass-disconnecting peers.
+ if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
+ raise
+
+ # wait to disconnect
+ wait_until_helper(lambda: not get_peer_ids(), timeout=5)
+
+ disconnect_nodes_helper(self.nodes[a], b)
def split_network(self):
"""
@@ -603,8 +636,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.sync_blocks(nodes)
self.sync_mempools(nodes)
- def wait_until(self, test_function, timeout=60, lock=None):
- return wait_until(test_function, timeout=timeout, lock=lock, timeout_factor=self.options.timeout_factor)
+ def wait_until(self, test_function, timeout=60):
+ return wait_until_helper(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor)
# Private helper methods. These should not be accessed by the subclass test scripts.
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 8f0d45c7f9..046efe730e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -31,7 +31,7 @@ from .util import (
get_auth_cookie,
get_rpc_proxy,
rpc_url,
- wait_until,
+ wait_until_helper,
p2p_port,
EncodeDecimal,
)
@@ -231,7 +231,7 @@ class TestNode():
if self.version_is_at_least(190000):
# getmempoolinfo.loaded is available since commit
# bb8ae2c (version 0.19.0)
- wait_until(lambda: rpc.getmempoolinfo()['loaded'])
+ wait_until_helper(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor)
# Wait for the node to finish reindex, block import, and
# loading the mempool. Usually importing happens fast or
# even "immediate" when the node is started. However, there
@@ -359,7 +359,7 @@ class TestNode():
return True
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
- wait_until(self.is_node_stopped, timeout=timeout, timeout_factor=self.timeout_factor)
+ wait_until_helper(self.is_node_stopped, timeout=timeout, timeout_factor=self.timeout_factor)
@contextlib.contextmanager
def assert_debug_log(self, expected_msgs, unexpected_msgs=None, timeout=2):
@@ -542,16 +542,7 @@ class TestNode():
return p2p_conn
- @property
- def p2p(self):
- """Return the first p2p connection
-
- Convenience property - most tests only use a single p2p connection to each
- node, so this saves having to write node.p2ps[0] many times."""
- assert self.p2ps, self._node_msg("No p2p connection")
- return self.p2ps[0]
-
- def num_connected_mininodes(self):
+ def num_test_p2p_connections(self):
"""Return number of test framework p2p connections to the node."""
return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION])
@@ -560,7 +551,7 @@ class TestNode():
for p in self.p2ps:
p.peer_disconnect()
del self.p2ps[:]
- wait_until(lambda: self.num_connected_mininodes() == 0)
+ wait_until_helper(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor)
class TestNodeCLIAttr:
@@ -650,10 +641,10 @@ class RPCOverloadWrapper():
def __getattr__(self, name):
return getattr(self.rpc, name)
- def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None):
+ def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None):
if descriptors is None:
descriptors = self.descriptors
- return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors)
+ return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup)
def importprivkey(self, privkey, label=None, rescan=None):
wallet_info = self.getwalletinfo()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 3362b41209..3356f1ab10 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -12,9 +12,9 @@ import inspect
import json
import logging
import os
-import random
import re
import time
+import unittest
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
@@ -225,7 +225,15 @@ def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
-def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
+def wait_until_helper(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
+ """Sleep until the predicate resolves to be True.
+
+ Warning: Note that this method is not recommended to be used in tests as it is
+ not aware of the context of the test framework. Using the `wait_until()` members
+ from `BitcoinTestFramework` or `P2PInterface` class ensures the timeout is
+ properly scaled. Furthermore, `wait_until()` from `P2PInterface` class in
+ `p2p.py` has a preset lock.
+ """
if attempts == float('inf') and timeout == float('inf'):
timeout = 60
timeout = timeout * timeout_factor
@@ -403,47 +411,6 @@ def set_node_times(nodes, t):
node.setmocktime(t)
-def disconnect_nodes(from_connection, node_num):
- def get_peer_ids():
- result = []
- for peer in from_connection.getpeerinfo():
- if "testnode{}".format(node_num) in peer['subver']:
- result.append(peer['id'])
- return result
-
- peer_ids = get_peer_ids()
- if not peer_ids:
- logger.warning("disconnect_nodes: {} and {} were not connected".format(
- from_connection.index,
- node_num,
- ))
- return
- for peer_id in peer_ids:
- try:
- from_connection.disconnectnode(nodeid=peer_id)
- except JSONRPCException as e:
- # If this node is disconnected between calculating the peer id
- # and issuing the disconnect, don't worry about it.
- # This avoids a race condition if we're mass-disconnecting peers.
- if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
- raise
-
- # wait to disconnect
- wait_until(lambda: not get_peer_ids(), timeout=5)
-
-
-def connect_nodes(from_connection, node_num):
- ip_port = "127.0.0.1:" + str(p2p_port(node_num))
- from_connection.addnode(ip_port, "onetry")
- # poll until version handshake complete to avoid race conditions
- # with transaction relaying
- # See comments in net_processing:
- # * Must have a version message before anything else
- # * Must have a verack message before anything else
- wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
- wait_until(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()))
-
-
# Transaction/Block functions
#############################
@@ -460,62 +427,6 @@ def find_output(node, txid, amount, *, blockhash=None):
raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
-def gather_inputs(from_node, amount_needed, confirmations_required=1):
- """
- Return a random set of unspent txouts that are enough to pay amount_needed
- """
- assert confirmations_required >= 0
- utxo = from_node.listunspent(confirmations_required)
- random.shuffle(utxo)
- inputs = []
- total_in = Decimal("0.00000000")
- while total_in < amount_needed and len(utxo) > 0:
- t = utxo.pop()
- total_in += t["amount"]
- inputs.append({"txid": t["txid"], "vout": t["vout"], "address": t["address"]})
- if total_in < amount_needed:
- raise RuntimeError("Insufficient funds: need %d, have %d" % (amount_needed, total_in))
- return (total_in, inputs)
-
-
-def make_change(from_node, amount_in, amount_out, fee):
- """
- Create change output(s), return them
- """
- outputs = {}
- amount = amount_out + fee
- change = amount_in - amount
- if change > amount * 2:
- # Create an extra change output to break up big inputs
- change_address = from_node.getnewaddress()
- # Split change in two, being careful of rounding:
- outputs[change_address] = Decimal(change / 2).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
- change = amount_in - amount - outputs[change_address]
- if change > 0:
- outputs[from_node.getnewaddress()] = change
- return outputs
-
-
-def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
- """
- Create a random transaction.
- Returns (txid, hex-encoded-transaction-data, fee)
- """
- from_node = random.choice(nodes)
- to_node = random.choice(nodes)
- fee = min_fee + fee_increment * random.randint(0, fee_variants)
-
- (total_in, inputs) = gather_inputs(from_node, amount + fee)
- outputs = make_change(from_node, total_in, amount, fee)
- outputs[to_node.getnewaddress()] = float(amount)
-
- rawtx = from_node.createrawtransaction(inputs, outputs)
- signresult = from_node.signrawtransactionwithwallet(rawtx)
- txid = from_node.sendrawtransaction(signresult["hex"], 0)
-
- return (txid, signresult["hex"], fee)
-
-
# Helper to create at least "count" utxos
# Pass in a fee that is sufficient for relay and mining new transactions.
def create_confirmed_utxos(fee, node, count):
@@ -617,3 +528,33 @@ def find_vout_for_address(node, txid, addr):
if any([addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]):
return i
raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
+
+def modinv(a, n):
+ """Compute the modular inverse of a modulo n using the extended Euclidean
+ Algorithm. See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
+ """
+ # TODO: Change to pow(a, -1, n) available in Python 3.8
+ t1, t2 = 0, 1
+ r1, r2 = n, a
+ while r2 != 0:
+ q = r1 // r2
+ t1, t2 = t2, t1 - q * t2
+ r1, r2 = r2, r1 - q * r2
+ if r1 > 1:
+ return None
+ if t1 < 0:
+ t1 += n
+ return t1
+
+class TestFrameworkUtil(unittest.TestCase):
+ def test_modinv(self):
+ test_vectors = [
+ [7, 11],
+ [11, 29],
+ [90, 13],
+ [1891, 3797],
+ [6003722857, 77695236973],
+ ]
+
+ for a, n in test_vectors:
+ self.assertEqual(modinv(a, n), pow(a, n-2, n))
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
new file mode 100644
index 0000000000..39b3bf2a5b
--- /dev/null
+++ b/test/functional/test_framework/wallet.py
@@ -0,0 +1,68 @@
+#!/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.
+"""A limited-functionality wallet, which may replace a real wallet in tests"""
+
+from decimal import Decimal
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
+from test_framework.util import (
+ assert_equal,
+ hex_str_to_bytes,
+ satoshi_round,
+)
+
+
+class MiniWallet:
+ def __init__(self, test_node):
+ 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'])
+
+ 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)
+ 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']})
+ return blocks
+
+ def get_utxo(self):
+ """Return the last utxo. Can be used to get the change output immediately after a send_self_transfer"""
+ return self._utxos.pop()
+
+ def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
+ """Create and send 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)
+ send_value = satoshi_round(utxo_to_spend['value'] - fee_rate * (vsize / 1000))
+ fee = utxo_to_spend['value'] - send_value
+ assert send_value > 0
+
+ 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_hex = tx.serialize().hex()
+
+ txid = from_node.sendrawtransaction(tx_hex)
+ self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value})
+ tx_info = from_node.getmempoolentry(txid)
+ assert_equal(tx_info['vsize'], vsize)
+ assert_equal(tx_info['fee'], fee)
+ return {'txid': txid, 'wtxid': tx_info['wtxid'], 'hex': tx_hex}
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index b090e93394..8cd82649b6 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -69,7 +69,11 @@ TEST_EXIT_SKIPPED = 77
TEST_FRAMEWORK_MODULES = [
"address",
"blocktools",
+ "muhash",
+ "key",
"script",
+ "segwit_addr",
+ "util",
]
EXTENDED_SCRIPTS = [
@@ -85,6 +89,7 @@ BASE_SCRIPTS = [
'wallet_hd.py',
'wallet_hd.py --descriptors',
'wallet_backup.py',
+ 'wallet_backup.py --descriptors',
# vv Tests less than 5m vv
'mining_getblocktemplate_longpoll.py',
'feature_maxuploadtarget.py',
@@ -103,9 +108,9 @@ BASE_SCRIPTS = [
'mempool_updatefromblock.py',
'wallet_dump.py',
'wallet_listtransactions.py',
+ 'feature_taproot.py',
# vv Tests less than 60s vv
'p2p_sendheaders.py',
- 'wallet_zapwallettxes.py',
'wallet_importmulti.py',
'mempool_limit.py',
'rpc_txoutproof.py',
@@ -138,6 +143,7 @@ BASE_SCRIPTS = [
'mempool_reorg.py',
'mempool_persist.py',
'wallet_multiwallet.py',
+ 'wallet_multiwallet.py --descriptors',
'wallet_multiwallet.py --usecli',
'wallet_createwallet.py',
'wallet_createwallet.py --usecli',
@@ -153,6 +159,7 @@ BASE_SCRIPTS = [
'feature_proxy.py',
'rpc_signrawtransaction.py',
'wallet_groups.py',
+ 'p2p_addrv2_relay.py',
'p2p_disconnect_ban.py',
'rpc_decodescript.py',
'rpc_blockchain.py',
@@ -194,6 +201,7 @@ BASE_SCRIPTS = [
'p2p_eviction.py',
'rpc_signmessage.py',
'rpc_generateblock.py',
+ 'rpc_generate.py',
'wallet_balance.py',
'feature_nulldummy.py',
'mempool_accept.py',
@@ -206,6 +214,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
'mining_basic.py',
+ 'feature_signet.py',
'wallet_bumpfee.py',
'wallet_implicitsegwit.py',
'rpc_named_arguments.py',
@@ -223,6 +232,7 @@ BASE_SCRIPTS = [
'rpc_estimatefee.py',
'rpc_getblockstats.py',
'wallet_create_tx.py',
+ 'wallet_send.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
'wallet_coinbase_category.py',
@@ -243,10 +253,11 @@ BASE_SCRIPTS = [
'p2p_node_network_limited.py',
'p2p_permissions.py',
'feature_blocksdir.py',
+ 'wallet_startup.py',
'feature_config_args.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
- 'rpc_getpeerinfo_banscore_deprecation.py',
+ 'rpc_getpeerinfo_deprecation.py',
'rpc_help.py',
'feature_help.py',
'feature_shutdown.py',
@@ -713,14 +724,16 @@ class RPCCoverage():
Return a set of currently untested RPC commands.
"""
- # This is shared from `test/functional/test-framework/coverage.py`
+ # This is shared from `test/functional/test_framework/coverage.py`
reference_filename = 'rpc_interface.txt'
coverage_file_prefix = 'coverage.'
coverage_ref_filename = os.path.join(self.dir, reference_filename)
coverage_filenames = set()
all_cmds = set()
- covered_cmds = set()
+ # Consider RPC generate covered, because it is overloaded in
+ # test_framework/test_node.py and not seen by the coverage check.
+ covered_cmds = set({'generate'})
if not os.path.isfile(coverage_ref_filename):
raise RuntimeError("No coverage reference found")
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 18f0beb598..958341c691 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -70,12 +70,14 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
+ locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
self.assert_raises_tool_error(
- 'Error loading wallet.dat. Is wallet being used by another process?',
- '-wallet=wallet.dat',
+ 'Error initializing wallet database environment "{}"!'.format(locked_dir),
+ '-wallet=' + self.default_wallet_name,
'info',
)
- self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "nonexistent.dat")
+ self.assert_raises_tool_error("Failed to load database path '{}'. Path does not exist.".format(path), '-wallet=nonexistent.dat', 'info')
def test_tool_wallet_info(self):
# Stop the node to close the wallet to call the info command.
@@ -96,13 +98,17 @@ class ToolWalletTest(BitcoinTestFramework):
out = textwrap.dedent('''\
Wallet info
===========
+ Name: \
+
+ Format: bdb
+ Descriptors: no
Encrypted: no
HD (hd seed available): yes
Keypool Size: 2
Transactions: 0
Address Book: 3
''')
- self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
+ self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
timestamp_after = self.wallet_timestamp()
self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
@@ -135,13 +141,17 @@ class ToolWalletTest(BitcoinTestFramework):
out = textwrap.dedent('''\
Wallet info
===========
+ Name: \
+
+ Format: bdb
+ Descriptors: no
Encrypted: no
HD (hd seed available): yes
Keypool Size: 2
Transactions: 1
Address Book: 3
''')
- self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
+ self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
shasum_after = self.wallet_shasum()
timestamp_after = self.wallet_timestamp()
self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
@@ -162,6 +172,9 @@ class ToolWalletTest(BitcoinTestFramework):
Topping up keypool...
Wallet info
===========
+ Name: foo
+ Format: bdb
+ Descriptors: no
Encrypted: no
HD (hd seed available): yes
Keypool Size: 2000
@@ -179,7 +192,7 @@ class ToolWalletTest(BitcoinTestFramework):
def test_getwalletinfo_on_different_wallet(self):
self.log.info('Starting node with arg -wallet=foo')
- self.start_node(0, ['-wallet=foo'])
+ self.start_node(0, ['-nowallet', '-wallet=foo'])
self.log.info('Calling getwalletinfo on a different wallet ("foo"), testing output')
shasum_before = self.wallet_shasum()
@@ -211,7 +224,7 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_tool_output('', '-wallet=salvage', 'salvage')
def run_test(self):
- self.wallet_path = os.path.join(self.nodes[0].datadir, self.chain, 'wallets', 'wallet.dat')
+ self.wallet_path = os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename)
self.test_invalid_tool_commands_and_args()
# Warning: The following tests are order-dependent.
self.test_tool_wallet_info()
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 8837e13005..2e0edcfa38 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -16,8 +16,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
- disconnect_nodes,
)
@@ -50,7 +48,7 @@ class AbandonConflictTest(BitcoinTestFramework):
balance = newbalance
# Disconnect nodes so node0's transactions don't get into node1's mempool
- disconnect_nodes(self.nodes[0], 1)
+ self.disconnect_nodes(0, 1)
# Identify the 10btc outputs
nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txA)["details"] if tx_out["amount"] == Decimal("10"))
@@ -161,7 +159,7 @@ class AbandonConflictTest(BitcoinTestFramework):
self.nodes[1].sendrawtransaction(signed["hex"])
self.nodes[1].generate(1)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks()
# Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index 68e22b7e86..6b3276e404 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -62,11 +62,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- connect_nodes,
-)
-from test_framework.segwit_addr import (
- encode,
- decode,
)
class AddressTypeTest(BitcoinTestFramework):
@@ -94,20 +89,13 @@ class AddressTypeTest(BitcoinTestFramework):
# Fully mesh-connect nodes for faster mempool sync
for i, j in itertools.product(range(self.num_nodes), repeat=2):
if i > j:
- connect_nodes(self.nodes[i], j)
+ self.connect_nodes(i, j)
self.sync_all()
def get_balances(self, key='trusted'):
"""Return a list of balances."""
return [self.nodes[i].getbalances()['mine'][key] for i in range(4)]
- # Quick test of python bech32 implementation
- def test_python_bech32(self, addr):
- hrp = addr[:4]
- assert_equal(hrp, "bcrt")
- (witver, witprog) = decode(hrp, addr)
- assert_equal(encode(hrp, witver, witprog), addr)
-
def test_address(self, node, address, multisig, typ):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
@@ -132,7 +120,6 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 40)
assert 'pubkey' in info
- self.test_python_bech32(info["address"])
elif typ == 'legacy':
# P2SH-multisig
assert info['isscript']
@@ -158,7 +145,6 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 64)
assert 'pubkeys' in info
- self.test_python_bech32(info["address"])
else:
# Unknown type
assert False
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index eddd938847..229c134a4b 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -9,7 +9,6 @@ from test_framework.util import (
assert_approx,
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
def reset_balance(node, discardaddr):
@@ -111,7 +110,7 @@ class AvoidReuseTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True)
self.restart_node(1)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
# Flags should still be node1.avoid_reuse=false, node2.avoid_reuse=true
assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False)
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index 4766355335..c7bd2ea02b 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -39,7 +39,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
@@ -62,10 +61,10 @@ class WalletBackupTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- connect_nodes(self.nodes[0], 3)
- connect_nodes(self.nodes[1], 3)
- connect_nodes(self.nodes[2], 3)
- connect_nodes(self.nodes[2], 0)
+ self.connect_nodes(0, 3)
+ self.connect_nodes(1, 3)
+ self.connect_nodes(2, 3)
+ self.connect_nodes(2, 0)
self.sync_all()
def one_send(self, from_node, to_address):
@@ -96,10 +95,10 @@ class WalletBackupTest(BitcoinTestFramework):
self.start_node(0)
self.start_node(1)
self.start_node(2)
- connect_nodes(self.nodes[0], 3)
- connect_nodes(self.nodes[1], 3)
- connect_nodes(self.nodes[2], 3)
- connect_nodes(self.nodes[2], 0)
+ self.connect_nodes(0, 3)
+ self.connect_nodes(1, 3)
+ self.connect_nodes(2, 3)
+ self.connect_nodes(2, 0)
def stop_three(self):
self.stop_node(0)
@@ -107,9 +106,9 @@ class WalletBackupTest(BitcoinTestFramework):
self.stop_node(2)
def erase_three(self):
- os.remove(os.path.join(self.nodes[0].datadir, self.chain, 'wallets', 'wallet.dat'))
- os.remove(os.path.join(self.nodes[1].datadir, self.chain, 'wallets', 'wallet.dat'))
- os.remove(os.path.join(self.nodes[2].datadir, self.chain, 'wallets', 'wallet.dat'))
+ os.remove(os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
+ os.remove(os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
+ os.remove(os.path.join(self.nodes[2].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
def run_test(self):
self.log.info("Generating initial blockchain")
@@ -135,11 +134,13 @@ class WalletBackupTest(BitcoinTestFramework):
self.log.info("Backing up")
self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
- self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, 'wallet.bak'))
- self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
self.nodes[2].backupwallet(os.path.join(self.nodes[2].datadir, 'wallet.bak'))
- self.nodes[2].dumpwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
+
+ if not self.options.descriptors:
+ self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
+ self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
+ self.nodes[2].dumpwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
self.log.info("More transactions")
for _ in range(5):
@@ -171,9 +172,9 @@ class WalletBackupTest(BitcoinTestFramework):
shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'chainstate'))
# Restore wallets from backup
- shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[0].datadir, self.chain, 'wallets', 'wallet.dat'))
- shutil.copyfile(os.path.join(self.nodes[1].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, self.chain, 'wallets', 'wallet.dat'))
- shutil.copyfile(os.path.join(self.nodes[2].datadir, 'wallet.bak'), os.path.join(self.nodes[2].datadir, self.chain, 'wallets', 'wallet.dat'))
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
+ shutil.copyfile(os.path.join(self.nodes[1].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
+ shutil.copyfile(os.path.join(self.nodes[2].datadir, 'wallet.bak'), os.path.join(self.nodes[2].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename))
self.log.info("Re-starting nodes")
self.start_three()
@@ -183,35 +184,36 @@ class WalletBackupTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), balance1)
assert_equal(self.nodes[2].getbalance(), balance2)
- self.log.info("Restoring using dumped wallet")
- self.stop_three()
- self.erase_three()
+ if not self.options.descriptors:
+ self.log.info("Restoring using dumped wallet")
+ self.stop_three()
+ self.erase_three()
- #start node2 with no chain
- shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'blocks'))
- shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'chainstate'))
+ #start node2 with no chain
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'blocks'))
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, self.chain, 'chainstate'))
- self.start_three()
+ self.start_three()
- assert_equal(self.nodes[0].getbalance(), 0)
- assert_equal(self.nodes[1].getbalance(), 0)
- assert_equal(self.nodes[2].getbalance(), 0)
+ assert_equal(self.nodes[0].getbalance(), 0)
+ assert_equal(self.nodes[1].getbalance(), 0)
+ assert_equal(self.nodes[2].getbalance(), 0)
- self.nodes[0].importwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
- self.nodes[1].importwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
- self.nodes[2].importwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
+ self.nodes[0].importwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
+ self.nodes[1].importwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
+ self.nodes[2].importwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
- self.sync_blocks()
+ self.sync_blocks()
- assert_equal(self.nodes[0].getbalance(), balance0)
- assert_equal(self.nodes[1].getbalance(), balance1)
- assert_equal(self.nodes[2].getbalance(), balance2)
+ assert_equal(self.nodes[0].getbalance(), balance0)
+ assert_equal(self.nodes[1].getbalance(), balance1)
+ assert_equal(self.nodes[2].getbalance(), balance2)
# Backup to source wallet file must fail
sourcePaths = [
- os.path.join(self.nodes[0].datadir, self.chain, 'wallets', 'wallet.dat'),
- os.path.join(self.nodes[0].datadir, self.chain, '.', 'wallets', 'wallet.dat'),
- os.path.join(self.nodes[0].datadir, self.chain, 'wallets', ''),
+ os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename),
+ os.path.join(self.nodes[0].datadir, self.chain, '.', 'wallets', self.default_wallet_name, self.wallet_data_filename),
+ os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name),
os.path.join(self.nodes[0].datadir, self.chain, 'wallets')]
for sourcePath in sourcePaths:
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 31829a18b3..589fab9992 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -11,7 +11,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
@@ -215,10 +214,10 @@ class WalletTest(BitcoinTestFramework):
# dynamically loading the wallet.
before = self.nodes[1].getbalances()['mine']['untrusted_pending']
dst = self.nodes[1].getnewaddress()
- self.nodes[1].unloadwallet('')
+ self.nodes[1].unloadwallet(self.default_wallet_name)
self.nodes[0].sendtoaddress(dst, 0.1)
self.sync_all()
- self.nodes[1].loadwallet('')
+ self.nodes[1].loadwallet(self.default_wallet_name)
after = self.nodes[1].getbalances()['mine']['untrusted_pending']
assert_equal(before + Decimal('0.1'), after)
@@ -262,7 +261,7 @@ class WalletTest(BitcoinTestFramework):
# Now confirm tx_orig
self.restart_node(1, ['-persistmempool=0'])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_blocks()
self.nodes[1].sendrawtransaction(tx_orig)
self.nodes[1].generatetoaddress(1, ADDRESS_WATCHONLY)
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index d9a8b58a84..d386d94e0c 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -11,8 +11,6 @@ from test_framework.util import (
assert_equal,
assert_fee_amount,
assert_raises_rpc_error,
- connect_nodes,
- wait_until,
)
from test_framework.wallet_util import test_address
@@ -33,9 +31,9 @@ class WalletTest(BitcoinTestFramework):
self.setup_nodes()
# Only need nodes 0-2 running at start of test
self.stop_node(3)
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
self.sync_all(self.nodes[0:3])
def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
@@ -136,11 +134,19 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].lockunspent, False,
[{"txid": unspent_0["txid"], "vout": 999}])
- # An output should be unlocked when spent
+ # The lock on a manually selected output is ignored
unspent_0 = self.nodes[1].listunspent()[0]
self.nodes[1].lockunspent(False, [unspent_0])
tx = self.nodes[1].createrawtransaction([unspent_0], { self.nodes[1].getnewaddress() : 1 })
- tx = self.nodes[1].fundrawtransaction(tx)['hex']
+ self.nodes[1].fundrawtransaction(tx,{"lockUnspents": True})
+
+ # fundrawtransaction can lock an input
+ self.nodes[1].lockunspent(True, [unspent_0])
+ assert_equal(len(self.nodes[1].listlockunspent()), 0)
+ tx = self.nodes[1].fundrawtransaction(tx,{"lockUnspents": True})['hex']
+ assert_equal(len(self.nodes[1].listlockunspent()), 1)
+
+ # Send transaction
tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"]
self.nodes[1].sendrawtransaction(tx)
assert_equal(len(self.nodes[1].listlockunspent()), 0)
@@ -274,7 +280,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getbalance(), node_0_bal)
self.start_node(3, self.nodes[3].extra_args)
- connect_nodes(self.nodes[0], 3)
+ self.connect_nodes(0, 3)
self.sync_all()
# check if we can list zero value tx as available coins
@@ -309,9 +315,9 @@ class WalletTest(BitcoinTestFramework):
self.start_node(0, ["-walletbroadcast=0"])
self.start_node(1, ["-walletbroadcast=0"])
self.start_node(2, ["-walletbroadcast=0"])
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
self.sync_all(self.nodes[0:3])
txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
@@ -336,9 +342,9 @@ class WalletTest(BitcoinTestFramework):
self.start_node(0)
self.start_node(1)
self.start_node(2)
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
self.sync_blocks(self.nodes[0:3])
self.nodes[0].generate(1)
@@ -527,8 +533,6 @@ class WalletTest(BitcoinTestFramework):
maintenance = [
'-rescan',
'-reindex',
- '-zapwallettxes=1',
- '-zapwallettxes=2',
]
chainlimit = 6
for m in maintenance:
@@ -540,7 +544,7 @@ class WalletTest(BitcoinTestFramework):
self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)])
if m == '-reindex':
# reindex will leave rpc warm up "early"; Wait for it to finish
- wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
+ self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
# Exercise listsinceblock with the last two blocks
@@ -589,7 +593,10 @@ class WalletTest(BitcoinTestFramework):
self.start_node(0, extra_args=extra_args)
# wait until the wallet has submitted all transactions to the mempool
- wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2)
+ self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2)
+
+ # Prevent potential race condition when calling wallet RPCs right after restart
+ self.nodes[0].syncwithvalidationinterfacequeue()
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
@@ -653,6 +660,18 @@ class WalletTest(BitcoinTestFramework):
assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"]))
+ self.log.info("Test send* RPCs with verbose=True")
+ address = self.nodes[0].getnewaddress("test")
+ txid_feeReason_one = self.nodes[2].sendtoaddress(address=address, amount=5, verbose=True)
+ assert_equal(txid_feeReason_one["fee_reason"], "Fallback fee")
+ txid_feeReason_two = self.nodes[2].sendmany(dummy='', amounts={address: 5}, verbose=True)
+ assert_equal(txid_feeReason_two["fee_reason"], "Fallback fee")
+ self.log.info("Test send* RPCs with verbose=False")
+ txid_feeReason_three = self.nodes[2].sendtoaddress(address=address, amount=5, verbose=False)
+ assert_equal(self.nodes[2].gettransaction(txid_feeReason_three)['txid'], txid_feeReason_three)
+ txid_feeReason_four = self.nodes[2].sendmany(dummy='', amounts={address: 5}, verbose=False)
+ assert_equal(self.nodes[2].gettransaction(txid_feeReason_four)['txid'], txid_feeReason_four)
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 0ef78b0e1c..4b29e65b09 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -50,6 +50,11 @@ class BumpFeeTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def clear_mempool(self):
+ # Clear mempool between subtests. The subtests may only depend on chainstate (utxos)
+ self.nodes[1].generate(1)
+ self.sync_all()
+
def run_test(self):
# Encrypt wallet for test_locked_wallet_fails test
self.nodes[1].encryptwallet(WALLET_PASSPHRASE)
@@ -71,7 +76,7 @@ class BumpFeeTest(BitcoinTestFramework):
self.log.info("Running tests")
dest_address = peer_node.getnewaddress()
- test_invalid_parameters(rbf_node, dest_address)
+ self.test_invalid_parameters(rbf_node, dest_address)
test_simple_bumpfee_succeeds(self, "default", rbf_node, peer_node, dest_address)
test_simple_bumpfee_succeeds(self, "fee_rate", rbf_node, peer_node, dest_address)
test_feerate_args(self, rbf_node, peer_node, dest_address)
@@ -93,28 +98,30 @@ class BumpFeeTest(BitcoinTestFramework):
test_small_output_with_feerate_succeeds(self, rbf_node, dest_address)
test_no_more_inputs_fails(self, rbf_node, dest_address)
-def test_invalid_parameters(node, dest_address):
- txid = spend_one_input(node, dest_address)
- # invalid estimate mode
- assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", node.bumpfee, txid, {
- "estimate_mode": "moo",
- })
- assert_raises_rpc_error(-3, "Expected type string", node.bumpfee, txid, {
- "estimate_mode": 38,
- })
- assert_raises_rpc_error(-3, "Expected type string", node.bumpfee, txid, {
- "estimate_mode": {
- "foo": "bar",
- },
- })
- assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", node.bumpfee, txid, {
- "estimate_mode": Decimal("3.141592"),
- })
- # confTarget and conf_target
- assert_raises_rpc_error(-8, "confTarget and conf_target options should not both be set", node.bumpfee, txid, {
- "confTarget": 123,
- "conf_target": 456,
- })
+ def test_invalid_parameters(self, node, dest_address):
+ txid = spend_one_input(node, dest_address)
+ # invalid estimate mode
+ assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", node.bumpfee, txid, {
+ "estimate_mode": "moo",
+ })
+ assert_raises_rpc_error(-3, "Expected type string", node.bumpfee, txid, {
+ "estimate_mode": 38,
+ })
+ assert_raises_rpc_error(-3, "Expected type string", node.bumpfee, txid, {
+ "estimate_mode": {
+ "foo": "bar",
+ },
+ })
+ assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", node.bumpfee, txid, {
+ "estimate_mode": Decimal("3.141592"),
+ })
+ # confTarget and conf_target
+ assert_raises_rpc_error(-8, "confTarget and conf_target options should not both be set", node.bumpfee, txid, {
+ "confTarget": 123,
+ "conf_target": 456,
+ })
+ self.clear_mempool()
+
def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
self.log.info('Test simple bumpfee: {}'.format(mode))
@@ -123,13 +130,19 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
if mode == "fee_rate":
+ bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": NORMAL})
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
else:
+ bumped_psbt = rbf_node.psbtbumpfee(rbfid)
bumped_tx = rbf_node.bumpfee(rbfid)
assert_equal(bumped_tx["errors"], [])
assert bumped_tx["fee"] > -rbftx["fee"]
assert_equal(bumped_tx["origfee"], -rbftx["fee"])
assert "psbt" not in bumped_tx
+ assert_equal(bumped_psbt["errors"], [])
+ assert bumped_psbt["fee"] > -rbftx["fee"]
+ assert_equal(bumped_psbt["origfee"], -rbftx["fee"])
+ assert "psbt" in bumped_psbt
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
self.sync_mempools((rbf_node, peer_node))
assert bumped_tx["txid"] in rbf_node.getrawmempool()
@@ -142,6 +155,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
bumpedwtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(oldwtx["replaced_by_txid"], bumped_tx["txid"])
assert_equal(bumpedwtx["replaces_txid"], rbfid)
+ self.clear_mempool()
def test_feerate_args(self, rbf_node, peer_node, dest_address):
@@ -161,6 +175,7 @@ def test_feerate_args(self, rbf_node, peer_node, dest_address):
assert_raises_rpc_error(-3, "Amount out of range", rbf_node.bumpfee, rbfid, {"fee_rate": -1})
assert_raises_rpc_error(-4, "is too high (cannot be higher than", rbf_node.bumpfee, rbfid, {"fee_rate": TOO_HIGH})
+ self.clear_mempool()
def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address):
@@ -192,12 +207,14 @@ def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address):
bumped_tx = rbf_node.bumpfee(rbfid)
assert bumped_tx["txid"] in rbf_node.getrawmempool()
assert rbfid not in rbf_node.getrawmempool()
+ self.clear_mempool()
def test_nonrbf_bumpfee_fails(self, peer_node, dest_address):
self.log.info('Test that we cannot replace a non RBF transaction')
not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000"))
assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid)
+ self.clear_mempool()
def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address):
@@ -205,20 +222,22 @@ def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address):
# here, the rbftx has a peer_node coin and then adds a rbf_node input
# Note that this test depends upon the RPC code checking input ownership prior to change outputs
# (since it can't use fundrawtransaction, it lacks a proper change output)
- utxos = [node.listunspent()[-1] for node in (rbf_node, peer_node)]
+ fee = Decimal("0.001")
+ utxos = [node.listunspent(query_options={'minimumAmount': fee})[-1] for node in (rbf_node, peer_node)]
inputs = [{
"txid": utxo["txid"],
"vout": utxo["vout"],
"address": utxo["address"],
"sequence": BIP125_SEQUENCE_NUMBER
} for utxo in utxos]
- output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("0.001")
+ output_val = sum(utxo["amount"] for utxo in utxos) - fee
rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val})
signedtx = rbf_node.signrawtransactionwithwallet(rawtx)
signedtx = peer_node.signrawtransactionwithwallet(signedtx["hex"])
rbfid = rbf_node.sendrawtransaction(signedtx["hex"])
assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet",
rbf_node.bumpfee, rbfid)
+ self.clear_mempool()
def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address):
@@ -229,6 +248,7 @@ def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_ad
tx = rbf_node.signrawtransactionwithwallet(tx)
rbf_node.sendrawtransaction(tx["hex"])
assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
+ self.clear_mempool()
def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
@@ -270,6 +290,7 @@ def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
rbf_node.generatetoaddress(1, rbf_node.getnewaddress())
assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1)
+ self.clear_mempool()
def test_dust_to_fee(self, rbf_node, dest_address):
@@ -292,6 +313,7 @@ def test_dust_to_fee(self, rbf_node, dest_address):
assert_equal(len(fulltx["vout"]), 2)
assert_equal(len(full_bumped_tx["vout"]), 1) # change output is eliminated
assert_equal(full_bumped_tx["vout"][0]['value'], Decimal("0.00050000"))
+ self.clear_mempool()
def test_settxfee(self, rbf_node, dest_address):
@@ -314,6 +336,8 @@ def test_settxfee(self, rbf_node, dest_address):
assert_raises_rpc_error(-8, "txfee cannot be more than wallet max tx fee", rbf_node.settxfee, Decimal('0.00003'))
self.restart_node(1, self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ self.connect_nodes(1, 0)
+ self.clear_mempool()
def test_maxtxfee_fails(self, rbf_node, dest_address):
@@ -324,9 +348,11 @@ def test_maxtxfee_fails(self, rbf_node, dest_address):
self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
rbfid = spend_one_input(rbf_node, dest_address)
- assert_raises_rpc_error(-4, "Unable to create transaction. Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid)
+ assert_raises_rpc_error(-4, "Unable to create transaction. Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", rbf_node.bumpfee, rbfid)
self.restart_node(1, self.extra_args[1])
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ self.connect_nodes(1, 0)
+ self.clear_mempool()
def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
@@ -391,7 +417,7 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1)
# Bump fee, obnoxiously high to add additional watchonly input
- bumped_psbt = watcher.bumpfee(original_txid, {"fee_rate": HIGH})
+ bumped_psbt = watcher.psbtbumpfee(original_txid, {"fee_rate": HIGH})
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1)
assert "txid" not in bumped_psbt
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])
@@ -409,6 +435,7 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
rbf_node.unloadwallet("watcher")
rbf_node.unloadwallet("signer")
+ self.clear_mempool()
def test_rebumping(self, rbf_node, dest_address):
@@ -417,6 +444,7 @@ def test_rebumping(self, rbf_node, dest_address):
bumped = rbf_node.bumpfee(rbfid, {"fee_rate": ECONOMICAL})
assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"fee_rate": NORMAL})
rbf_node.bumpfee(bumped["txid"], {"fee_rate": NORMAL})
+ self.clear_mempool()
def test_rebumping_not_replaceable(self, rbf_node, dest_address):
@@ -425,6 +453,7 @@ def test_rebumping_not_replaceable(self, rbf_node, dest_address):
bumped = rbf_node.bumpfee(rbfid, {"fee_rate": ECONOMICAL, "replaceable": False})
assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"],
{"fee_rate": NORMAL})
+ self.clear_mempool()
def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address):
@@ -464,6 +493,7 @@ def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address):
assert_equal(
sum(1 for t in rbf_node.listunspent(minconf=0, include_unsafe=False)
if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1)
+ self.clear_mempool()
def test_bumpfee_metadata(self, rbf_node, dest_address):
@@ -475,6 +505,7 @@ def test_bumpfee_metadata(self, rbf_node, dest_address):
bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"])
assert_equal(bumped_wtx["comment"], "comment value")
assert_equal(bumped_wtx["to"], "to value")
+ self.clear_mempool()
def test_locked_wallet_fails(self, rbf_node, dest_address):
@@ -484,6 +515,7 @@ def test_locked_wallet_fails(self, rbf_node, dest_address):
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
rbf_node.bumpfee, rbfid)
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ self.clear_mempool()
def test_change_script_match(self, rbf_node, dest_address):
@@ -504,6 +536,7 @@ def test_change_script_match(self, rbf_node, dest_address):
assert_equal(change_addresses, get_change_address(bumped_total_tx['txid']))
bumped_rate_tx = rbf_node.bumpfee(bumped_total_tx["txid"])
assert_equal(change_addresses, get_change_address(bumped_rate_tx['txid']))
+ self.clear_mempool()
def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
@@ -542,6 +575,7 @@ def test_no_more_inputs_fails(self, rbf_node, dest_address):
# spend all funds, no change output
rbfid = rbf_node.sendtoaddress(rbf_node.getnewaddress(), rbf_node.getbalance(), "", "", True)
assert_raises_rpc_error(-4, "Unable to create transaction. Insufficient funds", rbf_node.bumpfee, rbfid)
+ self.clear_mempool()
if __name__ == "__main__":
diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py
index ed9159726a..0f11aca525 100755
--- a/test/functional/wallet_create_tx.py
+++ b/test/functional/wallet_create_tx.py
@@ -53,12 +53,12 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.restart_node(0, extra_args=[fee_setting])
assert_raises_rpc_error(
-6,
- "Fee exceeds maximum configured by -maxtxfee",
+ "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
)
assert_raises_rpc_error(
-4,
- "Fee exceeds maximum configured by -maxtxfee",
+ "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
)
@@ -67,12 +67,12 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.nodes[0].settxfee(0.01)
assert_raises_rpc_error(
-6,
- "Fee exceeds maximum configured by -maxtxfee",
+ "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
)
assert_raises_rpc_error(
-4,
- "Fee exceeds maximum configured by -maxtxfee",
+ "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
)
self.nodes[0].settxfee(0)
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 9c63e8f7d3..51e9ec8f40 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -21,14 +21,18 @@ class WalletDescriptorTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
+ wallet_info = self.nodes[0].getwalletinfo()
+ assert_equal(wallet_info['format'], 'bdb')
+
# Make a descriptor wallet
self.log.info("Making a descriptor wallet")
self.nodes[0].createwallet(wallet_name="desc1", descriptors=True)
- self.nodes[0].unloadwallet("")
+ self.nodes[0].unloadwallet(self.default_wallet_name)
# A descriptor wallet should have 100 addresses * 3 types = 300 keys
self.log.info("Checking wallet info")
wallet_info = self.nodes[0].getwalletinfo()
+ assert_equal(wallet_info['format'], 'sqlite')
assert_equal(wallet_info['keypoolsize'], 300)
assert_equal(wallet_info['keypoolsize_hd_internal'], 300)
assert 'keypoololdest' not in wallet_info
diff --git a/test/functional/wallet_disable.py b/test/functional/wallet_disable.py
index 7c2ec56b5a..c2b30fb35b 100755
--- a/test/functional/wallet_disable.py
+++ b/test/functional/wallet_disable.py
@@ -16,6 +16,7 @@ class DisableWalletTest (BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [["-disablewallet"]]
+ self.wallet_names = []
def run_test (self):
# Make sure wallet is really disabled
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index 06f01ef191..09581d864b 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -95,7 +95,7 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
class WalletDumpTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
+ self.extra_args = [["-keypool=90", "-addresstype=legacy", "-wallet=dump"]]
self.rpc_timeout = 120
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index b6fe295127..e5c4f12f20 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -15,8 +15,14 @@ from test_framework.util import (
class WalletGroupTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 3
- self.extra_args = [[], [], ['-avoidpartialspends']]
+ self.num_nodes = 5
+ self.extra_args = [
+ [],
+ [],
+ ["-avoidpartialspends"],
+ ["-maxapsfee=0.00002719"],
+ ["-maxapsfee=0.00002720"],
+ ]
self.rpc_timeout = 480
def skip_test_if_missing_module(self):
@@ -50,8 +56,8 @@ class WalletGroupTest(BitcoinTestFramework):
# one output should be 0.2, the other should be ~0.3
v = [vout["value"] for vout in tx1["vout"]]
v.sort()
- assert_approx(v[0], 0.2)
- assert_approx(v[1], 0.3, 0.0001)
+ assert_approx(v[0], vexp=0.2, vspan=0.0001)
+ assert_approx(v[1], vexp=0.3, vspan=0.0001)
txid2 = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
tx2 = self.nodes[2].getrawtransaction(txid2, True)
@@ -61,8 +67,80 @@ class WalletGroupTest(BitcoinTestFramework):
# one output should be 0.2, the other should be ~1.3
v = [vout["value"] for vout in tx2["vout"]]
v.sort()
- assert_approx(v[0], 0.2)
- assert_approx(v[1], 1.3, 0.0001)
+ assert_approx(v[0], vexp=0.2, vspan=0.0001)
+ assert_approx(v[1], vexp=1.3, vspan=0.0001)
+
+ # Test 'avoid partial if warranted, even if disabled'
+ self.sync_all()
+ self.nodes[0].generate(1)
+ # Nodes 1-2 now have confirmed UTXOs (letters denote destinations):
+ # Node #1: Node #2:
+ # - A 1.0 - D0 1.0
+ # - B0 1.0 - D1 0.5
+ # - B1 0.5 - E0 1.0
+ # - C0 1.0 - E1 0.5
+ # - C1 0.5 - F ~1.3
+ # - D ~0.3
+ assert_approx(self.nodes[1].getbalance(), vexp=4.3, vspan=0.0001)
+ assert_approx(self.nodes[2].getbalance(), vexp=4.3, vspan=0.0001)
+ # Sending 1.4 btc should pick one 1.0 + one more. For node #1,
+ # this could be (A / B0 / C0) + (B1 / C1 / D). We ensure that it is
+ # B0 + B1 or C0 + C1, because this avoids partial spends while not being
+ # detrimental to transaction cost
+ txid3 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.4)
+ tx3 = self.nodes[1].getrawtransaction(txid3, True)
+ # tx3 should have 2 inputs and 2 outputs
+ assert_equal(2, len(tx3["vin"]))
+ assert_equal(2, len(tx3["vout"]))
+ # the accumulated value should be 1.5, so the outputs should be
+ # ~0.1 and 1.4 and should come from the same destination
+ values = [vout["value"] for vout in tx3["vout"]]
+ values.sort()
+ assert_approx(values[0], vexp=0.1, vspan=0.0001)
+ assert_approx(values[1], vexp=1.4, vspan=0.0001)
+
+ input_txids = [vin["txid"] for vin in tx3["vin"]]
+ input_addrs = [self.nodes[1].gettransaction(txid)['details'][0]['address'] for txid in input_txids]
+ assert_equal(input_addrs[0], input_addrs[1])
+ # Node 2 enforces avoidpartialspends so needs no checking here
+
+ # Test wallet option maxapsfee with Node 3
+ addr_aps = self.nodes[3].getnewaddress()
+ self.nodes[0].sendtoaddress(addr_aps, 1.0)
+ self.nodes[0].sendtoaddress(addr_aps, 1.0)
+ self.nodes[0].generate(1)
+ self.sync_all()
+ with self.nodes[3].assert_debug_log(['Fee non-grouped = 2820, grouped = 4160, using grouped']):
+ txid4 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 0.1)
+ tx4 = self.nodes[3].getrawtransaction(txid4, True)
+ # tx4 should have 2 inputs and 2 outputs although one output would
+ # have been enough and the transaction caused higher fees
+ assert_equal(2, len(tx4["vin"]))
+ assert_equal(2, len(tx4["vout"]))
+
+ addr_aps2 = self.nodes[3].getnewaddress()
+ [self.nodes[0].sendtoaddress(addr_aps2, 1.0) for _ in range(5)]
+ self.nodes[0].generate(1)
+ self.sync_all()
+ with self.nodes[3].assert_debug_log(['Fee non-grouped = 5520, grouped = 8240, using non-grouped']):
+ txid5 = self.nodes[3].sendtoaddress(self.nodes[0].getnewaddress(), 2.95)
+ tx5 = self.nodes[3].getrawtransaction(txid5, True)
+ # tx5 should have 3 inputs (1.0, 1.0, 1.0) and 2 outputs
+ assert_equal(3, len(tx5["vin"]))
+ assert_equal(2, len(tx5["vout"]))
+
+ # Test wallet option maxapsfee with node 4, which sets maxapsfee
+ # 1 sat higher, crossing the threshold from non-grouped to grouped.
+ addr_aps3 = self.nodes[4].getnewaddress()
+ [self.nodes[0].sendtoaddress(addr_aps3, 1.0) for _ in range(5)]
+ self.nodes[0].generate(1)
+ self.sync_all()
+ with self.nodes[4].assert_debug_log(['Fee non-grouped = 5520, grouped = 8240, using grouped']):
+ txid6 = self.nodes[4].sendtoaddress(self.nodes[0].getnewaddress(), 2.95)
+ tx6 = self.nodes[4].getrawtransaction(txid6, True)
+ # tx6 should have 5 inputs and 2 outputs
+ assert_equal(5, len(tx6["vin"]))
+ assert_equal(2, len(tx6["vout"]))
# Empty out node2's wallet
self.nodes[2].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=self.nodes[2].getbalance(), subtractfeefromamount=True)
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 3c336623e2..d45cf05689 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -10,7 +10,6 @@ import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
assert_raises_rpc_error,
)
@@ -84,7 +83,7 @@ class WalletHDTest(BitcoinTestFramework):
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "chainstate"))
shutil.copyfile(
os.path.join(self.nodes[1].datadir, "hd.bak"),
- os.path.join(self.nodes[1].datadir, self.chain, 'wallets', "wallet.dat"),
+ os.path.join(self.nodes[1].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename),
)
self.start_node(1)
@@ -99,7 +98,7 @@ class WalletHDTest(BitcoinTestFramework):
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/" + str(i) + "'")
assert_equal(hd_info_2["hdmasterfingerprint"], hd_fingerprint)
assert_equal(hd_add, hd_add_2)
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_all()
# Needs rescan
@@ -112,10 +111,10 @@ class WalletHDTest(BitcoinTestFramework):
shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "chainstate"))
shutil.copyfile(
os.path.join(self.nodes[1].datadir, "hd.bak"),
- os.path.join(self.nodes[1].datadir, self.chain, "wallets", "wallet.dat"),
+ os.path.join(self.nodes[1].datadir, self.chain, "wallets", self.default_wallet_name, self.wallet_data_filename),
)
self.start_node(1, extra_args=self.extra_args[1])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
self.sync_all()
# Wallet automatically scans blocks older than key on startup
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1)
@@ -183,7 +182,7 @@ class WalletHDTest(BitcoinTestFramework):
# Restart node 1 with keypool of 3 and a different wallet
self.nodes[1].createwallet(wallet_name='origin', blank=True)
self.restart_node(1, extra_args=['-keypool=3', '-wallet=origin'])
- connect_nodes(self.nodes[0], 1)
+ self.connect_nodes(0, 1)
# sethdseed restoring and seeing txs to addresses out of the keypool
origin_rpc = self.nodes[1].get_wallet_rpc('origin')
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 4ff7f1d525..aad112b499 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -22,7 +22,6 @@ happened previously.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.address import AddressType
from test_framework.util import (
- connect_nodes,
assert_equal,
set_node_times,
)
@@ -160,13 +159,12 @@ class ImportRescanTest(BitcoinTestFramework):
# Import keys with pruning disabled
self.start_nodes(extra_args=[[]] * self.num_nodes)
- for n in self.nodes:
- n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
+ self.import_deterministic_coinbase_privkeys()
self.stop_nodes()
self.start_nodes()
for i in range(1, self.num_nodes):
- connect_nodes(self.nodes[i], 0)
+ self.connect_nodes(i, 0)
def run_test(self):
# Create one transaction on node 0 with a unique amount for
@@ -183,6 +181,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.nodes[0].generate(1) # Generate one block for each send
variant.confirmation_height = self.nodes[0].getblockcount()
variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
+ self.sync_all() # Conclude sync before calling setmocktime to avoid timeouts
# Generate a block further in the future (past the rescan window).
assert_equal(self.nodes[0].getrawmempool(), [])
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index bd4fcdabcf..f7fdd6e908 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -820,7 +820,7 @@ class ImportMultiTest(BitcoinTestFramework):
# Cannot import those pubkeys to keypool of wallet with privkeys
self.log.info("Pubkeys cannot be added to the keypool of a wallet with private keys")
- wrpc = self.nodes[1].get_wallet_rpc("")
+ wrpc = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
assert wrpc.getwalletinfo()['private_keys_enabled']
result = wrpc.importmulti(
[{
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index 40a2b3ab6a..51795aca23 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -143,7 +143,7 @@ class KeyPoolTest(BitcoinTestFramework):
w2 = nodes[0].get_wallet_rpc('w2')
# refer to initial wallet as w1
- w1 = nodes[0].get_wallet_rpc('')
+ w1 = nodes[0].get_wallet_rpc(self.default_wallet_name)
# import private key and fund it
address = addr.pop()
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index 102ed23fba..78e06c5916 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -16,7 +16,6 @@ import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
)
@@ -30,7 +29,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- wallet_path = os.path.join(self.nodes[1].datadir, self.chain, "wallets", "wallet.dat")
+ 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)
@@ -38,9 +37,9 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
shutil.copyfile(wallet_path, wallet_backup_path)
self.start_node(1, self.extra_args[1])
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[0], 3)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
+ self.connect_nodes(0, 3)
for i, output_type in enumerate(["legacy", "p2sh-segwit", "bech32"]):
@@ -72,7 +71,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(idx)
shutil.copyfile(wallet_backup_path, wallet_path)
self.start_node(idx, self.extra_args[idx])
- connect_nodes(self.nodes[0], idx)
+ self.connect_nodes(0, idx)
self.sync_all()
self.log.info("Verify keypool is restored and balance is correct")
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 6d51ca6c93..09a336b764 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -10,7 +10,6 @@ from test_framework.util import (
assert_array_result,
assert_equal,
assert_raises_rpc_error,
- connect_nodes,
)
from decimal import Decimal
@@ -26,7 +25,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
def run_test(self):
# 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)
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
self.nodes[2].generate(101)
self.sync_all()
@@ -36,6 +35,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
self.test_double_spend()
self.test_double_send()
self.double_spends_filtered()
+ self.test_targetconfirmations()
def test_no_blockhash(self):
self.log.info("Test no blockhash")
@@ -74,6 +74,27 @@ class ListSinceBlockTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'Z000000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].listsinceblock,
"Z000000000000000000000000000000000000000000000000000000000000000")
+ def test_targetconfirmations(self):
+ '''
+ This tests when the value of target_confirmations exceeds the number of
+ blocks in the main chain. In this case, the genesis block hash should be
+ given for the `lastblock` property. If target_confirmations is < 1, then
+ a -8 invalid parameter error is thrown.
+ '''
+ self.log.info("Test target_confirmations")
+ blockhash, = self.nodes[2].generate(1)
+ blockheight = self.nodes[2].getblockheader(blockhash)['height']
+ self.sync_all()
+
+ assert_equal(
+ self.nodes[0].getblockhash(0),
+ self.nodes[0].listsinceblock(blockhash, blockheight + 1)['lastblock'])
+ assert_equal(
+ self.nodes[0].getblockhash(0),
+ self.nodes[0].listsinceblock(blockhash, blockheight + 1000)['lastblock'])
+ assert_raises_rpc_error(-8, "Invalid parameter",
+ self.nodes[0].listsinceblock, blockhash, 0)
+
def test_reorg(self):
'''
`listsinceblock` did not behave correctly when handed a block that was
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 1872545cdb..61791a756c 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -21,8 +21,6 @@ from test_framework.util import (
get_rpc_proxy,
)
-FEATURE_LATEST = 169900
-
got_loading_error = False
def test_load_unload(node, name):
global got_loading_error
@@ -62,15 +60,17 @@ class MultiWalletTest(BitcoinTestFramework):
wallet = lambda name: node.get_wallet_rpc(name)
def wallet_file(name):
+ if name == self.default_wallet_name:
+ return wallet_dir(self.default_wallet_name, self.wallet_data_filename)
if os.path.isdir(wallet_dir(name)):
return wallet_dir(name, "wallet.dat")
return wallet_dir(name)
- assert_equal(self.nodes[0].listwalletdir(), { 'wallets': [{ 'name': '' }] })
+ assert_equal(self.nodes[0].listwalletdir(), { 'wallets': [{ 'name': self.default_wallet_name }] })
# check wallet.dat is created
self.stop_nodes()
- assert_equal(os.path.isfile(wallet_dir('wallet.dat')), True)
+ assert_equal(os.path.isfile(wallet_dir(self.default_wallet_name, self.wallet_data_filename)), True)
# create symlink to verify wallet directory path can be referenced
# through symlink
@@ -79,13 +79,18 @@ class MultiWalletTest(BitcoinTestFramework):
# rename wallet.dat to make sure plain wallet file paths (as opposed to
# directory paths) can be loaded
- os.rename(wallet_dir("wallet.dat"), wallet_dir("w8"))
-
# create another dummy wallet for use in testing backups later
- self.start_node(0, [])
+ self.start_node(0, ["-nowallet", "-wallet=empty", "-wallet=plain"])
+ node.createwallet("created")
self.stop_nodes()
empty_wallet = os.path.join(self.options.tmpdir, 'empty.dat')
- os.rename(wallet_dir("wallet.dat"), empty_wallet)
+ os.rename(wallet_file("empty"), empty_wallet)
+ shutil.rmtree(wallet_dir("empty"))
+ empty_created_wallet = os.path.join(self.options.tmpdir, 'empty.created.dat')
+ os.rename(wallet_dir("created", self.wallet_data_filename), empty_created_wallet)
+ shutil.rmtree(wallet_dir("created"))
+ os.rename(wallet_file("plain"), wallet_dir("w8"))
+ shutil.rmtree(wallet_dir("plain"))
# restart node with a mix of wallet names:
# w1, w2, w3 - to verify new wallets created when non-existing paths specified
@@ -95,10 +100,10 @@ class MultiWalletTest(BitcoinTestFramework):
# w7_symlink - to verify symlinked wallet path is initialized correctly
# w8 - to verify existing wallet file is loaded correctly
# '' - to verify default wallet file is created correctly
- wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', '']
- extra_args = ['-wallet={}'.format(n) for n in wallet_names]
+ wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', self.default_wallet_name]
+ extra_args = ['-nowallet'] + ['-wallet={}'.format(n) for n in wallet_names]
self.start_node(0, extra_args)
- assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8'])
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8'])
assert_equal(set(node.listwallets()), set(wallet_names))
@@ -109,7 +114,7 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if wallet path can't be created
exp_stderr = "boost::filesystem::create_directory:"
- self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
@@ -134,22 +139,17 @@ class MultiWalletTest(BitcoinTestFramework):
open(not_a_dir, 'a', encoding="utf8").close()
self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
- self.log.info("Do not allow -zapwallettxes with multiwallet")
- self.nodes[0].assert_start_raises_init_error(['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
- self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
- self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file")
-
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
os.rename(wallet_dir(), wallet_dir2)
- self.start_node(0, ['-wallet=w4', '-wallet=w5'])
+ self.start_node(0, ['-nowallet', '-wallet=w4', '-wallet=w5'])
assert_equal(set(node.listwallets()), {"w4", "w5"})
w5 = wallet("w5")
node.generatetoaddress(nblocks=1, address=w5.getnewaddress())
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
os.rename(wallet_dir2, wallet_dir())
- self.restart_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()])
+ self.restart_node(0, ['-nowallet', '-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()])
assert_equal(set(node.listwallets()), {"w4", "w5"})
w5 = wallet("w5")
w5_info = w5.getwalletinfo()
@@ -158,12 +158,12 @@ class MultiWalletTest(BitcoinTestFramework):
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
os.mkdir(competing_wallet_dir)
self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
- exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
+ exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\S*\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.restart_node(0, extra_args)
- assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy'])
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy'])
wallets = [wallet(w) for w in wallet_names]
wallet_bad = wallet("bad")
@@ -211,7 +211,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.restart_node(0, ['-nowallet'])
assert_equal(node.listwallets(), [])
- assert_raises_rpc_error(-32601, "Method not found", node.getwalletinfo)
+ assert_raises_rpc_error(-18, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)", node.getwalletinfo)
self.log.info("Load first wallet")
loadwallet_name = node.loadwallet(wallet_names[0])
@@ -249,13 +249,17 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].listwallets()), set(wallet_names))
# Fail to load if wallet doesn't exist
- assert_raises_rpc_error(-18, 'Wallet wallets not found.', self.nodes[0].loadwallet, 'wallets')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallets")
+ assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path), self.nodes[0].loadwallet, 'wallets')
# Fail to load duplicate wallets
- assert_raises_rpc_error(-4, 'Wallet file verification failed. Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat")
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])
# Fail to load duplicate wallets by different ways (directory and filepath)
- assert_raises_rpc_error(-4, "Wallet file verification failed. Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
+ if not self.options.descriptors:
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallet.dat")
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, 'wallet.dat')
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-4, "BerkeleyDatabase: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
@@ -269,12 +273,14 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load if a directory is specified that doesn't contain a wallet
os.mkdir(wallet_dir('empty_wallet_dir'))
- assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "empty_wallet_dir")
+ assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(path), self.nodes[0].loadwallet, 'empty_wallet_dir')
self.log.info("Test dynamic wallet creation.")
# Fail to create a wallet if it already exists.
- assert_raises_rpc_error(-4, "Wallet w2 already exists.", self.nodes[0].createwallet, 'w2')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w2")
+ assert_raises_rpc_error(-4, "Failed to create database path '{}'. Database already exists.".format(path), self.nodes[0].createwallet, 'w2')
# Successfully create a wallet with a new name
loadwallet_name = self.nodes[0].createwallet('w9')
@@ -318,14 +324,14 @@ class MultiWalletTest(BitcoinTestFramework):
for wallet_name in self.nodes[0].listwallets():
self.nodes[0].unloadwallet(wallet_name)
assert_equal(self.nodes[0].listwallets(), [])
- assert_raises_rpc_error(-32601, "Method not found (wallet method is disabled because no wallet is loaded)", self.nodes[0].getwalletinfo)
+ assert_raises_rpc_error(-18, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)", self.nodes[0].getwalletinfo)
# Successfully load a previously unloaded wallet
self.nodes[0].loadwallet('w1')
assert_equal(self.nodes[0].listwallets(), ['w1'])
assert_equal(w1.getwalletinfo()['walletname'], 'w1')
- assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), ['', os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy', 'w9'])
+ assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy', 'w9'])
# Test backing up and restoring wallets
self.log.info("Test wallet backup")
@@ -336,9 +342,11 @@ class MultiWalletTest(BitcoinTestFramework):
rpc = self.nodes[0].get_wallet_rpc(wallet_name)
addr = rpc.getnewaddress()
backup = os.path.join(self.options.tmpdir, 'backup.dat')
+ if os.path.exists(backup):
+ os.unlink(backup)
rpc.backupwallet(backup)
self.nodes[0].unloadwallet(wallet_name)
- shutil.copyfile(empty_wallet, wallet_file(wallet_name))
+ shutil.copyfile(empty_created_wallet if wallet_name == self.default_wallet_name else empty_wallet, wallet_file(wallet_name))
self.nodes[0].loadwallet(wallet_name)
assert_equal(rpc.getaddressinfo(addr)['ismine'], False)
self.nodes[0].unloadwallet(wallet_name)
@@ -350,7 +358,10 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(1)
wallet = os.path.join(self.options.tmpdir, 'my_wallet')
self.nodes[0].createwallet(wallet)
- assert_raises_rpc_error(-4, "Error initializing wallet database environment", self.nodes[1].loadwallet, wallet)
+ if self.options.descriptors:
+ assert_raises_rpc_error(-4, "Unable to obtain an exclusive lock", self.nodes[1].loadwallet, wallet)
+ else:
+ assert_raises_rpc_error(-4, "Error initializing wallet database environment", self.nodes[1].loadwallet, wallet)
self.nodes[0].unloadwallet(wallet)
self.nodes[1].loadwallet(wallet)
diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py
index 455f1fc5e8..a1d6b098ad 100755
--- a/test/functional/wallet_reorgsrestore.py
+++ b/test/functional/wallet_reorgsrestore.py
@@ -20,8 +20,6 @@ import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- disconnect_nodes,
)
class ReorgsRestoreTest(BitcoinTestFramework):
@@ -38,9 +36,9 @@ class ReorgsRestoreTest(BitcoinTestFramework):
self.sync_blocks()
# Disconnect node1 from others to reorg its chain later
- disconnect_nodes(self.nodes[0], 1)
- disconnect_nodes(self.nodes[1], 2)
- connect_nodes(self.nodes[0], 2)
+ self.disconnect_nodes(0, 1)
+ self.disconnect_nodes(1, 2)
+ self.connect_nodes(0, 2)
# Send a tx to be unconfirmed later
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
@@ -50,7 +48,7 @@ class ReorgsRestoreTest(BitcoinTestFramework):
assert_equal(tx_before_reorg["confirmations"], 4)
# Disconnect node0 from node2 to broadcast a conflict on their respective chains
- disconnect_nodes(self.nodes[0], 2)
+ self.disconnect_nodes(0, 2)
nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from)["details"] if tx_out["amount"] == Decimal("10"))
inputs = []
inputs.append({"txid": txid_conflict_from, "vout": nA})
@@ -69,7 +67,7 @@ class ReorgsRestoreTest(BitcoinTestFramework):
self.nodes[2].generate(9)
# Reconnect node0 and node2 and check that conflicted_txid is effectively conflicted
- connect_nodes(self.nodes[0], 2)
+ self.connect_nodes(0, 2)
self.sync_blocks([self.nodes[0], self.nodes[2]])
conflicted = self.nodes[0].gettransaction(conflicted_txid)
conflicting = self.nodes[0].gettransaction(conflicting_txid)
@@ -89,7 +87,7 @@ class ReorgsRestoreTest(BitcoinTestFramework):
# Node0 wallet file is loaded on longest sync'ed node1
self.stop_node(1)
self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
- shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, self.chain, 'wallet.dat'))
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, self.chain, self.default_wallet_name, self.wallet_data_filename))
self.start_node(1)
tx_after_reorg = self.nodes[1].gettransaction(txid)
# Check that normal confirmed tx is confirmed again but with different blockhash
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 3417616d77..353deefcc1 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -7,9 +7,10 @@ import time
from test_framework.blocktools import create_block, create_coinbase
from test_framework.messages import ToHex
-from test_framework.mininode import P2PTxInvStore, mininode_lock
+from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, wait_until
+from test_framework.util import assert_equal
+
class ResendWalletTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -21,22 +22,22 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0] # alias
- node.add_p2p_connection(P2PTxInvStore())
+ peer_first = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a new transaction and wait until it's broadcast")
- txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16)
+ txid = node.sendtoaddress(node.getnewaddress(), 1)
# Wallet rebroadcast is first scheduled 1 sec after startup (see
- # nNextResend in ResendWalletTransactions()). Sleep for just over a
- # second to be certain that it has been called before the first
+ # nNextResend in ResendWalletTransactions()). Tell scheduler to call
+ # MaybeResendWalletTxn now to initialize nNextResend before the first
# setmocktime call below.
- time.sleep(1.1)
+ node.mockscheduler(1)
# Can take a few seconds due to transaction trickling
- wait_until(lambda: node.p2p.tx_invs_received[txid] >= 1, lock=mininode_lock)
+ peer_first.wait_for_broadcast([txid])
# Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown)
- node.add_p2p_connection(P2PTxInvStore())
+ peer_second = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a block")
# Create and submit a block without the transaction.
@@ -57,14 +58,19 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
twelve_hrs = 12 * 60 * 60
two_min = 2 * 60
node.setmocktime(now + twelve_hrs - two_min)
- time.sleep(2) # ensure enough time has passed for rebroadcast attempt to occur
- assert_equal(txid in node.p2ps[1].get_invs(), False)
+ node.mockscheduler(1) # Tell scheduler to call MaybeResendWalletTxn now
+ assert_equal(int(txid, 16) in peer_second.get_invs(), False)
self.log.info("Bump time & check that transaction is rebroadcast")
# Transaction should be rebroadcast approximately 24 hours in the future,
# but can range from 12-36. So bump 36 hours to be sure.
- node.setmocktime(now + 36 * 60 * 60)
- wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock)
+ with node.assert_debug_log(['ResendWalletTransactions: resubmit 1 unconfirmed transactions']):
+ node.setmocktime(now + 36 * 60 * 60)
+ # Tell scheduler to call MaybeResendWalletTxn now.
+ node.mockscheduler(1)
+ # Give some time for trickle to occur
+ node.setmocktime(now + 36 * 60 * 60 + 600)
+ peer_second.wait_for_broadcast([txid])
if __name__ == '__main__':
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
new file mode 100755
index 0000000000..60ab2d457e
--- /dev/null
+++ b/test/functional/wallet_send.py
@@ -0,0 +1,345 @@
+#!/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 the send RPC command."""
+
+from decimal import Decimal, getcontext
+from test_framework.authproxy import JSONRPCException
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_fee_amount,
+ assert_greater_than,
+ assert_raises_rpc_error
+)
+
+class WalletSendTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ # whitelist all peers to speed up tx relay / mempool sync
+ self.extra_args = [
+ ["-whitelist=127.0.0.1","-walletrbf=1"],
+ ["-whitelist=127.0.0.1","-walletrbf=1"],
+ ]
+ getcontext().prec = 8 # Satoshi precision for Decimal
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
+ arg_conf_target=None, arg_estimate_mode=None,
+ conf_target=None, estimate_mode=None, add_to_wallet=None, psbt=None,
+ inputs=None, add_inputs=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()
+ if to_wallet is None:
+ assert amount is None
+ else:
+ to_untrusted_pending_before = to_wallet.getbalances()["mine"]["untrusted_pending"]
+
+ if amount:
+ dest = to_wallet.getnewaddress()
+ outputs = {dest: amount}
+ else:
+ outputs = {"data": data}
+
+ # Construct options dictionary
+ options = {}
+ if add_to_wallet is not None:
+ options["add_to_wallet"] = add_to_wallet
+ else:
+ if psbt:
+ add_to_wallet = False
+ else:
+ add_to_wallet = from_wallet.getwalletinfo()["private_keys_enabled"] # Default value
+ if psbt is not None:
+ options["psbt"] = psbt
+ if conf_target is not None:
+ options["conf_target"] = conf_target
+ if estimate_mode is not None:
+ options["estimate_mode"] = estimate_mode
+ if inputs is not None:
+ options["inputs"] = inputs
+ if add_inputs is not None:
+ options["add_inputs"] = add_inputs
+ if change_address is not None:
+ options["change_address"] = change_address
+ if change_position is not None:
+ options["change_position"] = change_position
+ if change_type is not None:
+ options["change_type"] = change_type
+ if include_watching is not None:
+ options["include_watching"] = include_watching
+ if locktime is not None:
+ options["locktime"] = locktime
+ if lock_unspents is not None:
+ options["lock_unspents"] = lock_unspents
+ if replaceable is None:
+ replaceable = True # default
+ else:
+ options["replaceable"] = replaceable
+ if subtract_fee_from_outputs is not None:
+ options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
+
+ if len(options.keys()) == 0:
+ options = None
+
+ if expect_error is None:
+ res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
+ else:
+ try:
+ assert_raises_rpc_error(expect_error[0], expect_error[1], from_wallet.send,
+ outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
+ except AssertionError:
+ # Provide debug info if the test fails
+ self.log.error("Unexpected successful result:")
+ self.log.error(options)
+ res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
+ self.log.error(res)
+ if "txid" in res and add_to_wallet:
+ self.log.error("Transaction details:")
+ try:
+ tx = from_wallet.gettransaction(res["txid"])
+ self.log.error(tx)
+ self.log.error("testmempoolaccept (transaction may already be in mempool):")
+ self.log.error(from_wallet.testmempoolaccept([tx["hex"]]))
+ except JSONRPCException as exc:
+ self.log.error(exc)
+
+ raise
+
+ return
+
+ if locktime:
+ return res
+
+ if from_wallet.getwalletinfo()["private_keys_enabled"] and not include_watching:
+ assert_equal(res["complete"], True)
+ assert "txid" in res
+ else:
+ assert_equal(res["complete"], False)
+ assert not "txid" in res
+ assert "psbt" in res
+
+ if add_to_wallet and not include_watching:
+ # Ensure transaction exists in the wallet:
+ tx = from_wallet.gettransaction(res["txid"])
+ assert tx
+ assert_equal(tx["bip125-replaceable"], "yes" if replaceable else "no")
+ # Ensure transaction exists in the mempool:
+ tx = from_wallet.getrawtransaction(res["txid"], True)
+ assert tx
+ if amount:
+ if subtract_fee_from_outputs:
+ assert_equal(from_balance_before - from_wallet.getbalance(), amount)
+ else:
+ assert_greater_than(from_balance_before - from_wallet.getbalance(), 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())
+
+ if to_wallet:
+ self.sync_mempools()
+ if add_to_wallet:
+ if not subtract_fee_from_outputs:
+ assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before + Decimal(amount if amount else 0))
+ else:
+ assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before)
+
+ return res
+
+ def run_test(self):
+ self.log.info("Setup wallets...")
+ # w0 is a wallet with coinbase rewards
+ w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ # w1 is a regular wallet
+ self.nodes[1].createwallet(wallet_name="w1")
+ w1 = self.nodes[1].get_wallet_rpc("w1")
+ # w2 contains the private keys for w3
+ self.nodes[1].createwallet(wallet_name="w2")
+ w2 = self.nodes[1].get_wallet_rpc("w2")
+ # w3 is a watch-only wallet, based on w2
+ self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
+ w3 = self.nodes[1].get_wallet_rpc("w3")
+ for _ in range(3):
+ a2_receive = w2.getnewaddress()
+ a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
+ res = w3.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "watchonly": True
+ },{
+ "desc": w2.getaddressinfo(a2_change)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "internal": True,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}, {"success": True}])
+
+ w0.sendtoaddress(a2_receive, 10) # fund w3
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ # w4 has private keys enabled, but only contains watch-only keys (from w2)
+ self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
+ w4 = self.nodes[1].get_wallet_rpc("w4")
+ for _ in range(3):
+ a2_receive = w2.getnewaddress()
+ res = w4.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": False,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}])
+
+ w0.sendtoaddress(a2_receive, 10) # fund w4
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ self.log.info("Send to address...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True)
+
+ self.log.info("Don't broadcast...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
+ assert(res["hex"])
+
+ self.log.info("Return PSBT...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True)
+ assert(res["psbt"])
+
+ self.log.info("Create transaction that spends to address, but don't broadcast...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
+ # conf_target & estimate_mode can be set as argument or option
+ res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical", add_to_wallet=False)
+ res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=1, estimate_mode="economical", add_to_wallet=False)
+ assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],
+ self.nodes[1].decodepsbt(res2["psbt"])["fee"])
+ # but not at the same time
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical",
+ conf_target=1, estimate_mode="economical", add_to_wallet=False, expect_error=(-8,"Use either conf_target and estimate_mode or the options dictionary to control fee rate"))
+
+ self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")
+ res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
+ self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Create OP_RETURN...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
+ self.test_send(from_wallet=w0, data="Hello World", expect_error=(-8, "Data must be hexadecimal string (not 'Hello World')"))
+ self.test_send(from_wallet=w0, data="23")
+ res = self.test_send(from_wallet=w3, data="23")
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Set fee rate...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=2, estimate_mode="sat/b", add_to_wallet=False)
+ fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
+ assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00002"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=-1, estimate_mode="sat/b",
+ expect_error=(-3, "Amount out of range"))
+ # Fee rate of 0.1 satoshi per byte should throw an error
+ # TODO: error should use sat/b
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b",
+ expect_error=(-4, "Fee rate (0.00000100 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"))
+
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.000001, estimate_mode="BTC/KB",
+ expect_error=(-4, "Fee rate (0.00000100 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"))
+
+ # 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)
+ # assert res["hex"]
+ # hex = res["hex"]
+ # res = self.nodes[0].testmempoolaccept([hex])
+ # assert not res[0]["allowed"]
+ # assert_equal(res[0]["reject-reason"], "...") # low fee
+ # assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.000001"))
+
+ self.log.info("If inputs are specified, do not automatically add more...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[], add_to_wallet=False)
+ assert res["complete"]
+ utxo1 = w0.listunspent()[0]
+ assert_equal(utxo1["amount"], 50)
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1],
+ expect_error=(-4, "Insufficient funds"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=False,
+ expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=True, add_to_wallet=False)
+ assert res["complete"]
+
+ self.log.info("Manual change address and position...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, change_address="not an address",
+ expect_error=(-5, "Change address must be a valid bitcoin address"))
+ change_address = w0.getnewaddress()
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address)
+ assert res["complete"]
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)
+ assert res["complete"]
+ assert_equal(self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["addresses"], [change_address])
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)
+ assert res["complete"]
+ change_address = self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["addresses"][0]
+ assert change_address[0] == "m" or change_address[0] == "n"
+
+ self.log.info("Set lock time...")
+ height = self.nodes[0].getblockchaininfo()["blocks"]
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, locktime=height + 1)
+ assert res["complete"]
+ assert res["txid"]
+ txid = res["txid"]
+ # Although the wallet finishes the transaction, it can't be added to the mempool yet:
+ hex = self.nodes[0].gettransaction(res["txid"])["hex"]
+ res = self.nodes[0].testmempoolaccept([hex])
+ assert not res[0]["allowed"]
+ assert_equal(res[0]["reject-reason"], "non-final")
+ # It shouldn't be confirmed in the next block
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 0)
+ # The mempool should allow it now:
+ res = self.nodes[0].testmempoolaccept([hex])
+ assert res[0]["allowed"]
+ # Don't wait for wallet to add it to the mempool:
+ res = self.nodes[0].sendrawtransaction(hex)
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 1)
+ self.sync_all()
+
+ self.log.info("Lock unspents...")
+ utxo1 = w0.listunspent()[0]
+ assert_greater_than(utxo1["amount"], 1)
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False, lock_unspents=True)
+ assert res["complete"]
+ locked_coins = w0.listlockunspent()
+ assert_equal(len(locked_coins), 1)
+ # Locked coins are automatically unlocked when manually selected
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False)
+ assert res["complete"]
+
+ self.log.info("Replaceable...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=True)
+ assert res["complete"]
+ assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "yes")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=False)
+ assert res["complete"]
+ assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "no")
+
+ self.log.info("Subtract fee from output")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
+
+
+if __name__ == '__main__':
+ WalletSendTest().main()
diff --git a/test/functional/wallet_startup.py b/test/functional/wallet_startup.py
new file mode 100755
index 0000000000..d3119925f7
--- /dev/null
+++ b/test/functional/wallet_startup.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test wallet load on startup.
+
+Verify that a bitcoind node can maintain list of wallets loading on startup
+"""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+
+class WalletStartupTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.supports_cli = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def setup_nodes(self):
+ self.add_nodes(self.num_nodes)
+ self.start_nodes()
+
+ def run_test(self):
+ self.log.info('Should start without any wallets')
+ assert_equal(self.nodes[0].listwallets(), [])
+ assert_equal(self.nodes[0].listwalletdir(), {'wallets': []})
+
+ self.log.info('New default wallet should load by default when there are no other wallets')
+ self.nodes[0].createwallet(wallet_name='', load_on_startup=False)
+ self.restart_node(0)
+ assert_equal(self.nodes[0].listwallets(), [''])
+
+ self.log.info('Test load on startup behavior')
+ self.nodes[0].createwallet(wallet_name='w0', load_on_startup=True)
+ self.nodes[0].createwallet(wallet_name='w1', load_on_startup=False)
+ self.nodes[0].createwallet(wallet_name='w2', load_on_startup=True)
+ self.nodes[0].createwallet(wallet_name='w3', load_on_startup=False)
+ self.nodes[0].createwallet(wallet_name='w4', load_on_startup=False)
+ self.nodes[0].unloadwallet(wallet_name='w0', load_on_startup=False)
+ self.nodes[0].unloadwallet(wallet_name='w4', load_on_startup=False)
+ self.nodes[0].loadwallet(filename='w4', load_on_startup=True)
+ assert_equal(set(self.nodes[0].listwallets()), set(('', 'w1', 'w2', 'w3', 'w4')))
+ self.restart_node(0)
+ assert_equal(set(self.nodes[0].listwallets()), set(('', 'w2', 'w4')))
+ self.nodes[0].unloadwallet(wallet_name='', load_on_startup=False)
+ self.nodes[0].unloadwallet(wallet_name='w4', load_on_startup=False)
+ self.nodes[0].loadwallet(filename='w3', load_on_startup=True)
+ self.nodes[0].loadwallet(filename='')
+ self.restart_node(0)
+ assert_equal(set(self.nodes[0].listwallets()), set(('w2', 'w3')))
+
+if __name__ == '__main__':
+ WalletStartupTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 5e1a804d33..bdbbb3e530 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -8,8 +8,6 @@ import io
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- disconnect_nodes,
)
from test_framework.messages import CTransaction, COIN
@@ -25,12 +23,12 @@ class TxnMallTest(BitcoinTestFramework):
parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true",
help="Test double-spend of 1-confirmed transaction")
parser.add_argument("--segwit", dest="segwit", default=False, action="store_true",
- help="Test behaviour with SegWit txn (which should fail")
+ help="Test behaviour with SegWit txn (which should fail)")
def setup_network(self):
# Start with split network:
super().setup_network()
- disconnect_nodes(self.nodes[1], 2)
+ self.disconnect_nodes(1, 2)
def run_test(self):
if self.options.segwit:
@@ -118,7 +116,7 @@ class TxnMallTest(BitcoinTestFramework):
self.nodes[2].generate(1)
# Reconnect the split network, and sync chain:
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
self.nodes[2].sendrawtransaction(node0_tx2["hex"])
self.nodes[2].sendrawtransaction(tx2["hex"])
self.nodes[2].generate(1) # Mine another block to make sure we sync
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index cac58aeaf2..42de131354 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -8,8 +8,6 @@ from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- connect_nodes,
- disconnect_nodes,
find_output,
)
@@ -28,7 +26,7 @@ class TxnMallTest(BitcoinTestFramework):
def setup_network(self):
# Start with split network:
super().setup_network()
- disconnect_nodes(self.nodes[1], 2)
+ self.disconnect_nodes(1, 2)
def run_test(self):
# All nodes should start with 1,250 BTC:
@@ -116,7 +114,7 @@ class TxnMallTest(BitcoinTestFramework):
self.nodes[2].generate(1)
# Reconnect the split network, and sync chain:
- connect_nodes(self.nodes[1], 2)
+ self.connect_nodes(1, 2)
self.nodes[2].generate(1) # Mine another block to make sure we sync
self.sync_blocks()
assert_equal(self.nodes[0].gettransaction(doublespend_txid)["confirmations"], 2)
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index 1a76f65215..446a601aee 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -6,7 +6,7 @@
Test upgradewallet RPC. Download node binaries:
-contrib/devtools/previous_release.py -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
Only v0.15.2 and v0.16.3 are required by this test. The others are used in feature_backwards_compatibility.py
"""
@@ -31,6 +31,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
["-usehd=1"], # v0.16.3 wallet
["-usehd=0"] # v0.15.2 wallet
]
+ self.wallet_names = [self.default_wallet_name, None, None]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -46,6 +47,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
150200,
])
self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
def dumb_sync_blocks(self):
"""
diff --git a/test/functional/wallet_zapwallettxes.py b/test/functional/wallet_zapwallettxes.py
deleted file mode 100755
index 7f1cdbd20b..0000000000
--- a/test/functional/wallet_zapwallettxes.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2014-2018 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test the zapwallettxes functionality.
-
-- start two bitcoind nodes
-- create two transactions on node 0 - one is confirmed and one is unconfirmed.
-- restart node 0 and verify that both the confirmed and the unconfirmed
- transactions are still available.
-- restart node 0 with zapwallettxes and persistmempool, and verify that both
- the confirmed and the unconfirmed transactions are still available.
-- restart node 0 with just zapwallettxes and verify that the confirmed
- transactions are still available, but that the unconfirmed transaction has
- been zapped.
-"""
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- assert_raises_rpc_error,
- wait_until,
-)
-
-class ZapWalletTXesTest (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):
- self.log.info("Mining blocks...")
- self.nodes[0].generate(1)
- self.sync_all()
- self.nodes[1].generate(100)
- self.sync_all()
-
- # This transaction will be confirmed
- txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
-
- self.nodes[0].generate(1)
- self.sync_all()
-
- # This transaction will not be confirmed
- txid2 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 20)
-
- # Confirmed and unconfirmed transactions are now in the wallet.
- assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
- assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
-
- # Restart node0. Both confirmed and unconfirmed transactions remain in the wallet.
- self.restart_node(0)
-
- assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
- assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
-
- # Restart node0 with zapwallettxes and persistmempool. The unconfirmed
- # transaction is zapped from the wallet, but is re-added when the mempool is reloaded.
- self.restart_node(0, ["-persistmempool=1", "-zapwallettxes=2"])
-
- wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3)
- self.nodes[0].syncwithvalidationinterfacequeue() # Flush mempool to wallet
-
- assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
- assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
-
- # Restart node0 with zapwallettxes, but not persistmempool.
- # The unconfirmed transaction is zapped and is no longer in the wallet.
- self.restart_node(0, ["-zapwallettxes=2"])
-
- # tx1 is still be available because it was confirmed
- assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
-
- # This will raise an exception because the unconfirmed transaction has been zapped
- assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2)
-
-if __name__ == '__main__':
- ZapWalletTXesTest().main()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index 56b18752ec..c7895edbcc 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -56,6 +56,14 @@ def main():
'--m_dir',
help='Merge inputs from this directory into the seed_dir. Needs /target subdirectory.',
)
+ parser.add_argument(
+ '-g',
+ '--generate',
+ action='store_true',
+ help='Create new corpus seeds (or extend the existing ones) by running'
+ ' the given targets for a finite number of times. Outputs them to'
+ ' the passed seed_dir.'
+ )
args = parser.parse_args()
@@ -100,19 +108,20 @@ def main():
logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))
- test_list_seedless = []
- for t in test_list_selection:
- corpus_path = os.path.join(args.seed_dir, t)
- if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0:
- test_list_seedless.append(t)
- test_list_seedless.sort()
- if test_list_seedless:
- logging.info(
- "Fuzzing harnesses lacking a seed corpus: {}".format(
- " ".join(test_list_seedless)
+ if not args.generate:
+ test_list_seedless = []
+ for t in test_list_selection:
+ corpus_path = os.path.join(args.seed_dir, t)
+ if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0:
+ test_list_seedless.append(t)
+ test_list_seedless.sort()
+ if test_list_seedless:
+ logging.info(
+ "Fuzzing harnesses lacking a seed corpus: {}".format(
+ " ".join(test_list_seedless)
+ )
)
- )
- logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets")
+ logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets")
try:
help_output = subprocess.run(
@@ -133,6 +142,14 @@ def main():
sys.exit(1)
with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
+ if args.generate:
+ return generate_corpus_seeds(
+ fuzz_pool=fuzz_pool,
+ build_dir=config["environment"]["BUILDDIR"],
+ seed_dir=args.seed_dir,
+ targets=test_list_selection,
+ )
+
if args.m_dir:
merge_inputs(
fuzz_pool=fuzz_pool,
@@ -152,6 +169,37 @@ def main():
)
+def generate_corpus_seeds(*, fuzz_pool, build_dir, seed_dir, targets):
+ """Generates new corpus seeds.
+
+ Run {targets} without input, and outputs the generated corpus seeds to
+ {seed_dir}.
+ """
+ logging.info("Generating corpus seeds to {}".format(seed_dir))
+
+ def job(command):
+ logging.debug("Running '{}'\n".format(" ".join(command)))
+ logging.debug("Command '{}' output:\n'{}'\n".format(
+ ' '.join(command),
+ subprocess.run(command, check=True, stderr=subprocess.PIPE,
+ universal_newlines=True).stderr
+ ))
+
+ futures = []
+ for target in targets:
+ target_seed_dir = os.path.join(seed_dir, target)
+ os.makedirs(target_seed_dir, exist_ok=True)
+ command = [
+ os.path.join(build_dir, "src", "test", "fuzz", target),
+ "-runs=100000",
+ target_seed_dir,
+ ]
+ futures.append(fuzz_pool.submit(job, command))
+
+ for future in as_completed(futures):
+ future.result()
+
+
def merge_inputs(*, fuzz_pool, corpus, test_list, build_dir, merge_dir):
logging.info("Merge the inputs in the passed dir into the seed_dir. Passed dir {}".format(merge_dir))
jobs = []
diff --git a/contrib/devtools/previous_release.py b/test/get_previous_releases.py
index 5599051cf3..1348b8246b 100755
--- a/contrib/devtools/previous_release.py
+++ b/test/get_previous_releases.py
@@ -20,6 +20,40 @@ import sys
import hashlib
+SHA256_SUMS = {
+"d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz",
+"54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz",
+"2b843506c3f1af0eeca5854a920264f9a829f02d0d50328005950ddcbe88874d": "bitcoin-0.15.2-i686-pc-linux-gnu.tar.gz",
+"87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": "bitcoin-0.15.2-osx64.tar.gz",
+"566be44190fd76daa01f13d428939dadfb8e3daacefc8fa17f433cad28f73bd5": "bitcoin-0.15.2-x86_64-linux-gnu.tar.gz",
+
+"0768c6c15caffbaca6524824c9563b42c24f70633c681c2744649158aa3fd484": "bitcoin-0.16.3-aarch64-linux-gnu.tar.gz",
+"fb2818069854a6ad20ea03b28b55dbd35d8b1f7d453e90b83eace5d0098a2a87": "bitcoin-0.16.3-arm-linux-gnueabihf.tar.gz",
+"75a537844313b0a84bdb61ffcdc5c4ce19a738f7ddf71007cd2edf664efd7c37": "bitcoin-0.16.3-i686-pc-linux-gnu.tar.gz",
+"78c3bff3b619a19aed575961ea43cc9e142959218835cf51aede7f0b764fc25d": "bitcoin-0.16.3-osx64.tar.gz",
+"5d422a9d544742bc0df12427383f9c2517433ce7b58cf672b9a9b17c2ef51e4f": "bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
+
+"5a6b35d1a348a402f2d2d6ab5aed653a1a1f13bc63aaaf51605e3501b0733b7a": "bitcoin-0.17.2-aarch64-linux-gnu.tar.gz",
+"d1913a5d19c8e8da4a67d1bd5205d03c8614dfd2e02bba2fe3087476643a729e": "bitcoin-0.17.2-arm-linux-gnueabihf.tar.gz",
+"d295fc93f39bbf0fd937b730a93184899a2eb6c3a6d53f3d857cbe77ef89b98c": "bitcoin-0.17.2-i686-pc-linux-gnu.tar.gz",
+"a783ba20706dbfd5b47fbedf42165fce70fbbc7d78003305d964f6b3da14887f": "bitcoin-0.17.2-osx64.tar.gz",
+"943f9362b9f11130177839116f48f809d83478b4c28591d486ee9a7e35179da6": "bitcoin-0.17.2-x86_64-linux-gnu.tar.gz",
+
+"88f343af72803b851c7da13874cc5525026b0b55e63e1b5e1298390c4688adc6": "bitcoin-0.18.1-aarch64-linux-gnu.tar.gz",
+"cc7d483e4b20c5dabd4dcaf304965214cf4934bcc029ca99cbc9af00d3771a1f": "bitcoin-0.18.1-arm-linux-gnueabihf.tar.gz",
+"989e847b3e95fc9fedc0b109cae1b4fa43348f2f712e187a118461876af9bd16": "bitcoin-0.18.1-i686-pc-linux-gnu.tar.gz",
+"b7bbcee7a7540f711b171d6981f939ca8482005fde22689bc016596d80548bb1": "bitcoin-0.18.1-osx64.tar.gz",
+"425ee5ec631ae8da71ebc1c3f5c0269c627cf459379b9b030f047107a28e3ef8": "bitcoin-0.18.1-riscv64-linux-gnu.tar.gz",
+"600d1db5e751fa85903e935a01a74f5cc57e1e7473c15fd3e17ed21e202cfe5a": "bitcoin-0.18.1-x86_64-linux-gnu.tar.gz",
+
+"3a80431717842672df682bdb619e66523b59541483297772a7969413be3502ff": "bitcoin-0.19.1-aarch64-linux-gnu.tar.gz",
+"657f28213823d240dd3324d14829702f9ad6f0710f8bdd1c379cb3c447197f48": "bitcoin-0.19.1-arm-linux-gnueabihf.tar.gz",
+"10d1e53208aa7603022f4acc084a046299ab4ccf25fe01e81b3fb6f856772589": "bitcoin-0.19.1-i686-pc-linux-gnu.tar.gz",
+"1ae1b87de26487075cd2fd22e0d4ead87d969bd55c44f2f1d873ecdc6147ebb3": "bitcoin-0.19.1-osx64.tar.gz",
+"aa7a9563b48aa79252c8e7b6a41c07a5441bd9f14c5e4562cc72720ea6cb0ee5": "bitcoin-0.19.1-riscv64-linux-gnu.tar.gz",
+"5fcac9416e486d4960e1a946145566350ca670f9aaba99de6542080851122e4c": "bitcoin-0.19.1-x86_64-linux-gnu.tar.gz"
+}
+
@contextlib.contextmanager
def pushd(new_dir) -> None:
previous_dir = os.getcwd()
@@ -44,14 +78,10 @@ def download_binary(tag, args) -> int:
match.group(1), match.group(2))
tarball = 'bitcoin-{tag}-{platform}.tar.gz'.format(
tag=tag[1:], platform=args.platform)
- sha256Sums = "SHA256SUMS-{tag}.asc".format(tag=tag[1:])
tarballUrl = 'https://bitcoincore.org/{bin_path}/{tarball}'.format(
bin_path=bin_path, tarball=tarball)
- sha256SumsUrl = 'https://bitcoincore.org/{bin_path}/SHA256SUMS.asc'.format(
- bin_path=bin_path)
print('Fetching: {tarballUrl}'.format(tarballUrl=tarballUrl))
- print('Fetching: {sha256SumsUrl}'.format(sha256SumsUrl=sha256SumsUrl))
header, status = subprocess.Popen(
['curl', '--head', tarballUrl], stdout=subprocess.PIPE).communicate()
@@ -60,8 +90,7 @@ def download_binary(tag, args) -> int:
return 1
curlCmds = [
- ['curl', '--remote-name', tarballUrl],
- ['curl', '--output', sha256Sums, sha256SumsUrl],
+ ['curl', '--remote-name', tarballUrl]
]
for cmd in curlCmds:
@@ -73,29 +102,12 @@ def download_binary(tag, args) -> int:
with open(tarball, "rb") as afile:
hasher.update(afile.read())
tarballHash = hasher.hexdigest()
- tarballHash = '{} {}\n'.format(tarballHash, tarball)
- with open(sha256Sums, 'r', encoding="utf-8") as afile:
- shasums = afile.readlines()
- if tarballHash not in shasums:
+ if tarballHash not in SHA256_SUMS or SHA256_SUMS[tarballHash] != tarball:
print("Checksum did not match")
- Path(tarball).unlink()
return 1
print("Checksum matched")
- # Bitcoin Core Release Signing Keys v0.11.0+
- signingKey = "01EA5486DE18A882D4C2684590C8019E36C2E964"
-
- isKeyPresent = subprocess.run(
- ["gpg", "--list-keys", signingKey]).returncode
- if isKeyPresent:
- return isKeyPresent
-
- isVerified = subprocess.run(
- ["gpg", "--verify", sha256Sums]).returncode
- if isVerified:
- return isVerified
-
# Extract tarball
ret = subprocess.run(['tar', '-zxf', tarball, '-C', tag,
'--strip-components=1',
@@ -104,7 +116,6 @@ def download_binary(tag, args) -> int:
return ret
Path(tarball).unlink()
- Path(sha256Sums).unlink()
return 0
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index bd947d194c..f77242d335 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -23,7 +23,7 @@ CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWallet
CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR)
CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR)
# list unsupported, deprecated and duplicate args as they need no documentation
-SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb'])
+SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb', '-zapwallettxes'])
def lint_missing_argument_documentation():
diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh
index ff3f784437..827c978bed 100755
--- a/test/lint/commit-script-check.sh
+++ b/test/lint/commit-script-check.sh
@@ -37,7 +37,7 @@ for commit in $(git rev-list --reverse $1); do
git reset --quiet --hard HEAD
else
if git rev-list "--format=%b" -n1 $commit | grep -q '^-\(BEGIN\|END\)[ a-zA-Z]*-$'; then
- echo "Error: script block marker but no scripted-diff in title"
+ echo "Error: script block marker but no scripted-diff in title of commit $commit"
echo "Failed"
RET=1
fi
diff --git a/test/lint/lint-cpp.sh b/test/lint/lint-cpp.sh
new file mode 100755
index 0000000000..cac57b968d
--- /dev/null
+++ b/test/lint/lint-cpp.sh
@@ -0,0 +1,21 @@
+#!/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.
+#
+# Check for various C++ code patterns we want to avoid.
+
+export LC_ALL=C
+
+EXIT_CODE=0
+
+OUTPUT=$(git grep -E "boost::bind\(" -- "*.cpp" "*.h")
+if [[ ${OUTPUT} != "" ]]; then
+ echo "Use of boost::bind detected. Use std::bind instead."
+ echo
+ echo "${OUTPUT}"
+ EXIT_CODE=1
+fi
+
+exit ${EXIT_CODE} \ No newline at end of file
diff --git a/test/lint/lint-git-commit-check.sh b/test/lint/lint-git-commit-check.sh
index 8947f67bf6..ecaad215c4 100755
--- a/test/lint/lint-git-commit-check.sh
+++ b/test/lint/lint-git-commit-check.sh
@@ -23,10 +23,18 @@ while getopts "?" opt; do
esac
done
+# TRAVIS_BRANCH will be present in a Travis environment. For builds triggered
+# by a pull request this is the name of the branch targeted by the pull request.
+# https://docs.travis-ci.com/user/environment-variables/
+if [ -n "${TRAVIS_BRANCH}" ]; then
+ COMMIT_RANGE="$TRAVIS_BRANCH..HEAD"
+fi
+
if [ -z "${COMMIT_RANGE}" ]; then
if [ -n "$1" ]; then
COMMIT_RANGE="HEAD~$1...HEAD"
else
+ # This assumes that the target branch of the pull request will be master.
MERGE_BASE=$(git merge-base HEAD master)
COMMIT_RANGE="$MERGE_BASE..HEAD"
fi
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 2e5b801849..e5657f7555 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -4,6 +4,39 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
export LC_ALL=C
+
+# Be aware that bitcoind and bitcoin-qt differ in terms of localization: Qt
+# opts in to POSIX localization by running setlocale(LC_ALL, "") on startup,
+# whereas no such call is made in bitcoind.
+#
+# Qt runs setlocale(LC_ALL, "") on initialization. This installs the locale
+# specified by the user's LC_ALL (or LC_*) environment variable as the new
+# C locale.
+#
+# In contrast, bitcoind does not opt in to localization -- no call to
+# setlocale(LC_ALL, "") is made and the environment variables LC_* are
+# thus ignored.
+#
+# This results in situations where bitcoind is guaranteed to be running
+# with the classic locale ("C") whereas the locale of bitcoin-qt will vary
+# depending on the user's environment variables.
+#
+# An example: Assuming the environment variable LC_ALL=de_DE then the
+# call std::to_string(1.23) will return "1.230000" in bitcoind but
+# "1,230000" in bitcoin-qt.
+#
+# From the Qt documentation:
+# "On Unix/Linux Qt is configured to use the system locale settings by default.
+# This can cause a conflict when using POSIX functions, for instance, when
+# converting between data types such as floats and strings, since the notation
+# may differ between locales. To get around this problem, call the POSIX function
+# setlocale(LC_NUMERIC,"C") right after initializing QApplication, QGuiApplication
+# or QCoreApplication to reset the locale that is used for number formatting to
+# "C"-locale."
+#
+# See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings and
+# https://stackoverflow.com/a/34878283 for more details.
+
KNOWN_VIOLATIONS=(
"src/bitcoin-tx.cpp.*stoul"
"src/bitcoin-tx.cpp.*trim_right"
diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh
index d8bdb0a8d7..80af0a439d 100755
--- a/test/lint/lint-whitespace.sh
+++ b/test/lint/lint-whitespace.sh
@@ -13,32 +13,41 @@ while getopts "?" opt; do
case $opt in
?)
echo "Usage: $0 [N]"
- echo " TRAVIS_COMMIT_RANGE='<commit range>' $0"
+ echo " COMMIT_RANGE='<commit range>' $0"
echo " $0 -?"
echo "Checks unstaged changes, the previous N commits, or a commit range."
- echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' $0"
+ echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0"
exit 0
;;
esac
done
-if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then
+# TRAVIS_BRANCH will be present in a Travis environment. For builds triggered
+# by a pull request this is the name of the branch targeted by the pull request.
+# https://docs.travis-ci.com/user/environment-variables/
+if [ -n "${TRAVIS_BRANCH}" ]; then
+ COMMIT_RANGE="$TRAVIS_BRANCH..HEAD"
+fi
+
+if [ -z "${COMMIT_RANGE}" ]; then
if [ -n "$1" ]; then
- TRAVIS_COMMIT_RANGE="HEAD~$1...HEAD"
+ COMMIT_RANGE="HEAD~$1...HEAD"
else
- TRAVIS_COMMIT_RANGE="HEAD"
+ # This assumes that the target branch of the pull request will be master.
+ MERGE_BASE=$(git merge-base HEAD master)
+ COMMIT_RANGE="$MERGE_BASE..HEAD"
fi
fi
showdiff() {
- if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
+ if ! git diff -U0 "${COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
echo "Failed to get a diff"
exit 1
fi
}
showcodediff() {
- if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
+ if ! git diff -U0 "${COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then
echo "Failed to get a diff"
exit 1
fi
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 3d0ac7f995..625085c55b 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -11,7 +11,7 @@ mutex:CConnman::ThreadOpenConnections
mutex:CConnman::ThreadOpenAddedConnections
mutex:CConnman::SocketHandler
mutex:UpdateTip
-mutex:PeerLogicValidation::UpdatedBlockTip
+mutex:PeerManager::UpdatedBlockTip
mutex:g_best_block_mutex
# race (TODO fix)
race:CConnman::WakeMessageHandler
@@ -24,6 +24,7 @@ race:WalletBatch::WriteHDChain
race:BerkeleyBatch
race:BerkeleyDatabase
race:DatabaseBatch
+race:leveldb::DBImpl::DeleteObsoleteFiles
race:zmq::*
race:bitcoin-qt
# deadlock (TODO fix)
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index b3d9b9e6ec..75257d886b 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,6 +1,5 @@
# -fsanitize=undefined suppressions
# =================================
-float-divide-by-zero:policy/fees.cpp
float-divide-by-zero:validation.cpp
float-divide-by-zero:wallet/wallet.cpp